Skip to content

Commit

Permalink
format
Browse files Browse the repository at this point in the history
  • Loading branch information
cbournhonesque-sc committed Jan 4, 2024
1 parent a40080a commit c3314c4
Show file tree
Hide file tree
Showing 3 changed files with 181 additions and 194 deletions.
325 changes: 161 additions & 164 deletions examples/send_actions_over_network.rs
Original file line number Diff line number Diff line change
@@ -1,164 +1,161 @@
//! [`ActionDiff`] event streams are minimalistic representations
//! of the action state, intended for serialization and networking
//! While they are less convenient to work with than the complete [`ActionState`],
//! they are much smaller, and can be created from and reconstructed into [`ActionState`]
//!
//! Note that [`ActionState`] can also be serialized and sent directly.
//! This approach will be less bandwidth efficient, but involve less complexity and CPU work.
use bevy::ecs::event::{Events, ManualEventReader};
use bevy::input::InputPlugin;
use bevy::prelude::*;
use leafwing_input_manager::action_state::{ActionDiffEvent};
use leafwing_input_manager::prelude::*;
use leafwing_input_manager::systems::{generate_action_diffs};

use std::fmt::Debug;

#[derive(Actionlike, Clone, Copy, PartialEq, Eq, Hash, Debug, Reflect)]
enum FpsAction {
MoveLeft,
MoveRight,
Jump,
Shoot,
}

/// This identifier uniquely identifies entities across the network
#[derive(Component, Clone, PartialEq, Eq, Hash, Debug)]
struct StableId(u64);

/// Processes an [`Events`] stream of [`ActionDiff`] to update an [`ActionState`]
///
/// In a real scenario, you would have to map the entities between the server and client world.
/// In this case, we will just use the fact that there is only a single entity.
fn process_action_diffs<A: Actionlike>(
mut action_state_query: Query<&mut ActionState<A>>,
mut action_diff_events: EventReader<ActionDiffEvent<A>>,
) {
for action_diff_event in action_diff_events.read() {
if action_diff_event.owner.is_some() {
let mut action_state = action_state_query.get_single_mut().unwrap();
action_state.apply_diff(&action_diff_event.action_diff);
}
}
}

fn main() {
// In a real use case, these apps would be running on separate devices.
let mut client_app = App::new();

client_app
.add_plugins(MinimalPlugins)
.add_plugins(InputPlugin)
.add_plugins(InputManagerPlugin::<FpsAction>::default())
// Creates an event stream of `ActionDiffs` to send to the server
.add_systems(PostUpdate, generate_action_diffs::<FpsAction>)
.add_event::<ActionDiffEvent<FpsAction>>()
.add_systems(Startup, spawn_player);

let mut server_app = App::new();
server_app
.add_plugins(MinimalPlugins)
.add_plugins(InputManagerPlugin::<FpsAction>::server())
.add_event::<ActionDiffEvent<FpsAction>>()
// Reads in the event stream of `ActionDiffs` to update the `ActionState`
.add_systems(PreUpdate, process_action_diffs::<FpsAction>)
// Typically, the rest of this information would synchronized as well
.add_systems(Startup, spawn_player);

// Starting up the game
client_app.update();

// Sending inputs to the client
client_app.send_input(KeyCode::Space);
client_app.send_input(MouseButton::Left);

// These are converted into actions when the client_app's `Schedule` runs
client_app.update();

let mut player_state_query = client_app.world.query::<&ActionState<FpsAction>>();
let player_state = player_state_query.iter(&client_app.world).next().unwrap();
assert!(player_state.pressed(FpsAction::Jump));
assert!(player_state.pressed(FpsAction::Shoot));

// These events are transferred to the server
let event_reader =
send_events::<ActionDiffEvent<FpsAction>>(&client_app, &mut server_app, None);

// The server processes the event stream
server_app.update();

// And the actions are pressed on the server!
let mut player_state_query = server_app.world.query::<&ActionState<FpsAction>>();
let player_state = player_state_query.iter(&server_app.world).next().unwrap();
assert!(player_state.pressed(FpsAction::Jump));
assert!(player_state.pressed(FpsAction::Shoot));

// If we wait a tick, the buttons will be released
client_app.reset_inputs();
client_app.update();
let mut player_state_query = client_app.world.query::<&ActionState<FpsAction>>();
let player_state = player_state_query.iter(&client_app.world).next().unwrap();
assert!(player_state.released(FpsAction::Jump));
assert!(player_state.released(FpsAction::Shoot));

// Sending over the new `ActionDiff` event stream,
// we can see that the actions are now released on the server too
let _event_reader = send_events::<ActionDiffEvent<FpsAction>>(
&client_app,
&mut server_app,
Some(event_reader),
);

server_app.update();

let mut player_state_query = server_app.world.query::<&ActionState<FpsAction>>();
let player_state = player_state_query.iter(&server_app.world).next().unwrap();
assert!(player_state.released(FpsAction::Jump));
assert!(player_state.released(FpsAction::Shoot));
}

