use crate::{
AdvanceWorld, AdvanceWorldSystems, ConfirmedFrameCount, LoadWorld, LoadWorldSystems,
RollbackFrameCount,
};
use bevy::app::{App, Plugin};
use bevy::prelude::*;
use ggrs::Frame;
use std::cmp::Ordering;
#[derive(Component, Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct RollbackDespawned(Frame);
pub struct RollbackDespawnPlugin;
impl Plugin for RollbackDespawnPlugin {
fn build(&self, app: &mut App) {
app.register_disabling_component::<RollbackDespawned>();
app.add_systems(
LoadWorld,
resurrect_entities.in_set(LoadWorldSystems::EntityResurrect),
)
.add_systems(
AdvanceWorld,
despawn_confirmed_entities.in_set(AdvanceWorldSystems::DespawnConfirmed),
);
}
}
fn resurrect_entities(
world: &mut World,
despawn_query: &mut QueryState<(Entity, &RollbackDespawned)>,
) {
let rollback_frame = world.resource::<RollbackFrameCount>();
despawn_query
.iter(world)
.filter_map(|(entity, despawned_frame)| {
Some(entity).filter(|_e| despawned_frame > rollback_frame)
})
.collect::<Vec<_>>()
.into_iter()
.for_each(|entity| {
world.entity_mut(entity).remove::<RollbackDespawned>();
});
}
fn despawn_confirmed_entities(
world: &mut World,
despawn_query: &mut QueryState<(Entity, &RollbackDespawned)>,
mut local: Local<ConfirmedFrameCount>,
) {
let confirmed_frame = world.resource::<ConfirmedFrameCount>();
if *confirmed_frame == *local {
return; }
*local = *confirmed_frame;
despawn_query
.iter(world)
.filter_map(|(entity, despawned_frame)| {
Some(entity).filter(|_e| despawned_frame <= confirmed_frame)
})
.collect::<Vec<_>>()
.into_iter()
.for_each(|entity| {
world.despawn(entity);
});
}
pub trait RollbackDespawnCommandExtension {
fn despawn_rollback(&mut self);
}
impl RollbackDespawnCommandExtension for EntityCommands<'_> {
fn despawn_rollback(&mut self) {
self.queue_silenced(despawn_rollback);
}
}
fn despawn_rollback(mut entity: EntityWorldMut) {
if let Some(&RollbackFrameCount(frame)) = entity.get_resource::<RollbackFrameCount>() {
let &ConfirmedFrameCount(confirmed) = entity.get_resource::<ConfirmedFrameCount>().unwrap();
if confirmed < frame {
entity.insert_recursive::<Children>(RollbackDespawned(frame));
return;
}
}
entity.despawn();
}
macro_rules! newtype_partial_ord {
($i:ident, $j:ident) => {
impl PartialEq<$j> for $i {
fn eq(&self, other: &$j) -> bool {
self.0 == other.0
}
}
impl PartialOrd<$j> for $i {
fn partial_cmp(&self, other: &$j) -> Option<Ordering> {
Some(self.0.cmp(&other.0))
}
}
};
}
newtype_partial_ord!(RollbackDespawned, RollbackFrameCount);
newtype_partial_ord!(RollbackDespawned, ConfirmedFrameCount);