From 80cac4083b334e8397cef467e49171716736c2d8 Mon Sep 17 00:00:00 2001 From: Martin Indra Date: Tue, 19 Sep 2023 21:32:12 +0200 Subject: [PATCH] Multiplayer: impl transform syncing (#736) --- Cargo.lock | 3 + crates/movement/Cargo.toml | 7 +- crates/movement/src/lib.rs | 3 + crates/movement/src/syncing.rs | 112 ++++++++++++++++++++++++++++ crates/multiplayer/src/lib.rs | 2 +- crates/multiplayer/src/playermsg.rs | 34 ++++++++- 6 files changed, 156 insertions(+), 5 deletions(-) create mode 100644 crates/movement/src/syncing.rs diff --git a/Cargo.lock b/Cargo.lock index 9fc421c40..5f37820be 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2537,9 +2537,12 @@ dependencies = [ "de_core", "de_index", "de_map", + "de_messages", + "de_multiplayer", "de_objects", "de_pathing", "de_types", + "fastrand 1.9.0", "glam", "parry2d", "parry3d", diff --git a/crates/movement/Cargo.toml b/crates/movement/Cargo.toml index bdac1f27b..b2b180972 100644 --- a/crates/movement/Cargo.toml +++ b/crates/movement/Cargo.toml @@ -16,12 +16,15 @@ categories.workspace = true de_core.workspace = true de_index.workspace = true de_map.workspace = true +de_messages.workspace = true +de_multiplayer.workspace = true de_objects.workspace = true de_pathing.workspace = true de_types.workspace = true # Other bevy.workspace = true -parry3d.workspace = true -parry2d.workspace = true +fastrand.workspace = true glam.workspace = true +parry2d.workspace = true +parry3d.workspace = true diff --git a/crates/movement/src/lib.rs b/crates/movement/src/lib.rs index 67e5a5a80..559b58f5a 100644 --- a/crates/movement/src/lib.rs +++ b/crates/movement/src/lib.rs @@ -6,6 +6,7 @@ mod movement; mod obstacles; mod pathing; mod repulsion; +mod syncing; use std::f32::consts::PI; @@ -16,6 +17,7 @@ use movement::MovementPlugin; use obstacles::ObstaclesPlugin; use pathing::PathingPlugin; use repulsion::RepulsionPlugin; +use syncing::SyncingPlugin; /// Maximum object horizontal speed in meters per second. const MAX_H_SPEED: f32 = 10.; @@ -43,5 +45,6 @@ impl PluginGroup for MovementPluginGroup { .add(RepulsionPlugin) .add(KinematicsPlugin) .add(AltitudePlugin) + .add(SyncingPlugin) } } diff --git a/crates/movement/src/syncing.rs b/crates/movement/src/syncing.rs new file mode 100644 index 000000000..b85d4cc30 --- /dev/null +++ b/crates/movement/src/syncing.rs @@ -0,0 +1,112 @@ +use std::time::Duration; + +use bevy::prelude::*; +use de_core::{ + gamestate::GameState, + gconfig::GameConfig, + objects::{Local, MovableSolid}, + schedule::{Movement, PreMovement}, + state::AppState, +}; +use de_messages::ToPlayers; +use de_multiplayer::{NetEntities, NetRecvTransformEvent, ToPlayersEvent}; + +use crate::movement::MovementSet; + +const MIN_SYNC_PERIOD: Duration = Duration::from_secs(2); +const SYNC_RANDOMIZATION_MS: u64 = 2_000; + +pub(crate) struct SyncingPlugin; + +impl Plugin for SyncingPlugin { + fn build(&self, app: &mut App) { + app.add_systems( + PreMovement, + setup_entities + .run_if(is_multiplayer) + .run_if(in_state(AppState::InGame)), + ) + .add_systems( + Movement, + ( + receive_transforms + .run_if(on_event::()) + .after(MovementSet::UpdateTransform), + send_transforms + .run_if(is_multiplayer) + .after(MovementSet::UpdateTransform), + ) + .run_if(in_state(GameState::Playing)), + ); + } +} + +#[derive(Component)] +struct SyncTimer(Duration); + +impl SyncTimer { + fn schedule(time: Duration) -> Duration { + let jitter = Duration::from_millis(fastrand::u64(0..SYNC_RANDOMIZATION_MS)); + time + MIN_SYNC_PERIOD + jitter + } + + fn new(time: Duration) -> Self { + Self(Self::schedule(time)) + } + + /// Sets sync expiration to the future relative to the current time. + fn refresh(&mut self, time: Duration) { + self.0 = Self::schedule(time); + } + + /// Returns true if transform sync is already due. + fn outdated(&self, time: Duration) -> bool { + time >= self.0 + } +} + +type NotSetUp = (With, With, Without); + +fn is_multiplayer(config: Option>) -> bool { + match config { + Some(config) => config.multiplayer(), + None => false, + } +} + +fn setup_entities(mut commands: Commands, time: Res