-
Notifications
You must be signed in to change notification settings - Fork 47
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #19 from cBournhonesque/cb/fix-relations
Improve entity mapping, interpolation, prediction
- Loading branch information
Showing
48 changed files
with
2,267 additions
and
416 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
# Interpolation | ||
|
||
## Introduction | ||
Interpolation means that we will store replicated entities in a buffer, and then interpolate between the last two states to get a smoother movement. | ||
|
||
See this excellent explanation from Valve: [link](https://developer.valvesoftware.com/wiki/Source_Multiplayer_Networking) | ||
or this one from Gabriel Gambetta: [link](https://www.gabrielgambetta.com/entity-interpolation.html) | ||
|
||
|
||
## Implementation | ||
|
||
In lightyear, interpolation can be automatically managed for you. | ||
|
||
Every replicated entity can specify to which clients it should be interpolated to: | ||
```rust,noplayground | ||
Replicate { | ||
interpolation_target: NetworkTarget::AllExcept(vec![id]), | ||
..default() | ||
}, | ||
``` | ||
|
||
This means that all clients except for the one with id `id` will interpolate this entity. | ||
In practice, it means that they will store in a buffer the history for all components that are enabled for Interpolation. | ||
|
||
|
||
## Component Sync Mode | ||
|
||
Not all components in the protocol are necessarily interpolated. | ||
Each component can implement a `ComponentSyncMode` that defines how it gets handled for the `Predicted` and `Interpolated` entities. | ||
|
||
Only components that have `ComponentSyncMode::Full` will be interpolated. | ||
|
||
|
||
## Interpolation function | ||
|
||
By default, the implementation function for a given component will be linear interpolation. | ||
It is also possibly to override this behaviour by implementing a custom interpolation function. | ||
|
||
Here is an example: | ||
|
||
```rust,noplayground | ||
#[derive(Component, Message, Serialize, Deserialize, Debug, PartialEq, Clone)] | ||
pub struct Component1(pub f32); | ||
#[derive(Component, Message, Serialize, Deserialize, Debug, PartialEq, Clone)] | ||
pub struct Component2(pub f32); | ||
#[component_protocol(protocol = "MyProtocol")] | ||
pub enum MyComponentProtocol { | ||
#[sync(full)] | ||
Component1(Component1), | ||
#[sync(full, lerp = "MyCustomInterpFn")] | ||
Component2(Component2), | ||
} | ||
// custom interpolation logic | ||
pub struct MyCustomInterpFn; | ||
impl<C> InterpFn<C> for MyCustomInterpFn { | ||
fn lerp(start: C, _other: C, _t: f32) -> C { | ||
start | ||
} | ||
} | ||
``` | ||
|
||
You will have to add the attribute `lerp = "TYPE_NAME"` to the component. | ||
The `TYPE_NAME` must be a type that implements the `InterpFn` trait. | ||
```rust,noplayground | ||
pub trait InterpFn<C> { | ||
fn lerp(start: C, other: C, t: f32) -> C; | ||
} | ||
``` | ||
|
||
|
||
## Complex interpolation | ||
|
||
In some cases, the interpolation logic can be more complex than a simple linear interpolation. | ||
For example, we might want to have different interpolation functions for different entities, even if they have the same component type. | ||
Or we might want to do interpolation based on multiple comments (applying some cubic spline interpolation that relies not only on the position, | ||
but also on the velocity and acceleration). | ||
|
||
In those cases, you can disable the default per-component interpolation logic and provide your own custom logic. | ||
```rust,noplayground``` | ||
|
||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
# Replication groups | ||
|
||
This is an example that shows how to make Lightyear replicate multiple entities in a single message, | ||
to make sure that they are always in a consistent state (i.e. that entities in a group are all replicated on the same tick). | ||
|
||
Without a replication group, it is possible that one entity is replicated with the server's tick 10, and another entity | ||
is replicated with the server's tick 11. This is not a problem if the entities are independent, but if they depend on each other (for example | ||
for client prediction) it could cause issues. | ||
|
||
This is especially useful if you have an entity that depends on another entity (e.g. a player and its weapon), | ||
the weapon might have a component `Parent(owner: Entity)` which references the player entity. | ||
In which case we **need** the player entity to be spawned before the weapon entity, otherwise `Parent` component | ||
will reference an entity that does not exist. | ||
|
||
|
||
## Running the example | ||
|
||
To start the server, run `cargo run --example replication_groups server` | ||
|
||
Then you can launch multiple clients with the commands: | ||
|
||
- `cargo run --example replication_groups client -c 1` | ||
|
||
- `cargo run --example replication_groups client -c 2 --client-port 2000` |
Oops, something went wrong.