Skip to content

Commit

Permalink
update book and add packet stats manager to track packet loss
Browse files Browse the repository at this point in the history
  • Loading branch information
cbournhonesque-sc committed Dec 1, 2023
1 parent e48fdfc commit 31a59c7
Show file tree
Hide file tree
Showing 44 changed files with 577 additions and 116 deletions.
4 changes: 4 additions & 0 deletions .github/workflows/book.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ jobs:
mkdir mdbook
curl -sSL $url | tar -xz --directory=./mdbook
echo `pwd`/mdbook >> $GITHUB_PATH
- name: Install stable toolchain
uses: dtolnay/rust-toolchain@stable
- name: Install mdbook-mermaid
run: cargo install mdbook-mermaid
- name: Build Book
run: |
cd book
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/main.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ jobs:
- name: Clone repo
uses: actions/checkout@v4

- name: Instal stable toolchain
- name: Install stable toolchain
uses: dtolnay/rust-toolchain@stable

- name: Cache crates
Expand Down
5 changes: 5 additions & 0 deletions NOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@ ROUGH EDGES:
- have a NetworkMessage macro that all network messages must derive (Input, Message, Component)
- all types must be Encode/Decode always. If a type is Serialize/Deserialize, then we can convert it to Encode/Decode ?

- MapEntities:
- if we receive a mapped entity but the entity doesn't exist, we just don't do any mapping; but then the entity could be completely wrong?
- in that case should we just wait for the entity to be created or present in the mapping (this is what naia does)? And if it doesn't get created we just ignore the message?
- the entity mapping is present in the entity_map which exists on client, but not on server. So we cannot do the mapping on server.


- Prediction:
- TODO: handle despawns, spawns
Expand Down
67 changes: 26 additions & 41 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,57 +29,42 @@ I plan to add implementations for either WebRTC or WebTransport in the future, s
Lightyear provides a simple API for sending and receiving messages, and for replicating entities and components:
- the user needs to define a `Protocol` that defines all the messages, components, inputs that can be sent over the network; as well as the channels
- to send messages, the user mostly only needs to interact with the `Client<P>` and `Server<P>` structs, which provide methods to send messages and send inputs
- all messages are accessible via BevyEvents: `EventReader<MyMessage>` or `EventReader<EntitySpawnEvent>`
- all messages are accessible via BevyEvents: `EventReader<MessageEvent<MyMessage>>` or `EventReader<EntitySpawnEvent>`
- for replication, the user just needs to add a `Replicate` component to entities that need to be replicated.

### Batteries-included

