Preplaced ghost gameobjects aren't found unless the client loads first

I’m seeing the error “The ghost collection contains a ghost which does not have a valid prefab on the client!” when attempting to load a scene containing preplaced ghost gameobjects. This comes from GhostCollectionSystem line 399. With a breakpoint, I can confirm that the ghost collection contains all ghost types except the preplaced.

According to the docs, it’s implied that I should be able to load the server first.
https://docs.unity3d.com/Packages/com.unity.netcode@1.2/manual/ghost-spawning.html

If the client loads after the server, I see the above error. However, the error disappears and the ghost works as expected if I ensure that the client loads the scene first. Is this right?

The ghost gameobject is a prefab. It’s inside a subscene. The prefab is also linked to another “entity prefab container” prefab loaded earlier in the application.

DOTS 1.2.0.
Unity 2022.3.21

The docs are saying that correctly: there is no need for the client to load the scene first. Client can load/unload any sub-scene with pre-spawend ghosts at will (and everything still works fine).

The problem comes to how the ghost prefab reference and registration works and the fact the client must load the necessary prefabs as demanded by the server.

When you add a ghost instance to a sub-scene, its prefab is stored in the scene itself (unless another sub-scene references the same prefab, in which case it may be stored in another archive, shared among scenes).

I will try to explain here how things works as simple as I can.

So, let’s start server side. Server load the scene, Sub-scene are loaded too, end they contains:

  • Entity Prefab (because Netcode needs them to spawn ghost) if the scene entity archive does not have a reference to another archive that contains already the same prefab.
  • The ghost instance (that reference that prefab)
Scene A
  Pre-Spawned Ghost Instance -> Ghost Prefab X 
  Ghost Prefab X

When the first client connects and go in game (start streaming ghosts) the server sends to him the list of prefab resources that he must load (or have loaded). This is what the GhostCollectionSystem assess and throw exception if not honored by the client.
The server only stream to the client ghosts for which their associated prefabs have been loaded and processed by the client.

If the client set the connection to be in game (start receiving ghosts), and the prefabs that are needed are not loaded yet, or the resources are still loading or are going to be loaded, it is client responsibility then to inform the GhostCollectionSystem that the resources are loading and thus to wait for processing the prefab and relative hash match.

The ghost prefabs data is stored inside the GhostCollectionPrefab that contains:

  • The entity associated with that prefab type
  • The hash (CRC) of the prefab
  • A Loading flag.

This list sent by the server to the client as part of the snapshot data, and it is the beginning of the ghost protocol exchange. This list always growth. never shrink.
The clients communicate to the server, as part of the command stream, the number prefabs (from that list, in server order) loaded so far.

When the client receives the list from the server (sent in multiple chunks), the client matches the entry in the list with any loaded prefab he has loaded and do the same process the server does to calculate a CRC (hash).

What to do if the prefab is not there yet (or we want to delay loading them later on)?

This buffer can be inspected by the client before the GhostCollectionSystem runs and set the Loading flag as LoadingActive. The flag should be set every frame until the resource is loaded (see the documentation here: Struct GhostCollectionPrefab).

How can I load/unload sub-scene with ghosts dynamically without caring about this or more in general similarly to an open-world game scenario?

A simple solution is to have a single sub-scene (or multiple, depend on how you would like to have your data organized) that contains pretty much all the ghost-prefabs. This is pre-loaded by both server and client before accepting connections and setting the connection itself as in-game.

Then you can load/unload at will any sub-scene without caring about that loading detail because you are guarateed that the necessary ghost prefabs are already loaded.

1 Like

Thanks for the big write-up. It makes and it what I expected. Unfortunately, I’m not seeing this behaviour, and I don’t see where a bug could be.

My connection flow:

  • Server and clients agree to start the lobby.

  • Server and clients load PrefabsSubScene.

  • The subscene contains a single prefab: PrefabData.

  • PrefabData contains entity links to all prefab entities the game requires.

  • PrefabDataBaker uses GetEntity on the prefab of the prespawned ghost.

  • I’ve also tried using RegisterPrefabForBaking.

  • Server and clients wait for a PrefabData singleton to be present in their respective worlds.

  • I can verify here that the prefab of the prespawned ghost exists in the client world.

  • I can verify here that the prefab exists in Ghost Collection, with a linked entity.

  • Clients send a Ready RPC, and adds NetworkStreamInGame locally.

  • Server adds NetworkStreamInGame.

  • Server and clients start loading GameScene, which contains GameSubScene, which contains the prespawned ghost.

  • Client sees error, as above.

I’ve also tried adding a delay between adding NetworkStreamInGame and loading the game scene/subscene.

Again, if I force the client to wait for the server to load game scene/subscene first, the client does not see the error.

Any thoughts?

1 Like

Bump, also having this issue.