Mirror icon indicating copy to clipboard operation
Mirror copied to clipboard

feat: Fix SyncList<NetworkIdentity/GameObject/NetworkBehaviour> fragility by caching network IDs

Open lumeriith opened this issue 4 years ago • 0 comments

SyncVars of NetworkIdentity and other networked objects internally use a field of netId to make them resilient when interest management happens. This prevents SyncVars of those types from being forever null when the object is hidden to clients. SyncLists don't do such caching, so if an interest management happens and object gets hidden, SyncLists will point to null forever.

This PR implements same care to SyncLists by making clients store netIds and componentIndexes instead of direct references to those networked objects. (NetworkIdentity, GameObject, NetworkBehaviour)

What now works with this PR

class Monster : NetworkBehaviour { }

class Human : NetworkBehaviour
{
    public readonly SyncList<Monster> pets = new SyncList<Monster>();
}

// ...

someHuman.pets.Add(rabbitMonster);

// ... Rabbit gets really far away and gets hidden

Debug.Log(someHuman.pets[0]); // is now null on client

// ... Rabbit gets closeby again

Debug.Log(someHuman.[0]); // points to rabbit again!

Caveats

  1. Some list operations are now a few lines longer, which will affect SyncList performance even non-networked object ones (although difference will be miniscule)
  2. This does nothing if said networked objects are inside structs. e.g. SyncList<StructWithNetIdentityField>

Alternatives we could perhaps consider?

  1. Leave SyncList alone, and add several special traited types of SyncList. e.g. SyncListNetworkBehaviour<T> where T : NetworkBehaviour SyncListNetworkIdentity SyncListGameObject or maybe a single type SyncListNetworkedObject<T> that supports caching of NetworkIdentity, GameObject, and NetworkBehaviour

  2. Introduce Syncable<T> and make users use them like SyncList<Syncable<GameObject>>, SyncList<Syncable<Monster>> (requires Weaver work, as it can't generate readers/writers for generic types)

struct Syncable<T>
{
   public Syncable(T original)
   {
      // Store relevant data of networked object (netId, componentIndex)
   }
   
   public T Get()
   {
      // Dereference back to networked object
   }
}

lumeriith avatar Jul 08 '21 06:07 lumeriith