colyseus / colyseus-unity-sdk

⚔ Colyseus Multiplayer SDK for Unity
https://docs.colyseus.io/getting-started/unity-sdk/
MIT License
371 stars 102 forks source link

IndexOutOfRangeException when inserting and deleting same MapSchema element in same patch #103

Closed stats closed 3 years ago

stats commented 4 years ago

An IndexOutOfRangeException is thrown when an element is added and removed from MapSchema in the same patch.

A minimal example of this behaviour can been seen here: Test case for index outside bounds

The error boils down to adding an enemy

  const enemy = new Enemy();
  enemy.x = 547;
  enemy.y = 547;
  this.state.entities[generateId()] = enemy;

and then most likely in the same patch removing the enemy in the update

for(let key in this.state.entities) {
    const enemy = this.state.entities[key];
      if(enemy.x == 547 && enemy.y == 547) {
        delete this.state.entities[key];
      }
    }

I don't think that this is a behaviour that would be experienced often in the wild. In my example the error was caused by some poor logic. An example where this might legitimately occur would be an area of effect ability such as an explosion or poison cloud occuring at the spawn location, and immediately destroys an added entity.

As long as your update rate is faster than your patch rate IndexOutOfRangeException is possible.

endel commented 4 years ago

Hi @stats, could this be a @colyseus/schema versioning issue? I've just tried your use case and it didn't seem to throw the error for me. (EDIT: YES IT DOES HAPPEN)

@knowuh reported on Discord that he was having this issue with the unity3d example project, and after upgrading the @colyseus/schema version the error was gone. (rm -R node_modules package-lock.json && npm install)

endel commented 4 years ago

(I do see the error! sorry, gonna investigate 🙈 )

endel commented 4 years ago

This change has caused regression and I had to revert it. The test case now includes another assertion that is now breaking.

Basically, this was passing:

state.mapOfPlayers['two'] = new Player("Snake", 10, 10);
delete state.mapOfPlayers['two'];

And this was breaking: (item "one" was not being encoded as a delete)

state.mapOfPlayers['one'].x++;
delete state.mapOfPlayers['one'];
endel commented 3 years ago

This has been finally fixed on 0.14! 🎉 https://github.com/colyseus/colyseus/releases/tag/0.14.0