#![forbid(unsafe_code)]
use bevy::{
prelude::*,
reflect::{FromType, GetTypeRegistration, TypeRegistry},
};
use ggrs::{Config, PlayerHandle};
use ggrs_stage::GGRSStage;
use reflect_resource::ReflectResource;
pub(crate) mod ggrs_stage;
pub(crate) mod reflect_resource;
pub(crate) mod world_snapshot;
pub const GGRS_UPDATE: &str = "ggrs_update";
const DEFAULT_FPS: usize = 60;
pub enum SessionType {
SyncTestSession,
P2PSession,
SpectatorSession,
}
impl Default for SessionType {
fn default() -> Self {
SessionType::SyncTestSession
}
}
#[derive(Component)]
pub struct Rollback {
id: u32,
}
impl Rollback {
pub fn new(id: u32) -> Self {
Self { id }
}
pub const fn id(&self) -> u32 {
self.id
}
}
#[derive(Default)]
pub struct RollbackIdProvider {
next_id: u32,
}
impl RollbackIdProvider {
pub fn next_id(&mut self) -> u32 {
if self.next_id == u32::MAX {
panic!("RollbackIdProvider: u32::MAX has been reached.");
}
let ret = self.next_id;
self.next_id += 1;
ret
}
}
pub struct GGRSPlugin<T: Config + Send + Sync> {
input_system: Option<Box<dyn System<In = PlayerHandle, Out = T::Input>>>,
fps: usize,
type_registry: TypeRegistry,
schedule: Schedule,
}
impl<T: Config + Send + Sync> GGRSPlugin<T> {
pub fn new() -> Self {
Self {
input_system: None,
fps: DEFAULT_FPS,
type_registry: TypeRegistry::default(),
schedule: Default::default(),
}
}
pub fn with_update_frequency(mut self, fps: usize) -> Self {
self.fps = fps;
self
}
pub fn with_input_system<Params>(
mut self,
input_fn: impl IntoSystem<PlayerHandle, T::Input, Params>,
) -> Self {
self.input_system = Some(Box::new(input_fn.system()));
self
}
pub fn register_rollback_type<Type>(self) -> Self
where
Type: GetTypeRegistration + Reflect + Default + Component,
{
let mut registry = self.type_registry.write();
registry.register::<Type>();
let registration = registry.get_mut(std::any::TypeId::of::<Type>()).unwrap();
registration.insert(<ReflectComponent as FromType<Type>>::from_type());
registration.insert(<ReflectResource as FromType<Type>>::from_type());
drop(registry);
self
}
pub fn with_rollback_schedule(mut self, schedule: Schedule) -> Self {
self.schedule = schedule;
self
}
pub fn build(self, app: &mut App) {
let mut input_system = self
.input_system
.expect("Adding an input system through GGRSBuilder::with_input_system is required");
input_system.initialize(&mut app.world);
let mut stage = GGRSStage::<T>::new(input_system);
stage.set_update_frequency(self.fps);
stage.set_schedule(self.schedule);
stage.set_type_registry(self.type_registry);
app.add_stage_before(CoreStage::Update, GGRS_UPDATE, stage);
app.insert_resource(RollbackIdProvider::default());
}
}