Skip to content

Commit

Permalink
Add a RollbackEventHook Trait
Browse files Browse the repository at this point in the history
This allows you to register functions that will be called in response
to GGRS messages.
  • Loading branch information
zicklag committed Nov 13, 2022
1 parent 7a016aa commit bfdfbc1
Show file tree
Hide file tree
Showing 2 changed files with 73 additions and 5 deletions.
38 changes: 34 additions & 4 deletions src/ggrs_stage.rs
@@ -1,4 +1,4 @@
use crate::{world_snapshot::WorldSnapshot, SessionType};
use crate::{world_snapshot::WorldSnapshot, RollbackEventHook, SessionType};
use bevy::{prelude::*, reflect::TypeRegistry};
use ggrs::{
Config, GGRSError, GGRSRequest, GameStateCell, InputStatus, P2PSession, PlayerHandle,
Expand Down Expand Up @@ -29,6 +29,7 @@ where
accumulator: Duration,
/// boolean to see if we should run slow to let remote clients catch up
run_slow: bool,
hooks: Vec<Box<dyn RollbackEventHook>>,
}

impl<T: Config + Send + Sync> Stage for GGRSStage<T> {
Expand Down Expand Up @@ -81,6 +82,7 @@ impl<T: Config> GGRSStage<T> {
last_update: Instant::now(),
accumulator: Duration::ZERO,
run_slow: false,
hooks: Vec::new(),
}
}

Expand Down Expand Up @@ -191,9 +193,33 @@ impl<T: Config> GGRSStage<T> {
pub(crate) fn handle_requests(&mut self, requests: Vec<GGRSRequest<T>>, world: &mut World) {
for request in requests {
match request {
GGRSRequest::SaveGameState { cell, frame } => self.save_world(cell, frame, world),
GGRSRequest::LoadGameState { frame, .. } => self.load_world(frame, world),
GGRSRequest::AdvanceFrame { inputs } => self.advance_frame(inputs, world),
GGRSRequest::SaveGameState { cell, frame } => {
for hook in &mut self.hooks {
hook.pre_save(frame, self.snapshots.len(), world);
}
self.save_world(cell, frame, world);
for hook in &mut self.hooks {
hook.post_save(frame, self.snapshots.len(), world);
}
}
GGRSRequest::LoadGameState { frame, .. } => {
for hook in &mut self.hooks {
hook.pre_load(frame, self.snapshots.len(), world);
}
self.load_world(frame, world);
for hook in &mut self.hooks {
hook.post_load(frame, self.snapshots.len(), world);
}
}
GGRSRequest::AdvanceFrame { inputs } => {
for hook in &mut self.hooks {
hook.pre_advance(world);
}
self.advance_frame(inputs, world);
for hook in &mut self.hooks {
hook.post_advance(world);
}
}
}
}
}
Expand Down Expand Up @@ -247,6 +273,10 @@ impl<T: Config> GGRSStage<T> {
self.schedule = schedule;
}

pub(crate) fn set_hooks(&mut self, hooks: Vec<Box<dyn RollbackEventHook>>) {
self.hooks = hooks;
}

pub(crate) fn set_type_registry(&mut self, type_registry: TypeRegistry) {
self.type_registry = type_registry;
}
Expand Down
40 changes: 39 additions & 1 deletion src/lib.rs
Expand Up @@ -5,7 +5,7 @@ use bevy::{
prelude::*,
reflect::{FromType, GetTypeRegistration, TypeRegistry, TypeRegistryInternal},
};
use ggrs::{Config, PlayerHandle};
use ggrs::{Config, Frame, PlayerHandle};
use ggrs_stage::GGRSStage;
use parking_lot::RwLock;
use reflect_resource::ReflectResource;
Expand Down Expand Up @@ -73,12 +73,41 @@ impl RollbackIdProvider {
}
}

/// An object that may hook into rollback events.
pub trait RollbackEventHook: Sync + Send + 'static {
/// Run before a snapshot is saved
fn pre_save(&mut self, frame: Frame, max_snapshots: usize, world: &mut World) {
let _ = (frame, max_snapshots, world);
}
/// Run after a snapshot is saved
fn post_save(&mut self, frame: Frame, max_snapshots: usize, world: &mut World) {
let _ = (frame, max_snapshots, world);
}
/// Run before a snapshot is loaded ( restored )
fn pre_load(&mut self, frame: Frame, max_snapshots: usize, world: &mut World) {
let _ = (frame, max_snapshots, world);
}
/// Run after a snapshot is loaded ( restored )
fn post_load(&mut self, frame: Frame, max_snapshots: usize, world: &mut World) {
let _ = (frame, max_snapshots, world);
}
/// Run before the game simulation is advanced one frame
fn pre_advance(&mut self, world: &mut World) {
let _ = world;
}
/// Run after the game simulation is advanced one frame
fn post_advance(&mut self, world: &mut World) {
let _ = world;
}
}

/// A builder to configure GGRS for a bevy app.
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,
hooks: Vec<Box<dyn RollbackEventHook>>,
}

impl<T: Config + Send + Sync> Default for GGRSPlugin<T> {
Expand All @@ -103,6 +132,7 @@ impl<T: Config + Send + Sync> Default for GGRSPlugin<T> {
})),
},
schedule: Default::default(),
hooks: Default::default(),
}
}
}
Expand Down Expand Up @@ -150,6 +180,13 @@ impl<T: Config + Send + Sync> GGRSPlugin<T> {
self
}

/// Add a [`RollbackEventHook`] object will have the opportunity to modify the world or take
/// other actions during snapshot saves, loads, and frame advances.q
pub fn add_rollback_hook<H: RollbackEventHook>(mut self, hook: H) -> Self {
self.hooks.push(Box::new(hook));
self
}

/// Consumes the builder and makes changes on the bevy app according to the settings.
pub fn build(self, app: &mut App) {
let mut input_system = self
Expand All @@ -161,6 +198,7 @@ impl<T: Config + Send + Sync> GGRSPlugin<T> {
stage.set_update_frequency(self.fps);
stage.set_schedule(self.schedule);
stage.set_type_registry(self.type_registry);
stage.set_hooks(self.hooks);
app.add_stage_before(CoreStage::Update, GGRS_UPDATE, stage);
// other resources
app.insert_resource(RollbackIdProvider::default());
Expand Down

0 comments on commit bfdfbc1

Please sign in to comment.