-
Notifications
You must be signed in to change notification settings - Fork 47
Commit
- Loading branch information
There are no files selected for viewing
This file was deleted.
This file was deleted.
This file was deleted.
This file was deleted.
This file was deleted.
This file was deleted.
This file was deleted.
This file was deleted.
This file was deleted.
This file was deleted.
This file was deleted.
This file was deleted.
This file was deleted.
This file was deleted.
This file was deleted.
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
# Mapping Entities | ||
|
||
|
||
Some messages or components contain references to other Entities. | ||
For example: | ||
|
||
```rust,noplayground | ||
#[derive(Message)] | ||
struct SpawnedEntity { | ||
entity: Entity, | ||
} | ||
#[derive(Component, Message)] | ||
struct Parent { | ||
entity: Entity, | ||
} | ||
``` | ||
|
||
In this case, we cannot replicate the Component or Message directly, because the Entity is only valid on the local machine. | ||
So the Entity that the client would receive from the server would only be valid for the Server [`World`](bevy::prelude::World), not the Client's. | ||
|
||
We can solve this problem by mapping the server Entity to the corresponding client [`Entity`](bevy::prelude::Entity). | ||
|
||
The trait [`EntityMap`](crate::prelude::EntityMap) is used to do this mapping. | ||
|
||
```rust,noplayground | ||
pub trait MapEntities { | ||
/// Map the entities inside the message or component from the remote World to the local World | ||
fn map_entities(&mut self, entity_map: &EntityMap); | ||
} | ||
``` | ||
|
||
This is applied to every Message or Component received from the remote World. | ||
|
||
|
||
Messages or Components implement this trait by default like this: | ||
```rust,noplayground | ||
pub trait MapEntities { | ||
fn map_entities(&mut self, entity_map: &EntityMap) {} | ||
} | ||
``` | ||
i.e. they don't do any mapping. | ||
|
||
If your Message or Component needs to perform some kind of mapping, you need to add the `#[message(custom_map)]` attribute, | ||
and then derive the `MapEntities` trait yourself. | ||
```rust,noplayground | ||
#[derive(Message)] | ||
#[message(custom_map)] | ||
struct SpawnedEntity { | ||
entity: Entity, | ||
} | ||
impl MapEntities for SpawnedEntity { | ||
fn map_entities(&mut self, entity_map: &EntityMap) { | ||
self.entity.map_entities(entity_map); | ||
} | ||
} | ||
``` | ||
|
||
The [`MapEntities`](crate::prelude::MapEntities) trait is already implemented for [`Entity`](bevy::prelude::Entity). | ||
|
||
|
||
Note that the [`EntityMap`](crate::prelude::EntityMap) is only present on the client, not on the server; it is currently not possible | ||
for clients to send Messages or Components that contain mapped Entities to the server. | ||
|
||
|
||
## TODOs | ||
|
||
- if we receive a mapped entity but the entity doesn't exist in the client's [`EntityMap`], we currently don't apply any mapping, but still receive the Message or Component. | ||
- that could be completely invalid, so we should probably not receive the Message or Component at all ? | ||
- instead we might to wait for the MappingEntity to be created; as soon as it's present in [`EntityMap`] we can then apply the mapping and receive the Message or Component. | ||
- therefore we need a waitlist of messages that are waiting for the mapped entity to be created | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,109 @@ | ||
# System order | ||
|
||
|
||
Lightyear provides several [`SystemSets`](bevy::prelude::SystemSet) that you can use to run your systems in the correct order. | ||
|
||
The main things to keep in mind are: | ||
- All packets are read during the `PreUpdate` schedule. This is also where all components that were received are replicated to the Client World. | ||
- All packets are sent during the `PostUpdate` schedule. All messages that were buffered are then sent to the remote, and all replication messages (entity spawn, component updated, etc.) are also sent | ||
- There are 2 [`SystemSets`](bevy::prelude::SystemSet) that you should interact with: | ||
- [`BufferInputs`](crate::prelude::BufferInputs): this is where you should be running `client.add_inputs()` so that they are buffered and sent to the server correctly | ||
- [`Main`](crate::prelude::Main): this is where all your [`FixedUpdate`] Schedule systems (physics, etc.) should be run, so that they interact correctly with client-side prediction, etc. | ||
|
||
Here is a simplified version of the system order: | ||
```mermaid | ||
--- | ||
title: Simplified SystemSet order | ||
--- | ||
stateDiagram-v2 | ||
classDef flush font-style:italic; | ||
ReceiveFlush: Flush | ||
PreUpdate --> FixedUpdate | ||
FixedUpdate --> PostUpdate | ||
state PreUpdate { | ||
Receive --> ReceiveFlush | ||
ReceiveFlush --> Prediction | ||
ReceiveFlush --> Interpolation | ||
} | ||
state FixedUpdate { | ||
TickUpdate --> BufferInputs | ||
BufferInputs --> Main | ||
} | ||
state PostUpdate { | ||
Send | ||
} | ||
``` | ||
|
||
|
||
|
||
|
||
## Full system order | ||
|
||
```mermaid | ||
--- | ||
title: SystemSet order | ||
--- | ||
stateDiagram-v2 | ||
classDef flush font-style:italic; | ||
SpawnPredictionHistory : SpawnHistory | ||
SpawnInterpolationHistory : SpawnHistory | ||
SpawnPredictionHistoryFlush : Flush | ||
SpawnInterpolationHistoryFlush : Flush | ||
SpawnPredictionFlush : Flush | ||
SpawnInterpolationFlush: Flush | ||
CheckRollbackFlush: Flush | ||
DespawnFlush: Flush | ||
ReceiveFlush: Flush | ||
FixedUpdatePrediction: Prediction | ||
PreUpdate --> FixedUpdate | ||
FixedUpdate --> PostUpdate | ||
state PreUpdate { | ||
Receive --> ReceiveFlush | ||
ReceiveFlush --> Prediction | ||
ReceiveFlush --> Interpolation | ||
} | ||
state Prediction { | ||
SpawnPrediction --> SpawnPredictionFlush | ||
SpawnPredictionFlush --> SpawnPredictionHistory | ||
SpawnPredictionHistory --> SpawnPredictionHistoryFlush | ||
SpawnPredictionHistoryFlush --> CheckRollback | ||
CheckRollback --> CheckRollbackFlush | ||
CheckRollbackFlush --> Rollback | ||
} | ||
state Interpolation { | ||
SpawnInterpolation --> SpawnInterpolationFlush | ||
SpawnInterpolationFlush --> SpawnInterpolationHistory | ||
SpawnInterpolationHistory --> SpawnInterpolationHistoryFlush | ||
SpawnInterpolationHistoryFlush --> Despawn | ||
Despawn --> DespawnFlush | ||
DespawnFlush --> Interpolate | ||
} | ||
state FixedUpdate { | ||
TickUpdate --> BufferInputs | ||
BufferInputs --> WriteInputEvent | ||
WriteInputEvent --> Main | ||
Main --> ClearInputEvent | ||
Main --> FixedUpdatePrediction | ||
} | ||
state FixedUpdatePrediction { | ||
PredictionEntityDespawn --> PredictionEntityDespawnFlush | ||
PredictionEntityDespawnFlush --> UpdatePredictionHistory | ||
UpdatePredictionHistory --> IncrementRollbackTick : if rollback | ||
} | ||
state PostUpdate { | ||
state Send { | ||
SendEntityUpdates --> SendComponentUpdates | ||
SendComponentUpdates --> SendInputMessage | ||
SendInputMessage --> SendPackets | ||
} | ||
-- | ||
Sync | ||
} | ||
``` |