#[derive(Component)]
struct Player;

fn spawn_player(mut commands: Commands) {
use FpsAction::*;
use KeyCode::*;

commands
.spawn(InputManagerBundle {
input_map: InputMap::new([(W, MoveLeft), (D, MoveRight), (Space, Jump)])
.insert(MouseButton::Left, Shoot)
.build(),
..default()
})
.insert(Player);
}

/// A simple mock network interface that copies a set of events from the client to the server
///
/// The events are sent directly;
/// in real applications they would be serialized to a networking protocol instead.
///
/// The [`ManualEventReader`] returned must be reused in order to avoid double-sending events
#[must_use]
fn send_events<A: Send + Sync + 'static + Debug + Clone + Event>(
client_app: &App,
server_app: &mut App,
reader: Option<ManualEventReader<A>>,
) -> ManualEventReader<A> {
let client_events: &Events<A> = client_app.world.resource();
let mut server_events: Mut<Events<A>> = server_app.world.resource_mut();

// Get an event reader, one way or another
let mut reader = reader.unwrap_or_else(|| client_events.get_reader());

// Push the clients' events to the server
for client_event in reader.read(client_events) {
dbg!(client_event.clone());
server_events.send(client_event.clone());
}

// Return the event reader for reuse
reader
}
//! [`ActionDiff`] event streams are minimalistic representations
//! of the action state, intended for serialization and networking
//! While they are less convenient to work with than the complete [`ActionState`],
//! they are much smaller, and can be created from and reconstructed into [`ActionState`]
//!
//! Note that [`ActionState`] can also be serialized and sent directly.
//! This approach will be less bandwidth efficient, but involve less complexity and CPU work.
use bevy::ecs::event::{Events, ManualEventReader};
use bevy::input::InputPlugin;
use bevy::prelude::*;
use leafwing_input_manager::action_state::ActionDiffEvent;
use leafwing_input_manager::prelude::*;
use leafwing_input_manager::systems::generate_action_diffs;

use std::fmt::Debug;

#[derive(Actionlike, Clone, Copy, PartialEq, Eq, Hash, Debug, Reflect)]
enum FpsAction {
MoveLeft,
MoveRight,
Jump,
Shoot,
}

/// This identifier uniquely identifies entities across the network
#[derive(Component, Clone, PartialEq, Eq, Hash, Debug)]
struct StableId(u64);

/// Processes an [`Events`] stream of [`ActionDiff`] to update an [`ActionState`]
///
/// In a real scenario, you would have to map the entities between the server and client world.
/// In this case, we will just use the fact that there is only a single entity.
fn process_action_diffs<A: Actionlike>(
mut action_state_query: Query<&mut ActionState<A>>,
mut action_diff_events: EventReader<ActionDiffEvent<A>>,
) {
for action_diff_event in action_diff_events.read() {
if action_diff_event.owner.is_some() {
let mut action_state = action_state_query.get_single_mut().unwrap();
action_state.apply_diff(&action_diff_event.action_diff);
}
}
}

