Client replication
It is also possible to use lightyear to replicate entities from the client to the server. There are different possibilities.
Client authoritative
To replicate a client-entity to the server, it is exactly the same as for a server-entity.
Just add the Replicate
component to the entity and it will be replicated to the server.
#![allow(unused)] fn main() { fn handle_connection( mut connection_event: EventReader<ConnectEvent>, mut commands: Commands, ) { for event in connection_event.read() { let local_client_id = event.client_id(); commands.spawn(( /* your other components here */ Replicate { replication_target: NetworkTarget::All, interpolation_target: NetworkTarget::AllExcept(vec![local_client_id]), ..default() }, )); } } }
Note that prediction_target
and interpolation_target
will be unused as the server doesn't do any
prediction or interpolation.
If you want to then broadcast that entity to other clients, you will have to add a Replicate
component
on the server entity. This should generally happen in the ServerReplicationSet::ClientReplication
SystemSet on the server, so that it happens right
after receiving the client entity.
Be careful to not replicate the entity back to the original client, as it would create a duplicate entity on the client.
Example flow:
--- title: Client Authoritative --- sequenceDiagram participant Client1 participant Server participant Client2 participant Client3 Client1->>Server: Connect() Server->>Client1: Connected Client1->>Client1: ConnectEvent Client1->>Client1: SpawnPredicted(PlayerID: 1) Client1->>Server: Replicate(PlayerID: 1) Server-->>Client2: Replicate(PlayerID: 1) Client2->>Client2: SpawnConfirmed(PlayerID: 1) Server-->>Client3: Replicate(PlayerID: 1) Client3->>Client3: SpawnConfirmed(PlayerID: 1)
Pre-spawned predicted entities
Sometimes you might want to spawn a predicted entity on the client, but then replicate it to the server and let the server get control over the entity (server-authoritative).
For example you might have a predicted character that spawns another predicted entity (a projectile that other clients should see, for example).
You could wait for the user input to reach the server, then for the server to spawn the projectile and for the projectile to be replicated back to the client, but that would mean a delay of 1-RTT on the client to see the projectile when they pres a button.
A solution is to spawn the predicted projectile on the client; but then replicate back to the server. When the server replicates back the projectile, it creates a Confirmed entity on the client, but will re-use the existing predicted projectile as the Predicted entity.
The way to do this is to add a PrePredicted
component on the client entity that you want to predict.
On the server, to replicate back the entity to the client, you will need to manually add a Replicate
component to the entity
to specify to which clients you want to rebroadcast it.
Note that you must add the Replicate
component in the ServerReplicationSet::ClientReplication
SystemSet
for proper handling of
pre-spawned predicted entities!
When the server replicates back the entity, the client will check if the entity has a PrePredicted
component.
If not present, that means this is not a pre-spawned Predicted entity, and the client will spawn both the Confirmed
and Predicted entities.
If it's present, the client will spawn a new Confirmed entity, but will re-use the entity
as the Predicted entity.
Note that pre-spawned predicted entities will give authority to the server's entity immediately, the client to server replication will stop immediately after the initial replication, and the server entity should be the authoritative one.
Example flow:
--- title: Client PrePredicted --- sequenceDiagram participant Client1 participant Server participant Client2 participant Client3 Client1->>Server: Connect() Server->>Client1: Connected Client1->>Client1: ConnectEvent Client1->>Client1: SpawnPredicted(PlayerID: 1) Client1->>Server: Replicate(PlayerID: 1) Server-->>Client1: Replicate(PlayerID: 1) Client1->>Client1: SpawnConfirmed(PlayerID: 1) Server-->>Client2: Replicate(PlayerID: 1) Client2->>Client2: SpawnConfirmed(PlayerID: 1) Server-->>Client3: Replicate(PlayerID: 1) Client3->>Client3: SpawnConfirmed(PlayerID: 1)
Notes to myself:
- one thing to be careful for is that we want to immediately stop replicating updates from the pre-spawned predicted entity
to the server; because that entity should be server-authoritative. Right after the first time the
Send
SystemSet
runs, runclean_prespawned_entities
to removeReplicate
from those entities. - another thing we have to be careful of is this: let's say we receive on the server a pre-predicted entity with
ShouldBePredicted(1)
. Then we rebroadcast it to other clients. If an entity1
already exists on other clients; we will start using that entity as our Prediction target! That means that we should:- even if pre-spawned replication, require users to set the
prediction_target
correctly - only broadcast
ShouldBePredicted
to the clients who haveprediction_target
set. - be careful that
ShouldBePredicted
can be added once during spawn, and once from replication. In that case the second one should win out!
- even if pre-spawned replication, require users to set the