Entity mapping

How does coherence map entities between multiple clients?
i.e. entity 1 on client 1 corresponds to entity 17 on client 2?

Does the replicator keep track of how an entity is mapped to all other clients/simulators? So that when simulators/clients send messages to the replicator, they don’t need to do anything?

Or is it that the receiver maintains a mapping between its local entity and the authoritative entity? For example simulator spawns an entity A, replicates it to a client. The client spawns an entity B and maintains a local mapping between A and B?

Hey, so the entity ID is not stable across clients for performance and a couple other reasons, so it should not really be considered as something you need to access or store.

You’re right though, the RS does have the official map between clients of what the ids are so when you send a command to an entity the RS knows what instances on each client to relay the command to.

So, as a developer all you have to consider is what we call entity references: CoherenceSync references | Unity Multiplayer SDK Documentation | coherence

If you have a command like SetTarget(GameObject target) that you send to one entity that references another coherence is able to resolve which entity the target is on each client automatically.

TL;DR use entity references and don’t use entity IDs and coherence will work it out for you.

Ah I see, so would the flow be something like this?
Client 1 creates entity A and wants to replicate it to client 2. It sends a SpawnMessage(entity A) to the RS.

The RS forwards the message to client 2. Client 2 spawns a corresponding entity B, and sends a message to the RS. The RS now has a mapping between A and B.

But there’s a period of time where B has been spawned, but the RS doesn’t know the mapping, which is why the docs mention that Entity References can be null?

Hey so you’re close but not quite on the mark. Spawning an entity is just instantiating a prefab with the CoherenceSync behavior. Once that is done the client automatically informs the RS of the spawned entity with info about which prefab. The RS then replicates the spawn on the other clients that can see the entity automatically. There is no time in which an RS doesn’t have the mapping to the replicated versions since it is what instructed the other clients to spawn the replicants.

The references being null is because you can send a command with a null reference like SetTarget(null) which could tell an AI on a client to stop targeting things.

Additionally, because of network concurrency, it is possible for a client to send a command referencing an entity that the receiving client can’t see because of live queries. In this case the reference would also be null.

I guess i’m just confused about what the internals of ‘replication’ and ‘spawning’ mean.

From what I understand,

  • client spawns entity A with CoherenceSync component.
  • RS receives entity A and spawns an entity B (different entity id) with the replicated CoherenceSync component
  • RS replicated it to client 2, which spawns an entity C with the replicated CoherenceSync component.
  • RS needs to somehow receive a message from client 2 to be able to maintain a mapping between entity B and entity C, no?

No, it’s simpler… sort of

Specifically it works like this:

  • client 1 creates Entity A and coherence generates an ID 32767 and assigns that to the CoherenceSync behaviour since this is the first entity it has created.
  • coherence sends the create info to the RS with the generated ID 32767 and spawning info for the prefab
  • RS accepts the creation of the entity and gives it an internal ID 10 since this is the 10th entity it has registered because of other clients creating stuff.
  • The RS creates a map for Entity A with client 1 which is 10 ↔ 32767
  • The RS stores the info about the entity in its internal data so any other client connecting can get info about entities without having to communicate with other clients.
  • RS sees that client 2 should know about Entity A so it creates an instruction to client 2 with how to construct the entity.
  • The RS generates an ID for the entity for client 2 to use in a different range so it doesn’t interfere with IDs that client 2 can generate. In this case the ID for client 2 that represents Entity A will be 1 since this is the first entity replicated from the RS to this client.
  • The RS creates a map between client 2’s version of Entity A which is 10 ↔ 1
  • The RS sends a message to client 2 to create an entity that uses Entity A’s spawn info with the ID 1
  • client 2 creates the entity and no message has to go back to the RS since it has all the info and the RS has the mapping

In the end, the “official” ID of Entity A is 10, but from client 1’s POV it is 32767 and from client 2’s POV it is 1. The key is that entities created by a client are allowed to allocate IDs from 32767-65534 and the RS when sending IDs for replicated entities uses numbers from 1 - 32766.

Nomenclature:

  • spawn is the the act of creating an instance usually through unity’s call to Instantiate.
  • replication is the idea of duplicating an instance of a thing where the state is copied from the state authority to other instances of the entity on different clients.
2 Likes

I see, thank you for the explanation!

So the key part is that each client has an id range reserved for replication (1 - 32766) and an id range for its own entities (32767 - 65534).
That’s helpful for me to get a better mental model.

That means that there’s a max number of 32766 entities replicated at once?

64k total but 32k created max for a client so you’d need minimum two clients to have max entities.