fn main() {
// In a real use case, these apps would be running on separate devices.
let mut client_app = App::new();

client_app
.add_plugins(MinimalPlugins)
.add_plugins(InputPlugin)
.add_plugins(InputManagerPlugin::<FpsAction>::default())
// Creates an event stream of `ActionDiffs` to send to the server
.add_systems(PostUpdate, generate_action_diffs::<FpsAction>)
.add_event::<ActionDiffEvent<FpsAction>>()
.add_systems(Startup, spawn_player);

let mut server_app = App::new();
server_app
.add_plugins(MinimalPlugins)
.add_plugins(InputManagerPlugin::<FpsAction>::server())
.add_event::<ActionDiffEvent<FpsAction>>()
// Reads in the event stream of `ActionDiffs` to update the `ActionState`
.add_systems(PreUpdate, process_action_diffs::<FpsAction>)
// Typically, the rest of this information would synchronized as well
.add_systems(Startup, spawn_player);

// Starting up the game
client_app.update();

// Sending inputs to the client
client_app.send_input(KeyCode::Space);
client_app.send_input(MouseButton::Left);

// These are converted into actions when the client_app's `Schedule` runs
client_app.update();

let mut player_state_query = client_app.world.query::<&ActionState<FpsAction>>();
let player_state = player_state_query.iter(&client_app.world).next().unwrap();
assert!(player_state.pressed(FpsAction::Jump));
assert!(player_state.pressed(FpsAction::Shoot));

// These events are transferred to the server
let event_reader =
send_events::<ActionDiffEvent<FpsAction>>(&client_app, &mut server_app, None);

// The server processes the event stream
server_app.update();

// And the actions are pressed on the server!
let mut player_state_query = server_app.world.query::<&ActionState<FpsAction>>();
let player_state = player_state_query.iter(&server_app.world).next().unwrap();
assert!(player_state.pressed(FpsAction::Jump));
assert!(player_state.pressed(FpsAction::Shoot));

// If we wait a tick, the buttons will be released
client_app.reset_inputs();
client_app.update();
let mut player_state_query = client_app.world.query::<&ActionState<FpsAction>>();
let player_state = player_state_query.iter(&client_app.world).next().unwrap();
assert!(player_state.released(FpsAction::Jump));
assert!(player_state.released(FpsAction::Shoot));

// Sending over the new `ActionDiff` event stream,
// we can see that the actions are now released on the server too
let _event_reader =
send_events::<ActionDiffEvent<FpsAction>>(&client_app, &mut server_app, Some(event_reader));

server_app.update();

let mut player_state_query = server_app.world.query::<&ActionState<FpsAction>>();
let player_state = player_state_query.iter(&server_app.world).next().unwrap();
assert!(player_state.released(FpsAction::Jump));
assert!(player_state.released(FpsAction::Shoot));
}

#[derive(Component)]
struct Player;

fn spawn_player(mut commands: Commands) {
use FpsAction::*;
use KeyCode::*;

commands
.spawn(InputManagerBundle {
input_map: InputMap::new([(W, MoveLeft), (D, MoveRight), (Space, Jump)])
.insert(MouseButton::Left, Shoot)
.build(),
..default()
})
.insert(Player);
}

/// A simple mock network interface that copies a set of events from the client to the server
///
/// The events are sent directly;
/// in real applications they would be serialized to a networking protocol instead.
///
/// The [`ManualEventReader`] returned must be reused in order to avoid double-sending events
#[must_use]
fn send_events<A: Send + Sync + 'static + Debug + Clone + Event>(
client_app: &App,
server_app: &mut App,
reader: Option<ManualEventReader<A>>,
) -> ManualEventReader<A> {
let client_events: &Events<A> = client_app.world.resource();
let mut server_events: Mut<Events<A>> = server_app.world.resource_mut();

// Get an event reader, one way or another
let mut reader = reader.unwrap_or_else(|| client_events.get_reader());

// Push the clients' events to the server
for client_event in reader.read(client_events) {
dbg!(client_event.clone());
server_events.send(client_event.clone());
}

// Return the event reader for reuse
reader
}
19 changes: 4 additions & 15 deletions src/action_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -507,31 +507,21 @@ impl<A: Actionlike> ActionState<A> {
/// This lets you reconstruct an [`ActionState`] from a stream of [`ActionDiff`]s
pub fn apply_diff(&mut self, action_diff: &ActionDiff<A>) {
match action_diff {
ActionDiff::Pressed {
action,
} => {
ActionDiff::Pressed { action } => {
self.press(action.clone());
self.action_data_mut(action.clone()).value = 1.;
}
ActionDiff::Released {
action,
} => {
ActionDiff::Released { action } => {
self.release(action.clone());
let action_data = self.action_data_mut(action.clone());
action_data.value = 0.;
action_data.axis_pair = None;
}
ActionDiff::ValueChanged {
action,
value,
} => {
ActionDiff::ValueChanged { action, value } => {
self.press(action.clone());
self.action_data_mut(action.clone()).value = *value;
}
ActionDiff::AxisPairChanged {
action,
axis_pair,
} => {
ActionDiff::AxisPairChanged { action, axis_pair } => {
self.press(action.clone());
let action_data = self.action_data_mut(action.clone());
action_data.axis_pair = Some(DualAxisData::from_xy(*axis_pair));
Expand Down Expand Up @@ -803,7 +793,6 @@ pub struct ActionDiffEvent<A: Actionlike> {
pub action_diff: ActionDiff<A>,
}


/// Stores presses and releases of buttons without timing information
///
/// These are typically accessed using the `Events<ActionDiffEvent>` resource.
Expand Down
Loading

0 comments on commit c3314c4

Please sign in to comment.