Skip to content

Commit

Permalink
Merge pull request #20 from cBournhonesque/cb/sync-groups
Browse files Browse the repository at this point in the history
Simplify entity mapping for prediction/replication
  • Loading branch information
cBournhonesque authored Dec 21, 2023
2 parents 06bd9bf + 4f12f13 commit e1f3b5b
Show file tree
Hide file tree
Showing 68 changed files with 483 additions and 627 deletions.
23 changes: 22 additions & 1 deletion NOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,11 @@
because start tick or end tick are not updated correctly in some edge cases.


- add PredictionGroup and InterpolationGroup.
- add PredictionGroup and InterpolationGroup?
- on top of ReplicationGroup?
- or do we just re-use the replication group id (that usually will have a remote entity id) and use it to see the prediction/interpolation group?
- then we add the prediction group id on the Confirmed or Predicted components?
- when we receive a replicated group with ShouldBePredicted, we udpate the replication graph of the prediction group.
- Then we don't really need the Confirmed/Predicted components anymore, we could just have resources on the Prediction or Interpolation plugin
- The resource needs:
- confirmed<->predicted mapping
Expand All @@ -25,7 +26,27 @@
- for each entity, fetch the confirmed/predicted entity
- do entity mapping if needed
- users can add their own entities in the prediction group (even if thre )
- examples:
- a shooter, we shoot bullets. the bullets should be in our prediction group?
I guess it's not needed if we don't do rollback for those bullets, i.e. we don't give them a Predicted component.
Or we could create the bullet, make it part of the entity group; the server will create the bullet a bit later.
When the bullet gets replicated on client; we should be able to map the Confirmed to the predicted bullet; so we don't spawn a new predicted.
(in practice, for important stuff, we would just wait for the server replication to spawn the entity (instead of spawning it on client and then deleting it if the server version doesn't spawn it?, and for non-important stuff we would just spawn a short-lived entity that is not predicted.)
- a character has HasWeapon(Entity), weapon has HasParent(Entity) which forms a cycle. I guess instead of creating this graph of deps,
we should just deal with all spawns first separately! Same for prediction, we first do all spawns first



- TODO: Give an option for rollback to choose how to perform the rollback!
- the default option is to snapback instantly to the rollback state.
- another option is: snapback to rollback state, perform rollback, then tell the user the previous predicted state and the new predicted state.
for example they could choose to lerp over several frames from the [old to new] (i.e correct only 10% of the way).
this would cause many consecutive rollback frames, but smoother corrections.
- register a component RollbackResult<C> {
// use option because the component could have gotten removed
old: Option<C>,
new: Option<C>,
}


- DEBUGGING REPLICATION BOX:
Expand Down
16 changes: 8 additions & 8 deletions examples/replication_groups/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -278,7 +278,7 @@ pub(crate) fn interpolate(
assert_eq!(tail_status.current, parent_status.current);
*tail = tail_start_value.clone();
*parent_position = pos_start.clone();
info!(
debug!(
?tail,
?parent_position,
"after interpolation; CURRENT = START"
Expand All @@ -296,7 +296,7 @@ pub(crate) fn interpolate(
assert_eq!(tail_status.current, parent_status.current);
*tail = tail_end_value.clone();
*parent_position = pos_end.clone();
info!(
debug!(
?tail,
?parent_position,
"after interpolation; CURRENT = END"
Expand Down Expand Up @@ -324,13 +324,13 @@ pub(crate) fn interpolate(
!= tail_start_value.0.front().unwrap().0
{
tail.0.push_front(tail_end_value.0.front().unwrap().clone());
info!("ADD POINT");
debug!("ADD POINT");
}
// the path is straight! just move the head and adjust the tail
*parent_position =
PlayerPosition::lerp(pos_start.clone(), pos_end.clone(), t);
tail.shorten_back(parent_position.0, tail_length.0);
info!(
debug!(
?tail,
?parent_position,
"after interpolation; FIRST SEGMENT"
Expand Down Expand Up @@ -371,7 +371,7 @@ pub(crate) fn interpolate(
// now move the head by `pos_distance_to_do` while remaining on the tail path
for i in (0..segment_idx).rev() {
let dist = segment_length(parent_position.0, tail_end_value.0[i].0);
info!(
debug!(
?i,
?dist,
?pos_distance_to_do,
Expand All @@ -380,7 +380,7 @@ pub(crate) fn interpolate(
"in other segments"
);
if pos_distance_to_do < 1000.0 * f32::EPSILON {
info!(?tail, ?parent_position, "after interpolation; ON POINT");
debug!(?tail, ?parent_position, "after interpolation; ON POINT");
// no need to change anything
continue 'outer;
}
Expand All @@ -406,7 +406,7 @@ pub(crate) fn interpolate(
.1
.get_tail(tail_end_value.0[i].0, dist - pos_distance_to_do);
tail.shorten_back(parent_position.0, tail_length.0);
info!(?tail, ?parent_position, "after interpolation; ELSE");
debug!(?tail, ?parent_position, "after interpolation; ELSE");
continue 'outer;
}
}
Expand All @@ -419,7 +419,7 @@ pub(crate) fn interpolate(
.1
.get_tail(pos_end.0, dist - pos_distance_to_do);
tail.shorten_back(parent_position.0, tail_length.0);
info!(?tail, ?parent_position, "after interpolation; ELSE FIRST");
debug!(?tail, ?parent_position, "after interpolation; ELSE FIRST");
}
}
}
Expand Down
8 changes: 2 additions & 6 deletions examples/replication_groups/shared.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,10 @@ pub struct SharedPlugin;

impl Plugin for SharedPlugin {
fn build(&self, app: &mut App) {
app.add_plugins(WorldInspectorPlugin::new());
app.add_systems(Update, draw_snakes);
}
}

// head
// snake

// This system defines how we update the player's positions when we receive an input
pub(crate) fn shared_movement_behaviour(position: &mut PlayerPosition, input: &Inputs) {
const MOVE_SPEED: f32 = 10.0;
Expand Down Expand Up @@ -121,13 +117,13 @@ pub(crate) fn draw_snakes(
if position.0.x != points.0.front().unwrap().0.x
&& position.0.y != points.0.front().unwrap().0.y
{
info!("DIAGONAL");
debug!("DIAGONAL");
}
// draw the rest of the lines
for (start, end) in points.0.iter().zip(points.0.iter().skip(1)) {
gizmos.line_2d(start.0, end.0, color.0);
if start.0.x != end.0.x && start.0.y != end.0.y {
info!("DIAGONAL");
debug!("DIAGONAL");
}
}
}
Expand Down
3 changes: 2 additions & 1 deletion lightyear/src/channel/builder.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
//! This module contains the [`Channel`] trait
use lightyear_macros::ChannelInternal;
use std::time::Duration;

use lightyear_macros::ChannelInternal;

use crate::channel::receivers::ordered_reliable::OrderedReliableReceiver;
use crate::channel::receivers::sequenced_reliable::SequencedReliableReceiver;
use crate::channel::receivers::sequenced_unreliable::SequencedUnreliableReceiver;
Expand Down
6 changes: 3 additions & 3 deletions lightyear/src/channel/receivers/mod.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use enum_dispatch::enum_dispatch;

use crate::packet::message::{MessageContainer, SingleData};
use crate::packet::message::MessageContainer;
use crate::packet::message::{FragmentData, SingleData};
use crate::shared::tick_manager::TickManager;
use crate::shared::time_manager::TimeManager;
use enum_dispatch::enum_dispatch;

/// Utilities to receive a Message from multiple fragment packets
pub(crate) mod fragment_receiver;
Expand Down
9 changes: 2 additions & 7 deletions lightyear/src/channel/senders/fragment_ack_receiver.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
use bevy::utils::HashMap;

use anyhow::Result;
use bytes::Bytes;
use bevy::utils::HashMap;
use tracing::{error, trace};

use crate::packet::message::{FragmentData, FragmentIndex, MessageAck, MessageId, SingleData};
use crate::packet::packet::FRAGMENT_SIZE;
use crate::packet::message::{FragmentIndex, MessageId};
use crate::shared::time_manager::WrappedTime;

/// `FragmentReceiver` is used to reconstruct fragmented messages
Expand Down Expand Up @@ -103,8 +100,6 @@ impl FragmentAckTracker {

#[cfg(test)]
mod tests {
use crate::channel::senders::fragment_sender::FragmentSender;

use super::*;

#[test]
Expand Down
10 changes: 4 additions & 6 deletions lightyear/src/channel/senders/mod.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
use std::collections::VecDeque;

use bytes::Bytes;
use crossbeam_channel::Receiver;
use enum_dispatch::enum_dispatch;

use crate::packet::message::{FragmentData, MessageAck, MessageId, SingleData};
use crate::shared::ping::manager::PingManager;
use crate::shared::tick_manager::TickManager;
use crate::shared::time_manager::TimeManager;
use bytes::Bytes;
use crossbeam_channel::Receiver;
use enum_dispatch::enum_dispatch;
use std::collections::VecDeque;

pub(crate) mod fragment_ack_receiver;
pub(crate) mod fragment_sender;
Expand Down
2 changes: 1 addition & 1 deletion lightyear/src/channel/senders/reliable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use std::time::Duration;

use bytes::Bytes;
use crossbeam_channel::Receiver;
use tracing::{info, trace};
use tracing::trace;

use crate::channel::builder::ReliableSettings;
use crate::channel::senders::fragment_sender::FragmentSender;
Expand Down
10 changes: 4 additions & 6 deletions lightyear/src/channel/senders/unordered_unreliable_with_acks.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
use bevy::utils::HashMap;
use std::collections::VecDeque;

use bytes::Bytes;
use crossbeam_channel::{Receiver, Sender};

use crate::channel::senders::fragment_ack_receiver::FragmentAckReceiver;
use crate::channel::senders::fragment_sender::FragmentSender;
use crate::channel::senders::ChannelSend;
use crate::packet::message::{FragmentData, FragmentIndex, MessageAck, MessageId, SingleData};
use crate::packet::message::{FragmentData, MessageAck, MessageId, SingleData};
use crate::shared::ping::manager::PingManager;
use crate::shared::tick_manager::TickManager;
use crate::shared::time_manager::{TimeManager, WrappedTime};
use crossbeam_channel::{Receiver, Sender};

const DISCARD_AFTER: chrono::Duration = chrono::Duration::milliseconds(3000);

Expand Down Expand Up @@ -130,10 +129,9 @@ impl ChannelSend for UnorderedUnreliableWithAcksSender {

#[cfg(test)]
mod tests {
use super::*;
use crate::channel::senders::fragment_ack_receiver::FragmentAckTracker;
use crate::packet::packet::FRAGMENT_SIZE;
use crossbeam_channel::TryRecvError;

use super::*;

#[test]
fn test_receive_ack() {
Expand Down
8 changes: 4 additions & 4 deletions lightyear/src/client/components.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
/*!
Defines components that are used for the client-side prediction and interpolation
*/
use std::fmt::Debug;

use crate::prelude::{Named, TypeNamed};
use bevy::ecs::component::TableStorage;
use bevy::prelude::{Component, Entity};
use std::fmt::{Debug, Formatter};

use crate::prelude::{MapEntities, Named};

/// Marks an entity that contains the server-updates that are received from the Server
/// (this entity is a copy of Predicted that is RTT ticks behind)
Expand All @@ -15,7 +15,7 @@ pub struct Confirmed {
pub interpolated: Option<Entity>,
}

pub trait SyncComponent: Component + Clone + PartialEq + Named {
pub trait SyncComponent: Component + Clone + PartialEq + Named + for<'a> MapEntities<'a> {
fn mode() -> ComponentSyncMode;
}

Expand Down
2 changes: 0 additions & 2 deletions lightyear/src/client/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@ use crate::client::interpolation::plugin::InterpolationConfig;
use crate::client::prediction::plugin::PredictionConfig;
use crate::client::sync::SyncConfig;
use crate::shared::config::SharedConfig;
use crate::transport::io::IoConfig;

use crate::shared::ping::manager::PingConfig;

#[derive(Clone)]
Expand Down
12 changes: 3 additions & 9 deletions lightyear/src/client/connection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,14 @@
use std::time::Duration;

use anyhow::Result;
use bevy::prelude::World;
use tracing::{debug, info, trace};
use tracing::{debug, trace};

use crate::channel::builder::PingChannel;
use crate::client::sync::SyncConfig;
use crate::connection::events::ConnectionEvents;
use crate::connection::message::ProtocolMessage;
use crate::inputs::input_buffer::InputBuffer;
use crate::packet::packet_manager::Payload;
use crate::protocol::channel::{ChannelKind, ChannelRegistry};
use crate::protocol::channel::ChannelRegistry;
use crate::protocol::Protocol;
use crate::serialize::reader::ReadBuffer;
use crate::shared::ping::manager::{PingConfig, PingManager};
use crate::shared::ping::message::SyncMessage;
use crate::shared::ping::manager::PingConfig;
use crate::shared::tick_manager::Tick;
use crate::shared::tick_manager::TickManager;
use crate::shared::time_manager::TimeManager;
Expand Down
1 change: 0 additions & 1 deletion lightyear/src/client/events.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
//! Wrapper around [`ConnectionEvents`] that adds client-specific functionality
//!
use crate::connection::events::ConnectionEvents;
use crate::prelude::Tick;
use crate::protocol::Protocol;

pub struct ClientEvents<P: Protocol> {
Expand Down
1 change: 0 additions & 1 deletion lightyear/src/client/input.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
//! Handles client-generated inputs
use anyhow::Context;
use bevy::prelude::{
not, App, EventReader, EventWriter, FixedUpdate, IntoSystemConfigs, IntoSystemSetConfigs,
Plugin, PostUpdate, Res, ResMut, SystemSet,
Expand Down
22 changes: 9 additions & 13 deletions lightyear/src/client/interpolation/despawn.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
use std::collections::HashMap;

use bevy::prelude::{Commands, Entity, EventReader, Query, RemovedComponents, ResMut, Resource};
use bevy::prelude::{Commands, EventReader, Query, RemovedComponents, ResMut};

use crate::client::components::{Confirmed, SyncComponent};
use crate::client::interpolation::interpolate::InterpolateStatus;
use crate::client::interpolation::interpolation_history::ConfirmedHistory;
use crate::client::interpolation::resource::InterpolationManager;
use crate::shared::events::ComponentRemoveEvent;

// Despawn logic:
Expand All @@ -17,13 +16,6 @@ use crate::shared::events::ComponentRemoveEvent;
// - TODO: despawning another client entity as a consequence from prediction, but we want to roll that back:
// - maybe we don't do it, and we wait until we are sure (confirmed despawn) before actually despawning the entity

#[derive(Resource, Default)]
/// Mapping from confirmed entities to interpolated entities
/// Needed to despawn interpolated entities when the confirmed entity gets despawned
pub struct InterpolationMapping {
pub confirmed_to_interpolated: HashMap<Entity, Entity>,
}

/// Remove the component from interpolated entities when it gets removed from confirmed
pub(crate) fn removed_components<C: SyncComponent>(
mut commands: Commands,
Expand All @@ -46,12 +38,16 @@ pub(crate) fn removed_components<C: SyncComponent>(
/// Despawn interpolated entities when the confirmed entity gets despawned
/// TODO: we should despawn interpolated only when it reaches the latest confirmed snapshot?
pub(crate) fn despawn_interpolated(
mut manager: ResMut<InterpolationManager>,
mut commands: Commands,
mut mapping: ResMut<InterpolationMapping>,
mut query: RemovedComponents<Confirmed>,
) {
for entity in query.read() {
if let Some(interpolated) = mapping.confirmed_to_interpolated.remove(&entity) {
for confirmed_entity in query.read() {
if let Some(interpolated) = manager
.interpolated_entity_map
.remote_to_interpolated
.remove(&confirmed_entity)
{
if let Some(mut entity_mut) = commands.get_entity(interpolated) {
entity_mut.despawn();
}
Expand Down
2 changes: 1 addition & 1 deletion lightyear/src/client/interpolation/interpolate.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use bevy::prelude::{Component, Query, ResMut};
use tracing::{info, trace, warn};
use tracing::trace;

use crate::client::components::{ComponentSyncMode, SyncComponent};
use crate::client::interpolation::interpolation_history::ConfirmedHistory;
Expand Down
Loading

0 comments on commit e1f3b5b

Please sign in to comment.