About Ghost spawning and components synchronization

Hey:
I am a beginner at NetCode For ECS. I implemented [netcode for objects] in my project now, but in consideration of performance, i want to implement dots to my project as well as [netcode for entities].
So my project is a lot of difference with unity dots samples. Here is the scenario:
I have lots of prefabs in my scene, and each of them may have different component that’s decided by custom configs, say excel data by others. So in the implemention of Netcode For Objects, I can do the following steps to make it right:

  1. Create a prefab with [NetworkObject] behaviour attached.
  2. In server side, instantiate the prafab above and fill lots of components which inherit from NetworkBehaviour.
  3. Then in client side, implements INetworkPrefabInstanceHandler, hook the networkObject’s spawn to add same component list.
  4. Finally, the field that marked with NetworkVariable in components can be synced correctlly.

Now, with [netcode for entities], what i can get from documents is that Ghost prefab should determine its component list in compile time, and bake it in subscene. But the number of components is indeterminate at compile time, and it’s not a good way to bake all my prefabs in subscene because of it’s quantity.

So, how can i do for this scenario.appreciate it.

Hey Hecocos!

This is broadly correct, yes. The issue is that: If you want to create a ghost with an entirely runtime-selected component list, netcode has no knowledge of said ghost, and therefore has not stored the necessary logic to properly deserialize it.

You can solve this in 2 ways:

  • GhostPrefabCreation.ConvertToGhostPrefab - This API converts an Entities prefab into a ghost, storing the necessary logic to properly deserialize it into a BlobAsset. The difficulty here is that you must call this API identically on all the clients, and the server. E.g. If the server defines a prefab with components A,C,F, all clients must also define a prefab with components A,C,F. If I remember correctly, they must also define these ghosts in the same order, too. This means you’ll need to replicate (via RPCs) any dynamic prefab types that arise through gameplay.

Not impossible, but non-trivial. Some users define 100% of their ghost prefabs in this way.

  • Alternatively: You write custom serialization logic for your components yourself, taking an entity (and all of it’s components) and converting it into a NativeArray, which you then replicate via an IBufferElementData (with a [GhostField] public byte Value;). This supports “arbitrary” dynamic components, but it does mean you’ll need to do most of the work of replicating and optimizing yourself. We see customers using this “mix of static and dynamic components” for their scripting DSL use-cases (example: designers can define arbitrary blackboard variables, which need to be synced).
  • GhostPrefabCreation.ConvertToGhostPrefab - This API converts an Entities prefab into a ghost, storing the necessary logic to properly deserialize it into a BlobAsset. The difficulty here is that you must call this API identically on all the clients, and the server. E.g. If the server defines a prefab with components A,C,F, all clients must also define a prefab with components A,C,F. If I remember correctly, they must also define these ghosts in the same order, too. This means you’ll need to replicate (via RPCs) any dynamic prefab types that arise through gameplay.

In this way, what i can do shoud be:

  1. In server, create a temp entity(named A) and adding all components needed with fixed order, then call GhostPrefabCreation.ConvertToGhostPrefab, then send [CreateNewGhostPrefabRpc] to client. Then i can create ghost entity by calling Instantiate(A), without do anthing else.
  2. In Client, handler CreateNewGhostPrefabRPC, create temp entity and adding all components like server doese and call GhostPrefabCreation.ConvertToGhostPrefab.

Is right for that ?

Broadly yes. You should also make sure not to register the same ghost type multiple times (so probably generate a hash using the Archetype), and obviously make use of the ComponentType TypeManager API to replicate a list of stable hashes. You’ll need to write a custom serializer for that RPC to ensure it can handle a FixedList of types too.

Recently I found that I netcode needs to support modify ghost entity at runtime. Basically I want to add and remove child entity at ghost entity at server and it will automatically sync to all clients. And also if the newly child entity has ghost component, it will also work like other ghost component like parent ghost entity that able to sync data between client and server.

Will official provide sample for this or even better make it into official netcode as official feature? I’m quite curious how to replicate “arbitrary” dynamic components between client and server that I believe it’s sync arbitrary count of components and its data between client and server? Btw for DSL do u mean Domain-specific language?

I used a Dict to maintain the ghost prefab’s component list, and each have different components. And this dict data exist both side. So i can just send prafab’s key to client, it can get component list from that Dict. I think this way can be simpler.

I can’t promise yes, but it would be a good thing to demo, definitely. And yes, re DSL.

If the archetype are know by both end (the client know that that ghost X has these component set) there is even not need to keep the list itself.
And would also not requires any rpc at all, if we are using the GhostType has key (this actually sent by the server as part of snapshot and the client need to ack that has loaded that prefab type) and having generator functions/method/factory for that specific ghost type hash.
Require some extra setup to pre-calculate that mapping, but once done it is a powerful mechanics.

If the archetype is more dynamic, and server driven, passing the list of stable-hash in an rpc is kinda necessary. you can also indeed reduce the size by a large margin by knowing all the replicated component types and store these in a sorted list, and using the index (8 bits or less per index suffice).