#### Serialization
Lightyear uses [bitcode](https://github.com/SoftbearStudios/bitcode/tree/main) for serialization, which supports very compact serialization.
It enables bit-packing (a bool will be serialized as a single bit).

#### Channels
Lightyear supports sending packets with different guarantees of ordering and reliability through the use of channels.

#### Input handling
Lightyear has special handling for player inputs (mouse presses, keyboards).
They are buffered every tick on the `Client`, and lightyear makes sure that the client input for tick `N` will be also processed on tick `N` on the server.
Inputs are protected against packet-loss: each packet will contain the client inputs for the last few frames.

#### Replication
Entities that have the `Replicate` component will be automatically replicated to clients. Only the components that change will be sent over the network.

This functionality is similar to what [bevy_replicon](https://github.com/lifescapegame/bevy_replicon) provides.

#### Advanced replication
Lightyear also supports easily enabling client-side prediction and snapshot interpolation to make sure that the game feels smooth for all players.
It is only a one-line change on the `Replicate` struct.

#### Configurable
Lightyear is highly configurable: you can configure the size of the input buffer, the amount of interpolation-delay, the packet send rate, etc.
All the configurations are accessible through the `ClientConfig` and `ServerConfig` structs.







On top of basic message-passing and replication, lightyear lets you easily add client-side prediction and snapshot interpolation to your game.
- Serialization
- Lightyear uses [bitcode](https://github.com/SoftbearStudios/bitcode/tree/main) for serialization, which supports very compact serialization. It uses bit-packing (a bool will be serialized as a single bit).
- Reliability
- Lightyear supports sending packets with different guarantees of ordering and reliability through the use of channels.
- Input handling
- Lightyear has special handling for player inputs (mouse presses, keyboards).
They are buffered every tick on the `Client`, and lightyear makes sure that the client input for tick `N` will be also processed on tick `N` on the server.
Inputs are protected against packet-loss: each packet will contain the client inputs for the last few frames.
- Replication
- Entities that have the `Replicate` component will be automatically replicated to clients. Only the components that change will be sent over the network. This functionality is similar to what [bevy_replicon](https://github.com/lifescapegame/bevy_replicon) provides.
- Advanced replication
- Lightyear also supports easily enabling client-side prediction and snapshot interpolation to make sure that the game feels smooth for all players.
It is only a one-line change on the `Replicate` struct.
- Lightyear also supports replicating components that contain references to other entities. The entities will be mapped from the Server World to the Client World upon replication.
- Configurable
- Lightyear is highly configurable: you can configure the size of the input buffer, the amount of interpolation-delay, the packet send rate, etc.
All the configurations are accessible through the `ClientConfig` and `ServerConfig` structs.


## Planned features

- Transport
- [ ] Adding a web-compatible transport (WebRTC or WebTransport)
- [ ] Add support for measuring packet loss
- Adding a web-compatible transport (WebRTC or WebTransport)
- Add support for measuring packet loss
- Metrics
- [ ] Improve the metrics/logs
- Improve the metrics/logs
- Packet
- [ ] Add support for channel priority and channel bandwidth limiting
- Add support for channel priority and channel bandwidth limiting
- Serialization
- [ ] Improve the serialization code to be more ergonomic, and to have fewer copies.
- Improve the serialization code to be more ergonomic, and to have fewer copies.
- Replication
- [ ] Enable support for entity-relations: replicating components that contain references to other entities.
- [ ] Add support for interest management: being able to only replicate entities to clients who are in the same 'zone' as them.
- [ ] Add more tests for subtle replication situations (receiving ComponentInsert after the entity has been despawned, etc.)
- Add better support for entity-relations: replicating components that contain references to other entities.
- Add support for interest management: being able to only replicate entities to clients who are in the same 'zone' as them.
- Add more tests for subtle replication situations (receiving ComponentInsert after the entity has been despawned, etc.)
7 changes: 7 additions & 0 deletions book/book.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,10 @@ language = "en"
multilingual = false
src = "src"
title = "Lightyear cheatbook"

[preprocessor]

[preprocessor.mermaid]
command = "mdbook-mermaid"

[output]
1 change: 0 additions & 1 deletion book/src/advanced_replication/component_sync_mode.md

This file was deleted.

1 change: 0 additions & 1 deletion book/src/advanced_replication/inputs.md

This file was deleted.

1 change: 0 additions & 1 deletion book/src/advanced_replication/interpolation.md

This file was deleted.

1 change: 0 additions & 1 deletion book/src/advanced_replication/prediction.md

This file was deleted.

1 change: 0 additions & 1 deletion book/src/advanced_replication/title.md

This file was deleted.

1 change: 0 additions & 1 deletion book/src/bevy/footguns.md

This file was deleted.

1 change: 0 additions & 1 deletion book/src/bevy/system_order.md

This file was deleted.

1 change: 0 additions & 1 deletion book/src/bevy/title.md

This file was deleted.

1 change: 0 additions & 1 deletion book/src/bevy_integration/client/time_sync.md

This file was deleted.

1 change: 0 additions & 1 deletion book/src/bevy_integration/client/title.md

This file was deleted.

1 change: 0 additions & 1 deletion book/src/bevy_integration/events.md

This file was deleted.

1 change: 0 additions & 1 deletion book/src/bevy_integration/server.md

This file was deleted.

1 change: 0 additions & 1 deletion book/src/bevy_integration/shared.md

This file was deleted.

1 change: 0 additions & 1 deletion book/src/bevy_integration/system_order.md

This file was deleted.

1 change: 0 additions & 1 deletion book/src/bevy_integration/title.md

This file was deleted.

1 change: 0 additions & 1 deletion book/src/boncepts/advanced_replication/interpolation.md

This file was deleted.

73 changes: 73 additions & 0 deletions book/src/concepts/advanced_replication/map_entities.md
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

109 changes: 109 additions & 0 deletions book/src/concepts/bevy_integration/system_order.md
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
}
```
Loading

0 comments on commit 31a59c7

Please sign in to comment.