Skip to content

Commit

Permalink
fix
Browse files Browse the repository at this point in the history
  • Loading branch information
cbournhonesque-sc committed Dec 21, 2023
1 parent 06bd9bf commit 83adcad
Show file tree
Hide file tree
Showing 66 changed files with 473 additions and 613 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
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
38 changes: 23 additions & 15 deletions lightyear/src/client/interpolation/interpolation_history.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
use std::ops::Deref;

use bevy::prelude::{
Commands, Component, DetectChanges, Entity, EventReader, Query, Ref, Res, ResMut, With, Without,
Commands, Component, DetectChanges, Entity, Query, Ref, Res, ResMut, With, Without,
};
use tracing::{debug, error, info, trace};
use tracing::{debug, error, trace};

use crate::client::components::Confirmed;
use crate::client::components::{ComponentSyncMode, SyncComponent};
use crate::client::events::ComponentUpdateEvent;
use crate::client::interpolation::interpolate::InterpolateStatus;
use crate::client::interpolation::resource::InterpolationManager;
use crate::client::interpolation::Interpolated;
use crate::client::resource::Client;
use crate::protocol::Protocol;
Expand Down Expand Up @@ -76,6 +76,7 @@ impl<T: SyncComponent> ConfirmedHistory<T> {

// TODO: maybe add the component history on the Confirmed entity instead of Interpolated? would make more sense maybe
pub(crate) fn add_component_history<T: SyncComponent, P: Protocol>(
manager: Res<InterpolationManager>,
mut commands: Commands,
client: ResMut<Client<P>>,
interpolated_entities: Query<Entity, (Without<ConfirmedHistory<T>>, With<Interpolated>)>,
Expand All @@ -90,11 +91,14 @@ pub(crate) fn add_component_history<T: SyncComponent, P: Protocol>(
commands.get_entity(interpolated_entity).unwrap();
// insert history
let history = ConfirmedHistory::<T>::new();
// map any entities from confirmed to interpolated
let mut new_component = confirmed_component.deref().clone();
new_component.map_entities(Box::new(&manager.interpolated_entity_map));
match T::mode() {
ComponentSyncMode::Full => {
debug!("spawn interpolation history");
interpolated_entity_mut.insert((
// confirmed_component.deref().clone(),
new_component,
history,
InterpolateStatus::<T> {
start: None,
Expand All @@ -103,10 +107,11 @@ pub(crate) fn add_component_history<T: SyncComponent, P: Protocol>(
},
));
}
_ => {
ComponentSyncMode::Once | ComponentSyncMode::Simple => {
debug!("copy interpolation component");
// interpolated_entity_mut.insert(confirmed_component.deref().clone());
interpolated_entity_mut.insert(new_component);
}
ComponentSyncMode::None => {}
}
}
}
Expand All @@ -117,6 +122,7 @@ pub(crate) fn add_component_history<T: SyncComponent, P: Protocol>(
/// When we receive a server update, we need to store it in the confirmed history,
/// or update the interpolated component directly if InterpolatedComponentMode::Sync
pub(crate) fn apply_confirmed_update<T: SyncComponent, P: Protocol>(
manager: ResMut<InterpolationManager>,
client: Res<Client<P>>,
mut interpolated_entities: Query<
// TODO: handle missing T?
Expand All @@ -127,8 +133,8 @@ pub(crate) fn apply_confirmed_update<T: SyncComponent, P: Protocol>(
) {
for (confirmed_entity, confirmed, confirmed_component) in confirmed_entities.iter() {
if let Some(p) = confirmed.interpolated {
if confirmed_component.is_changed() {
if let Ok((interpolated_component, history_option)) =
if confirmed_component.is_changed() && !confirmed_component.is_added() {
if let Ok((mut interpolated_component, history_option)) =
interpolated_entities.get_mut(p)
{
match T::mode() {
Expand All @@ -150,17 +156,19 @@ pub(crate) fn apply_confirmed_update<T: SyncComponent, P: Protocol>(
);
continue;
};
info!(component = ?confirmed_component.name(), tick = ?channel.latest_tick, "adding confirmed update to history");
// map any entities from confirmed to predicted
let mut component = confirmed_component.deref().clone();
component.map_entities(Box::new(&manager.interpolated_entity_map));
trace!(component = ?component.name(), tick = ?channel.latest_tick, "adding confirmed update to history");
// assign the history at the value that the entity currently is
// TODO: think about mapping entities!
history
.buffer
.add_item(channel.latest_tick, confirmed_component.deref().clone());
history.buffer.add_item(channel.latest_tick, component);
}
// for sync-components, we just match the confirmed component
ComponentSyncMode::Simple => {
// TODO: think about mapping entities!
// *interpolated_component = confirmed_component.deref().clone();
// map any entities from confirmed to predicted
let mut component = confirmed_component.deref().clone();
component.map_entities(Box::new(&manager.interpolated_entity_map));
*interpolated_component = component;
}
_ => {}
}
Expand Down
Loading

0 comments on commit 83adcad

Please sign in to comment.