Ticket #222 (closed task: wontfix)
Better reference-counting of entities
|Reported by:||jan||Owned by:||jan|
Description (last modified by jan) (diff)
We currently have a fairly complicated and unsafe means of managing entities. CHandle is basically a reference-counted pointer. HEntity is an index into a table of CHandle; the class simulates smart pointer semantics by accessing this table.
A fixed table of CHandle is both inefficient (what if we really only need 10% of the limit at a time?) and unsafe (we can't validate HEntity indices sent via network, and risk dangling entity pointers).
To delete entities, they must be inserted into a reaper queue, the contents of which are deleted at the beginning of each update. However, the entities are still accessible and can be referenced.
Finally, there are a bunch of complicated and seemingly redundant flags/access mechanisms for entity aliveness - the refd bitset, its reference count (trumped however by the reaper queue), a destroyed flag and the destroy notifier flag.
This complexity is causing real problems - we've had several access violations at 0x400..0x460, which are most likely caused by NULL entity pointers. At the meeting yesterday, we discussed banning raw CEntity* pointers and replacing them all with smart pointers. Also, separating network ID from entity storage seems prudent (allows stringent error checks and removes an artificial limit on the number of handles).
The proposed solution we arrived at is based on weak pointers - this allows code (e.g. AI) to hold on to entity references and be able to determine in a controlled way whether the entity is dead. The implementation is such that every entity holds a reference-counted pointer to itself and hands it out in form of a smart pointer class. When the entity is `killed', it marks the shared pointer value as invalid and frees itself immediately. Only when the last weak reference is freed would the reference-counted-pointer class instance that they use be released.
The requirement for the reaper is based on the assumption that entities are killed fairly high on the call stack, and the calling functions may still be wanting to do something with the entity. To detect when an entity is dead, we are already using an isDead flag. I figure this could be replaced with the standard weak_ptr isValid mechanism.
Finally, the network ID. For practically limitless numbers of entities, we need a 32-bit counter that resets to zero each game. Entities store their number (which is what is serialized), and EntityManager needs a sparse mapping of ID -> HEntity (the new/old entity reference system).
This sounds like it would simplify things; a partial implementation is underway, but a few design questions remain to be hashed out (Philip and I will be discussing this shortly; any other input is welcome).
~update HEntity~ ~adapt EntityManager~
- proper construction of CEntity, including !JS_AddRoot
- update all code to use HEntity instead of CEntity*
- Owner set to jan
- Status changed from new to assigned
- Description modified (diff)
- Status changed from assigned to closed
- Resolution set to wontfix