Entity object ownership
#1
Documenting stuff is always good, so here's some thoughts about rev 1385. Entities were rewritten, primarily so that their "Tick()" method can get the raw cChunk parameter. In order to do so, entities need to be ticked from within their respective chunks' Tick() method, which meant the global world list of entities had to be removed and instead entities are now owned by the chunk to which they belong.

The life of an entity
When an entity is first constructed, it is unowned - that means whoever constructed it, must make sure to dispose of it accordingly. The entity doesn't belong to any world, isn't sent to any clients etc. It's perfectly legal to set entity position, rotation and other parameters in this state.
Then the entity is added to a world, through its Initialize() function, which calls cWorld::AddEntity(). This bubbles through the chunkmap and calls cChunk::AddEntity(). From that moment on, the entity is owned by whichever chunk it is currently in. Each chunk ticks all its entities in its Tick() method, and it also checks and removes all entities whose Destroy() method has been called. If an entity moves to a different chunk, position-wise, the chunk detects it and within its Tick() function moves the entity to the correct chunk. So an entity doesn't need to worry itself about moving itself across chunks, the chunks do it automatically. However, it happens only within the cChunk::Tick() method, so there is a time when the entity's position doesn't correspond to the chunk in which the entity is. This needs to be taken into account when accessing entities by their chunks.
If an entity ever needs to teleport to a different world, it needs to remove itself from the world, using cWorld::RemoveEntity(), and add self to the other world using cWorld::AddEntity(). The RemoveEntity() call transfers entity ownership to whoever called it, so this whole procedure is actually a transfer of ownership, too. See cPlayer::MoveToWorld() for an example.
The entity object is deleted by the cChunk::Tick() function if the entity is marked as destroyed (by calling its Destroy() function). If required to be destroyed immediately,an entity can be also removed from the world (cWorld::RemoveEntity() ) and then safely deleted.

There are several caveats to this new system:
- An entity should not move into an unloaded chunk. If it does, currently the entity gets lost (memory leak), with a LOGWARNING()
- An entity must not be initialized until its correct position is set. If an entity is initialized into an unloaded chunk, the entity gets lost (memory leak) without any LOG, and the server will crash later on.
- When the entity crosses chunk borders, its owning chunk will get updated only after the world tick (this might get dangerous for plugins setting entity positions)
Reply
Thanks given by:
#2
Nice work, must have taken you quite some time
Reply
Thanks given by:
#3
Yeah, that's pretty cool Smile must have been a lot of work. Just a quick question, what would happen if an entity is still "alive" and the chunk is unloaded? For example, we have a mob on a chunk doing its thing (you know, being a mob and stuff) and the player flies really far away from the chunk so the server can unload the chunk, is the entity state saved anywhere or does the server keep the chunk loaded while the mob exists?
Reply
Thanks given by:
#4
Right now the entity gets properly destroyed, but is not saved to a file (WSSAnvil is still missing most of the entity saving and loading code, only pickups have been done).

However, the first caveat is extremely important for you here - an entity should practically freeze when it's on a chunk that's on the edge of loaded chunks, otherwise it might get lost. I believe vanilla behaves the same - the further away, the less the entities move, until they stop at all.
Reply
Thanks given by:




Users browsing this thread: 2 Guest(s)