#![allow(type_alias_bounds)]
#![allow(clippy::module_inception)]
use crate::input_buffer::{Compressed, InputBuffer};
use alloc::{format, string::String, vec, vec::Vec};
use bevy_app::App;
use bevy_ecs::bundle::Bundle;
use bevy_ecs::query::{IterQueryData, QueryData};
use bevy_ecs::{
component::Component,
entity::{Entity, EntityMapper, MapEntities},
};
use bevy_reflect::Reflect;
use bevy_utils::prelude::DebugName;
use core::fmt::{Debug, Formatter, Write};
use core::time::Duration;
use lightyear_core::prelude::Tick;
#[cfg(feature = "interpolation")]
use lightyear_interpolation::plugin::InterpolationDelay;
use serde::de::DeserializeOwned;
use serde::{Deserialize, Serialize};
#[allow(unused_imports)]
use tracing::{debug, error, info, trace};
#[derive(Serialize, Deserialize, Clone, Copy, PartialEq, Debug, Reflect)]
pub enum InputTarget {
Entity(Entity),
PreSpawned(u64),
}
#[derive(Serialize, Deserialize, Clone, PartialEq, Debug, Reflect)]
pub struct PerTargetData<S> {
pub target: InputTarget,
pub states: S,
}
pub trait InputSnapshot: Send + Sync + Debug + Clone + PartialEq + Default + 'static {
fn decay_tick(&mut self, tick_duration: Duration);
}
pub trait ActionStateQueryData {
type Mut: QueryData + IterQueryData;
type MutItemInner<'w>;
type Main: Component + Send + Sync + Default + 'static;
type Bundle: Bundle + Send + Sync + 'static;
fn as_read_only<'a, 'w: 'a, 's>(
state: &'a <Self::Mut as QueryData>::Item<'w, 's>,
) -> <<Self::Mut as QueryData>::ReadOnly as QueryData>::Item<'a, 's>;
fn into_inner<'w, 's>(
mut_item: <Self::Mut as QueryData>::Item<'w, 's>,
) -> Self::MutItemInner<'w>;
fn as_mut<'w>(bundle: &'w mut Self::Bundle) -> Self::MutItemInner<'w>;
fn base_value() -> Self::Bundle;
}
pub(crate) type StateRef<S: ActionStateSequence> =
<<S::State as ActionStateQueryData>::Mut as QueryData>::ReadOnly;
pub(crate) type StateRefItem<'w, 's, S: ActionStateSequence> =
<StateRef<S> as QueryData>::Item<'w, 's>;
pub(crate) type StateMut<S: ActionStateSequence> = <S::State as ActionStateQueryData>::Mut;
pub(crate) type StateMutItem<'w, 's, S: ActionStateSequence> =
<StateMut<S> as QueryData>::Item<'w, 's>;
pub(crate) type StateMutItemInner<'w, S: ActionStateSequence> =
<S::State as ActionStateQueryData>::MutItemInner<'w>;
pub trait ActionStateSequence:
Serialize + DeserializeOwned + Clone + Debug + Send + Sync + 'static
{
type Action: Send + Sync + 'static;
type Snapshot: InputSnapshot;
type State: ActionStateQueryData;
type Marker: Component;
fn len(&self) -> usize;
fn register_required_components(app: &mut App) {
app.register_required_components::<InputBuffer<Self::Snapshot, Self::Action>, <Self::State as ActionStateQueryData>::Main>();
app.register_required_components::<Self::Marker, InputBuffer<Self::Snapshot, Self::Action>>();
}
fn get_snapshots_from_message(
self,
tick_duration: Duration,
) -> impl Iterator<Item = Compressed<Self::Snapshot>>;
fn update_buffer(
self,
input_buffer: &mut InputBuffer<Self::Snapshot, Self::Action>,
end_tick: Tick,
tick_duration: Duration,
) -> Option<Tick> {
let last_remote_tick = input_buffer.last_remote_tick;
let previous_end_tick = input_buffer.end_tick();
let mut previous_predicted_input =
last_remote_tick.and_then(|t| input_buffer.get(t)).cloned();
let mut earliest_mismatch: Option<Tick> = None;
let start_tick = end_tick + 1 - self.len() as u32;
let mut latest_received_input = None;
for (delta, input) in self.get_snapshots_from_message(tick_duration).enumerate() {
let tick = start_tick + Tick(delta as u32);
if previous_end_tick.is_some_and(|end_tick| end_tick >= tick) {
previous_predicted_input = input_buffer.get(tick).cloned();
} else {
previous_predicted_input = previous_predicted_input.map(|prev| {
let mut prev = prev;
prev.decay_tick(tick_duration);
prev
});
};
if earliest_mismatch.is_some() {
input_buffer.set_raw(tick, input);
} else {
match input {
Compressed::Absent => latest_received_input = None,
Compressed::Input(latest) => latest_received_input = Some(latest),
_ => {}
}
if last_remote_tick.is_none_or(|t| tick > t) {
if match (&previous_predicted_input, &latest_received_input) {
(Some(prev), Some(latest)) => prev == latest,
(None, None) => true,
_ => false,
} {
if previous_end_tick.is_none_or(|end_tick| tick > end_tick) {
input_buffer
.set_raw(tick, Compressed::from(latest_received_input.clone()));
}
continue;
}
debug!(
"Mismatch detected at tick {tick:?} for new_input {latest_received_input:?}. Previous predicted input: {previous_predicted_input:?}"
);
input_buffer.set_raw(tick, Compressed::from(latest_received_input.clone()));
input_buffer.clip_after(tick);
earliest_mismatch = Some(tick);
}
}
}
input_buffer.last_remote_tick = Some(end_tick);
trace!("input buffer after update: {input_buffer:?}");
earliest_mismatch
}
fn build_from_input_buffer(
input_buffer: &InputBuffer<Self::Snapshot, Self::Action>,
num_ticks: u32,
end_tick: Tick,
) -> Option<Self>
where
Self: Sized;
fn to_snapshot<'w, 's>(state: StateRefItem<'w, 's, Self>) -> Self::Snapshot;
fn from_snapshot<'w>(state: StateMutItemInner<'w, Self>, snapshot: &Self::Snapshot);
fn from_snapshot_transitions<'w>(
state: StateMutItemInner<'w, Self>,
snapshot: &Self::Snapshot,
) {
Self::from_snapshot(state, snapshot);
}
fn decay_tick(state: StateMutItem<Self>, tick_duration: Duration) {
let mut snapshot =
Self::to_snapshot(<Self::State as ActionStateQueryData>::as_read_only(&state));
snapshot.decay_tick(tick_duration);
Self::from_snapshot(
<Self::State as ActionStateQueryData>::into_inner(state),
&snapshot,
);
}
}
#[derive(Serialize, Deserialize, Clone, PartialEq, Debug, Reflect)]
pub struct InputMessage<S> {
#[cfg(feature = "interpolation")]
pub interpolation_delay: Option<InterpolationDelay>,
pub end_tick: Tick,
pub inputs: Vec<PerTargetData<S>>,
pub rebroadcast: bool,
}
impl<S: ActionStateSequence + MapEntities> MapEntities for InputMessage<S> {
fn map_entities<M: EntityMapper>(&mut self, entity_mapper: &mut M) {
self.inputs.iter_mut().for_each(|data| {
if let InputTarget::Entity(e) = &mut data.target {
*e = entity_mapper.get_mapped(*e);
}
data.states.map_entities(entity_mapper);
});
}
}
impl<S: ActionStateSequence + Debug> core::fmt::Display for InputMessage<S> {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
let debug_name = DebugName::type_name::<S::Action>();
let ty = debug_name.shortname();
if self.inputs.is_empty() {
return write!(f, "EmptyInputMessage<{ty:?}>");
}
let buffer_str = self
.inputs
.iter()
.map(|data| {
let mut str = format!("Target: {:?}\n", data.target);
let _ = writeln!(&mut str, "States: {:?}", data.states);
str
})
.collect::<Vec<String>>()
.join("\n");
write!(
f,
"InputMessage<{ty:?}> (End Tick: {:?}):\n{buffer_str}",
self.end_tick
)
}
}
impl<S: ActionStateSequence> InputMessage<S> {
pub fn new(end_tick: Tick) -> Self {
Self {
#[cfg(feature = "interpolation")]
interpolation_delay: None,
end_tick,
rebroadcast: false,
inputs: vec![],
}
}
}