use crate::prelude::Tick;
use crate::tick::TickDuration;
use crate::time::{Overstep, TickDelta, TickInstant};
use bevy_app::{App, FixedFirst, Plugin};
use bevy_derive::{Deref, DerefMut};
use bevy_ecs::component::{Component, Mutable};
use bevy_ecs::entity::Entity;
use bevy_ecs::event::{EntityEvent, Event};
use bevy_ecs::prelude::{On, Resource};
use bevy_ecs::query::With;
use bevy_ecs::system::{Query, ResMut};
use bevy_reflect::Reflect;
use bevy_time::{Fixed, Time};
use core::ops::{Deref, DerefMut};
use core::time::Duration;
#[derive(Default, Debug, Clone, Reflect)]
pub struct Timeline<T: TimelineConfig> {
pub context: T::Context,
pub now: TickInstant,
#[reflect(ignore)]
pub marker: core::marker::PhantomData<T>,
}
pub trait TimelineConfig: Component + Send + Sync + Sized + 'static {
type Context;
type Timeline: NetworkTimeline + Default;
}
pub trait NetworkTimeline: Component<Mutability = Mutable> {
type Config: TimelineConfig;
const PAUSED_DURING_ROLLBACK: bool = true;
fn now(&self) -> TickInstant;
fn tick(&self) -> Tick;
fn overstep(&self) -> Overstep;
fn set_now(&mut self, now: TickInstant);
fn apply_delta(&mut self, delta: TickDelta);
fn apply_duration(&mut self, duration: Duration, tick_duration: Duration) {
self.apply_delta(TickDelta::from_duration(duration, tick_duration));
}
}
impl<C: TimelineConfig, T: Component<Mutability = Mutable> + DerefMut<Target = Timeline<C>>>
NetworkTimeline for T
{
type Config = C;
fn now(&self) -> TickInstant {
self.now
}
fn tick(&self) -> Tick {
self.now().tick()
}
fn overstep(&self) -> Overstep {
self.now().overstep()
}
fn set_now(&mut self, now: TickInstant) {
self.now = now;
}
fn apply_delta(&mut self, delta: TickDelta) {
self.now = self.now + delta;
}
}
impl<T: TimelineConfig> Deref for Timeline<T> {
type Target = T::Context;
fn deref(&self) -> &Self::Target {
&self.context
}
}
impl<T: TimelineConfig> DerefMut for Timeline<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.context
}
}
#[derive(Resource, Deref, DerefMut, Default, Clone, Reflect)]
pub struct LocalTimeline {
tick: Tick,
}
impl LocalTimeline {
pub fn tick(&self) -> Tick {
self.tick
}
pub fn apply_delta(&mut self, delta: i16) {
self.tick = self.tick + delta;
}
}
pub(crate) fn increment_local_tick(mut timeline: ResMut<LocalTimeline>) {
timeline.tick += 1;
}
pub struct NetworkTimelinePlugin<T> {
pub(crate) _marker: core::marker::PhantomData<T>,
}
impl<T> Default for NetworkTimelinePlugin<T> {
fn default() -> Self {
Self {
_marker: core::marker::PhantomData,
}
}
}
impl<T: NetworkTimeline> Plugin for NetworkTimelinePlugin<T> {
fn build(&self, _: &mut App) {}
}
#[derive(Event)]
pub struct SetTickDuration(pub Duration);
pub struct TimelinePlugin {
pub(crate) tick_duration: Duration,
}
impl TimelinePlugin {
fn update_tick_duration(trigger: On<SetTickDuration>, mut time: ResMut<Time<Fixed>>) {
time.set_timestep(trigger.0);
}
}
impl Plugin for TimelinePlugin {
fn build(&self, app: &mut App) {
app.init_resource::<LocalTimeline>();
app.insert_resource(TickDuration(self.tick_duration));
app.world_mut()
.resource_mut::<Time<Fixed>>()
.set_timestep(self.tick_duration);
app.add_observer(Self::update_tick_duration);
app.add_systems(FixedFirst, increment_local_tick);
}
fn finish(&self, app: &mut App) {
app.world_mut().trigger(SetTickDuration(self.tick_duration));
}
}
#[derive(EntityEvent, Debug)]
pub struct SyncEvent<T: TimelineConfig> {
pub entity: Entity,
pub tick_delta: i16,
marker: core::marker::PhantomData<T>,
}
impl<T: TimelineConfig> SyncEvent<T> {
pub fn new(entity: Entity, tick_delta: i16) -> Self {
SyncEvent {
entity,
tick_delta,
marker: core::marker::PhantomData,
}
}
}
impl<T: TimelineConfig> Clone for SyncEvent<T> {
fn clone(&self) -> Self {
*self
}
}
impl<T: TimelineConfig> Copy for SyncEvent<T> {}
#[derive(Component)]
pub enum Rollback {
FromState,
FromInputs,
}
pub fn is_in_rollback(client: Query<(), With<Rollback>>) -> bool {
client.single().is_ok()
}