Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion framework_crates/bones_framework/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ once_cell = "1.17"
thiserror = "1.0"
gilrs = "0.11.0"
send_wrapper = "0.6.0"
numquant = "0.2"


# Tracing
Expand Down Expand Up @@ -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"
Expand Down
97 changes: 73 additions & 24 deletions framework_crates/bones_framework/src/input.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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.
Expand All @@ -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.
Expand All @@ -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<Self::ControlSource>;
/// Automatic implementation for `DenseInput`.
impl<T> 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<Self::Dense>;

// 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<Dense: DenseInput>: 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<Dense>,
{
/// 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<Dense>,
T: InputCollector<'a, Control>,
{
fn get_dense_control(&self) -> Dense {
self.get_control().get_dense_input()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use numquant::{IntRange, Quantized};
use crate::prelude::*;

/// A newtype around [`Vec2`] that implements [`From<u16>`] and [`Into<u16>`] 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);

Expand Down
45 changes: 13 additions & 32 deletions framework_crates/bones_framework/src/networking.rs
Original file line number Diff line number Diff line change
@@ -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::*;
Expand All @@ -20,16 +18,13 @@ use {
ggrs::{NetworkStats, PlayerHandle},
};

use crate::input::PlayerControls as PlayerControlsTrait;

pub use iroh;

pub mod input;
pub mod lan;
pub mod online;
pub mod online_lobby;
pub mod online_matchmaking;
pub mod proto;
pub mod random;
pub mod socket;

Expand Down Expand Up @@ -64,7 +59,7 @@ impl From<ggrs::InputStatus> 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")]
Expand Down Expand Up @@ -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,

Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -647,7 +642,7 @@ where

impl<InputTypes> 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;
Expand All @@ -658,26 +653,12 @@ where

let mut skip_frames: u32 = 0;

{
let player_inputs = world.resource::<InputTypes::PlayerControls>();

// 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
Expand Down Expand Up @@ -862,7 +843,7 @@ where

// update game controls from ggrs inputs
let mut player_inputs =
world.resource_mut::<InputTypes::PlayerControls>();
world.resource_mut::<InputTypes::Controls>();
for (player_idx, (input, status)) in
network_inputs.into_iter().enumerate()
{
Expand Down
93 changes: 7 additions & 86 deletions framework_crates/bones_framework/src/networking/input.rs
Original file line number Diff line number Diff line change
@@ -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<Self::Dense>;

// 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,
<Self::PlayerControls as PlayerControls<'a, Self::Control>>::ControlMapping,
<Self::PlayerControls as PlayerControls<'a, Self::Control>>::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.
Expand All @@ -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<Dense>,
T: PlayerControls<'a, Control>,
Control: DenseControl<Dense>,
T: Controls<'a, Control>,
{
// type NetworkControl = PlayerControl;
// type NetworkControl = Control;
fn network_update(
&mut self,
player_idx: usize,
Expand All @@ -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<T> 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<Dense: DenseInput>: 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<Dense>,
{
/// 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<Dense>,
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()
}
}