use bevy::ecs::message::MessageCursor;
use bevy::input::InputPlugin;
use bevy::prelude::*;
use leafwing_input_manager::action_diff::ActionDiffMessage;
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,
}
fn process_action_diffs<A: Actionlike>(
mut action_state_query: Query<&mut ActionState<A>>,
mut action_diff_messages: MessageReader<ActionDiffMessage<A>>,
) {
for action_diff_message in action_diff_messages.read() {
if action_diff_message.owner.is_some() {
let mut action_state = action_state_query.single_mut().unwrap();
action_diff_message
.action_diffs
.iter()
.for_each(|diff| action_state.apply_diff(diff));
}
}
}
fn main() {
let mut client_app = App::new();
client_app
.add_plugins(MinimalPlugins)
.add_plugins(InputPlugin)
.add_plugins(InputManagerPlugin::<FpsAction>::default())
.add_systems(PostUpdate, generate_action_diffs::<FpsAction>)
.add_message::<ActionDiffMessage<FpsAction>>()
.add_systems(Startup, spawn_player);
let mut server_app = App::new();
server_app
.add_plugins(MinimalPlugins)
.add_plugins(InputManagerPlugin::<FpsAction>::server())
.add_message::<ActionDiffMessage<FpsAction>>()
.add_systems(PreUpdate, process_action_diffs::<FpsAction>)
.add_systems(Startup, spawn_player);
client_app.update();
KeyCode::Space.press(client_app.world_mut());
MouseButton::Left.press(client_app.world_mut());
client_app.update();
let mut player_state_query = client_app.world_mut().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));
let message_reader =
send_messages::<ActionDiffMessage<FpsAction>>(&client_app, &mut server_app, None);
server_app.update();
let mut player_state_query = server_app.world_mut().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));
KeyCode::Space.release(client_app.world_mut());
MouseButton::Left.release(client_app.world_mut());
client_app.update();
let mut player_state_query = client_app.world_mut().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));
let _message_reader = send_messages::<ActionDiffMessage<FpsAction>>(
&client_app,
&mut server_app,
Some(message_reader),
);
server_app.update();
let mut player_state_query = server_app.world_mut().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::*;
let input_map = InputMap::new([(MoveLeft, KeyW), (MoveRight, KeyD), (Jump, Space)])
.with(Shoot, MouseButton::Left);
commands.spawn(input_map).insert(Player);
}
#[must_use]
fn send_messages<A: Send + Sync + 'static + Debug + Clone + Message>(
client_app: &App,
server_app: &mut App,
reader: Option<MessageCursor<A>>,
) -> MessageCursor<A> {
let client_messages: &Messages<A> = client_app.world().resource();
let mut server_messages: Mut<Messages<A>> = server_app.world_mut().resource_mut();
let mut reader = reader.unwrap_or_else(|| client_messages.get_cursor());
for client_message in reader.read(client_messages) {
dbg!(client_message.clone());
server_messages.write(client_message.clone());
}
reader
}