Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Crash when disposing entities #123

Closed
AridTag opened this issue Jul 27, 2021 · 3 comments
Closed

Crash when disposing entities #123

AridTag opened this issue Jul 27, 2021 · 3 comments
Labels
bug Something isn't working

Comments

@AridTag
Copy link

AridTag commented Jul 27, 2021

Hi I'm trying to use DefaultEcs for an MMO emulation project. When the users client is closed a couple of network connections are disconnected. There is an entity for each of the network connections the client makes.

I've been experiencing a crash when trying to Dispose of those entities.

System.IndexOutOfRangeException: Index was outside the bounds of the array.
   at DefaultEcs.EntityMap`1.DefaultEcs.Technical.IEntityContainer.Remove(Int32 entityId) in Z:\GitWorkspace\*******\src\DefaultEcs\EntityMap.cs:line 242
   at DefaultEcs.Technical.EntityContainerWatcher.Remove(EntityDisposedMessage& message) in Z:\GitWorkspace\*******\src\DefaultEcs\Technical\EntityContainerWatcher.cs:line 104
   at DefaultEcs.Technical.Command.Executer.Execute(Byte[] memory, Int32 commandLength, List`1 objects) in Z:\GitWorkspace\*******\src\DefaultEcs\Technical\Command\Executer.cs:line 89
   at DefaultEcs.Command.EntityCommandRecorder.Execute() in Z:\GitWorkspace\*******\src\DefaultEcs\Command\EntityCommandRecorder.cs:line 214
   at *******.Server.UpdateLoop(Object obj) in Z:\GitWorkspace\*******\src\*******\Server.cs:line 167
   at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
   at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)

When a network connection is closed and it's not a connection associated with a player in the world a NetworkClientDisconnected component is added to the entity and you can see in the following log that it gets picked up by the NetworkClientDisconnectedCleanupSystem, if it's associated with a player in the world it will get a PlayerDisconnectedComponent and get picked up by the PlayerDisconnectedCleanupSystem.

Both of these systems use an EntityCommandRecorder to do the disposing and then in PostUpdate if any commands were recorded it publishes a message to have the server add the recorder to a queue to be processed when all systems have completed the current Update.

dbug: ******.Ecs.Systems.NetworkClientDisconnectedCleanupSystem[0]
      Disposing entity Entity 1:2.0
dbug: ******.Server[0]
      Queuing entity commands
dbug: ******.Ecs.Systems.NetworkClientDisconnectedCleanupSystem[0]
      Disposing entity Entity 1:3.0
dbug: ******.Server[0]
      Queuing entity commands
------------------------------------
-- Client closed here --------------
------------------------------------      
dbug: ******.Ecs.Systems.NetworkClientDisconnectedCleanupSystem[0]
      Disposing entity Entity 1:3.1
dbug: ******.Server[0]
      Queuing entity commands
dbug: ******.Ecs.Systems.World.PlayerDisconnectedCleanupSystem[0]
      Disposing entity: Entity 1:2.1
dbug: ******.Server[0]
      Queuing entity commands

My original thinking was that I must be accidentally disposing the same entity twice somehow but I could not figure out where I was doing it if I was. So I ended up cloning the DefaultEcs repo and adding it as a project to my solution so that I could modify Entity.cs and have it do a Debug.WriteLine in its Dispose method to see exactly what entities are being disposed leading up to the crash and hopefully prove it wasn't my fault.

These are the Debug.WriteLines from Entity.Dispose()

Entity.cs - Disposing Entity 1:2.0
Entity.cs - Disposing Entity 1:3.0
Entity.cs - Disposing Entity 1:3.1
Entity.cs - Disposing Entity 1:2.1

This is a screenshot of the _mapping array in ComponentPool.cs at the point of the exception
image

It looks like this is getting corrupted somehow? I'm not really sure. Any help is greatly appreciated

@Doraku
Copy link
Owner

Doraku commented Jul 27, 2021

From the look of it, the error occurs when accessing the previous component pool. Since you already have the source to debug, could you try a small change?

public void Remove(in EntityDisposedMessage message) => _container.Remove(message.EntityId);

_subscriptions.Add((s, w) => w.Subscribe<EntityDisposedMessage>(s.Remove));

Change the message type from EntityDisposedMessage to EntityDisposingMessage, I tried to replicate what I though was occuring but couldn't make it fails :/, still this change is worth a shot (basically the subscription order is not guaranteed and maybe the component is removed from the pool before the callback to remove the entity from the EntityMap is called, I made the Disposing/Disposed message specifically for this but this subscription slipped pass me). I will keep looking into it.

edit:
ok I managed to replicate it, do you by any chance have multiple EntityMap/EntityMultiMap with the same key? if so then this is definitely the fix :) awaiting your check before pushing it.

@Doraku Doraku added the bug Something isn't working label Jul 27, 2021
@AridTag
Copy link
Author

AridTag commented Jul 27, 2021

I do indeed have multiple Maps with the same key. This change does appear to have fixed it!

Thanks!

@Doraku Doraku closed this as completed in 9aa503c Jul 27, 2021
@Doraku
Copy link
Owner

Doraku commented Jul 27, 2021

Thank you for giving all those infos, made finding the problem much easier :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

2 participants