diff --git a/framework_crates/bones_framework/Cargo.toml b/framework_crates/bones_framework/Cargo.toml index 7c266dd720..0cba3411c4 100644 --- a/framework_crates/bones_framework/Cargo.toml +++ b/framework_crates/bones_framework/Cargo.toml @@ -103,6 +103,7 @@ once_cell = "1.17" thiserror = "1.0" gilrs = "0.11.0" send_wrapper = "0.6.0" +numquant = "0.2" # Tracing @@ -147,7 +148,6 @@ ggrs = { git = "https://github.com/MaxCWhitehead/ggrs.git", rev = "96499377407ce bones_matchmaker_proto = { version = "0.4.0", path = "../../other_crates/bones_matchmaker_proto" } bytes = "1.4" mdns-sd = { version = "0.10", default-features = false } -numquant = "0.2" ping-rs = "0.1" postcard = { version = "1.0", features = ["alloc"] } rcgen = "0.12" diff --git a/framework_crates/bones_framework/src/input.rs b/framework_crates/bones_framework/src/input.rs index 2891298155..d87a74837f 100644 --- a/framework_crates/bones_framework/src/input.rs +++ b/framework_crates/bones_framework/src/input.rs @@ -7,6 +7,7 @@ pub mod gamepad; pub mod gilrs; pub mod keyboard; pub mod mouse; +pub mod proto; pub mod window; /// The state of a button, ether pressed or released. @@ -29,8 +30,10 @@ impl ButtonState { /// Module prelude. pub mod prelude { - pub use super::{gamepad::*, keyboard::*, mouse::*, window::*, ButtonState}; - pub use crate::input::{InputCollector, PlayerControls}; + pub use super::{gamepad::*, keyboard::*, mouse::*, proto, window::*, ButtonState}; + pub use crate::input::{ + Controls, DenseControl, DenseInput, DenseInputCollector, DenseInputConfig, InputCollector, + }; } /// Maps raw inputs to game controls and exposes controls for respective player and their control source. @@ -40,11 +43,7 @@ pub mod prelude { /// [`InputCollector::update_just_pressed`] computes any changes in pressed buttons that may be stored on control. /// /// [`InputCollector::advance_frame`] is used to mark that the input has been consumed, and update the prev frame inputs to current, to compute changes next frame. -/// -/// Generic type param ControlMapping is HasSchema because it is expected to be a Resource retrievable on world. -pub trait InputCollector<'a, ControlMapping: HasSchema, ControlSource, Control>: - Send + Sync -{ +pub trait InputCollector<'a, Control>: Send + Sync { /// Update the internal state with new inputs. This must be called every render frame with the /// input events. This updates which buttons are pressed, but does not compute what buttons were "just_pressed". /// use [`InputCollector::update_just_pressed`] to do this. @@ -62,30 +61,80 @@ pub trait InputCollector<'a, ControlMapping: HasSchema, ControlSource, Control>: /// This does not modify previous frame's input, to do this use [`InputCollector::advance_frame`]. fn update_just_pressed(&mut self); - /// Get control for player based on provided `ControlSource`. - fn get_control(&self, player_idx: usize, control_source: ControlSource) -> &Control; + /// Get control for player. + fn get_control(&self) -> &Control; } /// Trait that tracks player control state. Provides associated types for other input trait implementations. -pub trait PlayerControls<'a, Control> { - /// InputCollector used to update controls. - type InputCollector: InputCollector<'a, Self::ControlMapping, Self::ControlSource, Control>; +pub trait Controls<'a, Control> { + /// Get control for player. + fn get_control(&self, player_idx: usize) -> &Control; - /// Control mapping from raw input, expected to be able to be retrieved as `Resource` from world. - type ControlMapping: HasSchema; + /// Get mutable control for player. + fn get_control_mut(&mut self, player_idx: usize) -> &mut Control; +} - /// Type used to map source of input to control. - type ControlSource; +use std::fmt::Debug; - /// Update control state from input collector. - fn update_controls(&mut self, collector: &mut Self::InputCollector); +/// Dense input for network replication. +pub trait DenseInput: + bytemuck::Pod + bytemuck::Zeroable + Copy + Clone + PartialEq + Eq + Send + Sync +{ +} - /// Get `ControlSource` for player (only present for local player). - fn get_control_source(&self, local_player_idx: usize) -> Option; +/// Automatic implementation for `DenseInput`. +impl DenseInput for T where + T: bytemuck::Pod + bytemuck::Zeroable + Copy + Clone + PartialEq + Eq + Send + Sync +{ +} - /// Get control for player. - fn get_control(&self, player_idx: usize) -> &Control; +/// Define input types used by game for use in networking. +/// +/// As long as types `Controls` and `InputCollector` implement traits [`Controls`] and [`InputCollector`], +/// trait bounds [`DenseControl`] and [`DenseInputCollector`] are automatically implemented. +#[allow(missing_docs)] +pub trait DenseInputConfig<'a> { + type Dense: DenseInput + Debug + Default; + type Control: DenseControl; + + // Must be HasSchema because expected to be retrieved from `World` as `Resource`. + type Controls: Controls<'a, Self::Control> + HasSchema; + + // InputCollector type params must match that of Controls, so using associated types. + type InputCollector: InputCollector<'a, Self::Control> + Default; +} - /// Get mutable control for player. - fn get_control_mut(&mut self, player_idx: usize) -> &mut Control; +/// Trait allowing for creating and applying [`DenseInput`] from control. +pub trait DenseControl: Send + Sync + Default { + /// Get [`DenseInput`] for control. + fn get_dense_input(&self) -> Dense; + + /// Update control from [`DenseInput`]. + fn update_from_dense(&mut self, new_control: &Dense); +} + +/// Extension of [`InputCollector`] exposing dense control for networking. +/// +/// This trait is automatically implemented for [`InputCollector`]'s such that `Control` +/// implements [`DenseControl`] (i.e. implements dense input) +pub trait DenseInputCollector<'a, Dense, Control>: InputCollector<'a, Control> +where + Dense: DenseInput, + Control: DenseControl, +{ + /// Get dense control + fn get_dense_control(&self) -> Dense; +} + +/// Provide automatic [`DenseInputCollector`] for [`InputCollector`] when type parameters +/// meet required bounds for networking. +impl<'a, T, Dense, Control> DenseInputCollector<'a, Dense, Control> for T +where + Dense: DenseInput, + Control: DenseControl, + T: InputCollector<'a, Control>, +{ + fn get_dense_control(&self) -> Dense { + self.get_control().get_dense_input() + } } diff --git a/framework_crates/bones_framework/src/networking/proto.rs b/framework_crates/bones_framework/src/input/proto.rs similarity index 97% rename from framework_crates/bones_framework/src/networking/proto.rs rename to framework_crates/bones_framework/src/input/proto.rs index 126b052c6a..842ad72cb2 100644 --- a/framework_crates/bones_framework/src/networking/proto.rs +++ b/framework_crates/bones_framework/src/input/proto.rs @@ -6,7 +6,7 @@ use numquant::{IntRange, Quantized}; use crate::prelude::*; /// A newtype around [`Vec2`] that implements [`From`] and [`Into`] as a way to compress -/// user stick input for use in [`self::input::DenseInput`]. +/// user stick input for use in [`crate::input::DenseInput`]. #[derive(Debug, Deref, DerefMut, Default)] pub struct DenseMoveDirection(pub Vec2); diff --git a/framework_crates/bones_framework/src/networking.rs b/framework_crates/bones_framework/src/networking.rs index 4e71fc3c2c..b7953687b1 100644 --- a/framework_crates/bones_framework/src/networking.rs +++ b/framework_crates/bones_framework/src/networking.rs @@ -1,9 +1,7 @@ #![doc = include_str!("./networking.md")] -use self::{ - input::{DenseInput, NetworkInputConfig, NetworkPlayerControl, NetworkPlayerControls}, - socket::Socket, -}; +use self::{input::NetworkControls, socket::Socket}; +use crate::input::{DenseControl, DenseInput, DenseInputConfig}; use crate::networking::online::OnlineMatchmakerResponse; pub use crate::networking::random::RngGenerator; use crate::prelude::*; @@ -20,8 +18,6 @@ use { ggrs::{NetworkStats, PlayerHandle}, }; -use crate::input::PlayerControls as PlayerControlsTrait; - pub use iroh; pub mod input; @@ -29,7 +25,6 @@ pub mod lan; pub mod online; pub mod online_lobby; pub mod online_matchmaking; -pub mod proto; pub mod random; pub mod socket; @@ -64,7 +59,7 @@ impl From for NetworkInputStatus { /// Module prelude. pub mod prelude { pub use super::{ - input, lan, online, proto, random, DisconnectedPlayers, RngGenerator, SyncingInfo, RUNTIME, + input, lan, online, random, DisconnectedPlayers, RngGenerator, SyncingInfo, RUNTIME, }; #[cfg(feature = "net-debug")] @@ -449,7 +444,7 @@ pub struct DisconnectedPlayers { /// [`SessionRunner`] implementation that uses [`ggrs`] for network play. /// /// This is where the whole `ggrs` integration is implemented. -pub struct GgrsSessionRunner<'a, InputTypes: NetworkInputConfig<'a>> { +pub struct GgrsSessionRunner<'a, InputTypes: DenseInputConfig<'a>> { /// The last player input we detected. pub last_player_input: InputTypes::Dense, @@ -540,7 +535,7 @@ impl GgrsSessionRunnerInfo { impl<'a, InputTypes> GgrsSessionRunner<'a, InputTypes> where - InputTypes: NetworkInputConfig<'a>, + InputTypes: DenseInputConfig<'a>, { /// Creates a new session runner from a `OnlineMatchmakerResponse::GameStarting` /// Any input values set as `None` will be set to default. @@ -647,7 +642,7 @@ where impl SessionRunner for GgrsSessionRunner<'static, InputTypes> where - InputTypes: NetworkInputConfig<'static> + 'static, + InputTypes: DenseInputConfig<'static> + 'static, { fn step(&mut self, frame_start: Instant, world: &mut World, stages: &mut SystemStages) { let step: f64 = 1.0 / self.network_fps; @@ -658,26 +653,12 @@ where let mut skip_frames: u32 = 0; - { - let player_inputs = world.resource::(); - - // Collect inputs and update controls - self.input_collector.apply_inputs(world); - self.input_collector.update_just_pressed(); - - // save local players dense input for use with ggrs - match player_inputs.get_control_source(self.local_player_idx as usize) { - Some(control_source) => { - let control = self - .input_collector - .get_control(self.local_player_idx as usize, control_source); - - self.last_player_input = control.get_dense_input(); - }, - None => warn!("GgrsSessionRunner local_player_idx {} has no control source, no local input provided.", - self.local_player_idx) - }; - } + // Collect inputs and update controls + self.input_collector.apply_inputs(world); + self.input_collector.update_just_pressed(); + + // save local players dense input for use with ggrs + self.last_player_input = self.input_collector.get_control().get_dense_input(); #[cfg(feature = "net-debug")] // Current frame before we start network update loop @@ -862,7 +843,7 @@ where // update game controls from ggrs inputs let mut player_inputs = - world.resource_mut::(); + world.resource_mut::(); for (player_idx, (input, status)) in network_inputs.into_iter().enumerate() { diff --git a/framework_crates/bones_framework/src/networking/input.rs b/framework_crates/bones_framework/src/networking/input.rs index a4a737733f..52ff4eb826 100644 --- a/framework_crates/bones_framework/src/networking/input.rs +++ b/framework_crates/bones_framework/src/networking/input.rs @@ -1,39 +1,12 @@ //! Input traits required by networking. These traits are networking specific, either only used in networking, //! or extending other traits from [`crate::input`] for networking. -use std::fmt::Debug; - -use bones_schema::HasSchema; - -use crate::input::{InputCollector, PlayerControls}; +use crate::input::{Controls, DenseControl, DenseInput}; use super::NetworkInputStatus; -/// Define input types used by game for use in networking. -/// -/// As long as types `PlayerControls` and `InputCollector` implement traits [`PlayerControls`] and [`InputCollector`], -/// trait bounds [`NetworkPlayerControl`] and [`NetworkInputCollector`] are automatically implemented. -#[allow(missing_docs)] -pub trait NetworkInputConfig<'a> { - type Dense: DenseInput + Debug + Default; - type Control: NetworkPlayerControl; - - // Must be HasSchema because expected to be retrieved from `World` as `Resource`. - type PlayerControls: PlayerControls<'a, Self::Control> + HasSchema; - - // InputCollector type params must match that of PlayerControls, so using associated types. - type InputCollector: InputCollector< - 'a, - >::ControlMapping, - >::ControlSource, - Self::Control, - > + Default; -} - -/// Required for use of [`PlayerControls`] in networking. -pub trait NetworkPlayerControls<'a, Dense: DenseInput, Control>: - PlayerControls<'a, Control> -{ +/// Required for use of [`Controls`] in networking. +pub trait NetworkControls<'a, Dense: DenseInput, Control>: Controls<'a, Control> { /// Update control of player from dense input. /// /// [`NetworkInputStatus`] communicates if input is confirmed, predicted, or from disconnected player. @@ -48,13 +21,13 @@ pub trait NetworkPlayerControls<'a, Dense: DenseInput, Control>: fn get_dense_control(&self, player_idx: usize) -> Dense; } -impl<'a, T, Dense, Control> NetworkPlayerControls<'a, Dense, Control> for T +impl<'a, T, Dense, Control> NetworkControls<'a, Dense, Control> for T where Dense: DenseInput, - Control: NetworkPlayerControl, - T: PlayerControls<'a, Control>, + Control: DenseControl, + T: Controls<'a, Control>, { - // type NetworkControl = PlayerControl; + // type NetworkControl = Control; fn network_update( &mut self, player_idx: usize, @@ -69,55 +42,3 @@ where self.get_control(player_idx).get_dense_input() } } - -/// Dense input for network replication. -pub trait DenseInput: - bytemuck::Pod + bytemuck::Zeroable + Copy + Clone + PartialEq + Eq + Send + Sync -{ -} - -/// Automatic implementation for `DenseInput`. -impl DenseInput for T where - T: bytemuck::Pod + bytemuck::Zeroable + Copy + Clone + PartialEq + Eq + Send + Sync -{ -} - -/// Trait allowing for creating and applying [`DenseInput`] from control. -pub trait NetworkPlayerControl: Send + Sync + Default { - /// Get [`DenseInput`] for control. - fn get_dense_input(&self) -> Dense; - - /// Update control from [`DenseInput`]. - fn update_from_dense(&mut self, new_control: &Dense); -} - -/// Extension of [`InputCollector`] exposing dense control for networking. -/// -/// This trait is automatically implemented for [`InputCollector`]'s such that `Control` -/// implements [`NetworkPlayerControl`] (i.e. implements dense input) -pub trait NetworkInputCollector<'a, Dense, ControlMapping, ControlSource, Control>: - InputCollector<'a, ControlMapping, ControlSource, Control> -where - Dense: DenseInput, - ControlMapping: HasSchema, - Control: NetworkPlayerControl, -{ - /// Get dense control - fn get_dense_control(&self, player_idx: usize, control_soure: ControlSource) -> Dense; -} - -/// Provide automatic [`NetworkInputCollector`] for [`InputCollector`] when type parameters -/// meet required bounds for networking. -impl<'a, T, Dense, ControlMapping, ControlSource, Control> - NetworkInputCollector<'a, Dense, ControlMapping, ControlSource, Control> for T -where - Dense: DenseInput, - Control: NetworkPlayerControl, - ControlMapping: HasSchema, - T: InputCollector<'a, ControlMapping, ControlSource, Control>, -{ - fn get_dense_control(&self, player_idx: usize, control_source: ControlSource) -> Dense { - self.get_control(player_idx, control_source) - .get_dense_input() - } -}