Search Unity

  1. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Question [FishNet] Rogue PrefabIds preventing networked spawns

Discussion in 'Multiplayer' started by npeaks_pahdo, Feb 8, 2024.

  1. npeaks_pahdo

    npeaks_pahdo

    Joined:
    Oct 13, 2023
    Posts:
    3
    Hi! I'm currently on FishNet 3.11.12, in unity 2022.3.10f1. The project is a rather simple prototype at this point; there's a single scene, one spawner GameObject, a mob Prefab and a Player Prefab. Irecently started encountering issues where, upon connecting to a built game running as the server, the client would fail to render, or fail to connect. Upon looking into the logs, I saw this error:
    "PrefabId 9 is out of range."

    This was followed by a null ref and would prevent the client from connecting. I tracked that down to the `FishNet/Runtime/Managing/Object/PrefabObjects/SinglePrefabObjects.cs` file, and after meandering through various places in the stack trace, I found that the PrefabId in question came from the NetworkObject component in the Mob prefab I was trying to spawn, and it was set to 9. This value is also the one stored in the backing variable in the `Mob.prefab` file.

    This PrefabId seems to be set in only one place in the plugin (`FishNet/Runtime/Managing/Object/ManagedObjects.cs:L260`), to which I added a debug line:

    Code (CSharp):
    1.  
    2.         public static void InitializePrefab(NetworkObject prefab, int index, ushort? collectionId = null)
    3.         {
    4.             if (prefab == null)
    5.                 return;
    6.             /* Only set the Id if not -1.
    7.              * A value of -1 would indicate it's a scene
    8.              * object. */
    9.             if (index != -1)
    10.             {
    11.                 //Use +1 because 0 indicates unset.
    12.                 Debug.Log($"Setting prefab for {prefab.gameObject.name}:{prefab.gameObject.GetInstanceID()} id to {index}");
    13.                 prefab.PrefabId = (ushort)index;
    14.                 if (collectionId != null)
    15.                     prefab.SpawnableCollectionId = collectionId.Value;
    16.             }
    17.  
    18.             byte componentIndex = 0;
    19.             prefab.UpdateNetworkBehaviours(null, ref componentIndex);
    20.         }
    21.  
    This code seems to run when the project starts up, and it sets the PrefabId on the prefab reference that exists in the DefaultPrefabsObject generated by FishNet in the Editor when assets change. However, the PrefabId that was out of range came from the prefab reference that was on the Spawner gameObject in my scene, and obviously the values don't match. When printing out the InstanceIds, those don't match either, so it makes sense that the initialized PrefabId values are not propagated.

    My question is, how is this flow meant to work? Is the Initialize code meant to run at build time and save the PrefabId to file? As it seems to be written, the initialized values have no way of reaching the prefab references across the project, relying on a hidden backing variable to magically match an index in an autogenerated file. As it stands right now, the project is unable to connect clients or spawn any networked gameObjects, and I'm a bit lost on how to proceed (short of rewriting the spawnable prefab feature to rely on the prefab guid instead of the index it uses right now).

    Any advice would be much appreciated!
     
  2. Punfish

    Punfish

    Joined:
    Dec 7, 2014
    Posts:
    383
    Please update to the latest V3 just to be sure it has not already been resolved.

    Out of range exceptions usually occur when the build differs from the editor, or two builds differ from each other of course.

    The Ids are serialized in editor and when building. But sometimes the editor serializations can differ from builds and that's what I suspect may be happening. See if 'Refresh Default Prefabs' is ticked on your NetworkManager, if not please mark i and try again; this will re-serialize on play in the matter editor serialization didn't get everything right, which can happen for a number of Unity reasons.
     
  3. npeaks_pahdo

    npeaks_pahdo

    Joined:
    Oct 13, 2023
    Posts:
    3
    I actually tried that setting as well, and it didn't help. As far as I can tell, the way that the spawning code is authored depends on the serialized prefab id in *every* instance of the prefab matching the index in the default prefabs array; if it doesn't match, the replication will not run properly. The regeneration process doesn't seem to persist the PrefabId back to the prefab file, so regenerating on play didn't do anything. Regardless, the issue is that on the built games (server and client), the values differ and that setting only runs in editor mode anyway.

    My build process contains a couple steps that generate and remove prefabs; I think this would cause the default prefabs to be regenerated during the build (indeed, when checking the logs, I can see the code in ManagedObjects.cs assigning prefab ids to the generated prefabs). What's odd is that the build process generates prefabs outside of the folder I told fish to look at for adding prefabs to the DefaultPrefabs object, so I'm not sure why seems to be including them in the DefaultPrefabs object. I actually disabled the build processor that was generating the prefabs, and the editor build seems to work okay, but built clients still have the wrong serialized value in the prefab objects, preventing the replication from working.

    Do you know which routine actually saves the PrefabIds after they've been set? I noticed that there are no calls to AssetDatabase around the PrefabID assignment; this would indicate that the correct values are never explicitly saved back to file.
     
    Last edited: Feb 11, 2024