diff options
author | Alexander Foremny <aforemny@posteo.de> | 2024-04-08 12:56:26 +0200 |
---|---|---|
committer | Alexander Foremny <aforemny@posteo.de> | 2024-04-08 12:56:28 +0200 |
commit | 6471203be63bb5dd7716aa9841014f7e9105d452 (patch) | |
tree | cc48a7407c7842201c3fe6dbd5c98c03c641fd64 | |
parent | 7643fb90dcf148605e3309c2007cd8a4c27914fa (diff) |
fix: send inputs via messages instead of input
It appears as if lightyear inputs are supposed to be keys that are being
held down. This abstraction does not fit well with our inputs, which are
one-time actions. In particular, I've found inputs being missed.
We hope to improve upon this, by sending inputs as lightyear messages
via a reliable, oredered channel.
-rw-r--r-- | shell.nix | 20 | ||||
-rw-r--r-- | src/client.rs | 78 | ||||
-rw-r--r-- | src/protocol.rs | 10 | ||||
-rw-r--r-- | src/server.rs | 70 |
4 files changed, 98 insertions, 80 deletions
@@ -1,11 +1,29 @@ -{ pkgs ? import (import ./nix/sources.nix).nixpkgs { } }: +{ pkgs ? import (import ./nix/sources.nix).nixpkgs { + overlays = [ + (self: super: { + "format" = pkgs.writers.writeDashBin "format" '' + set -efu + cd ${self.lib.escapeShellArg (toString ./.)} + find src -iname '*.rs' -exec ${pkgs.rustfmt}/bin/rustfmt '{}' \; + ''; + "watch" = pkgs.writers.writeDashBin "watch" '' + set -efu + cd ${self.lib.escapeShellArg (toString ./.)} + find src -iname '*.rs' | entr -rs 'clear; cargo check' + ''; + }) + ]; + } +}: let inherit (pkgs) lib; in pkgs.mkShell rec { nativeBuildInputs = [ pkgs.cargo + pkgs.format pkgs.pkg-config pkgs.rustc pkgs.rustfmt + pkgs.watch ]; buildInputs = [ pkgs.alsa-lib diff --git a/src/client.rs b/src/client.rs index 007edc6..d111732 100644 --- a/src/client.rs +++ b/src/client.rs @@ -21,8 +21,6 @@ use bevy::prelude::*; use bevy::sprite::{MaterialMesh2dBundle, Mesh2dHandle}; use lightyear::client::connection::ConnectionManager; use lightyear::client::events::*; -use lightyear::client::input::InputManager; -use lightyear::client::input::InputSystemSet; use lightyear::prelude::client::ClientConnection; use lightyear::prelude::client::NetClient; use lightyear::prelude::*; @@ -128,12 +126,7 @@ impl Plugin for ClientPlugin { move_camera.after(control_camera).after(lock_camera), ), ) - .add_systems( - FixedPreUpdate, - buffer_input - .in_set(InputSystemSet::BufferInputs) - .before(choose_attack), - ) + .add_systems(PreUpdate, player_input.before(choose_attack)) .add_systems(Last, (gizmos_hover_indicator, gizmos_attack_indicator)); } } @@ -224,10 +217,10 @@ fn setup( }); client.connect().unwrap(); connection_manager - .send_message::<Channel1, SelectChampion>(SelectChampion(champion.0)) + .send_message::<Input, SelectChampion>(SelectChampion(champion.0)) .unwrap(); connection_manager - .send_message::<Channel1, SelectFaction>(SelectFaction(faction.0)) + .send_message::<Input, SelectFaction>(SelectFaction(faction.0)) .unwrap(); } @@ -316,17 +309,16 @@ fn move_players(mut players: Query<(&mut Transform, &PlayerPosition), Changed<Pl } } -fn buffer_input( +fn player_input( players: Query<(&PlayerId, &PlayerPosition, &Shape)>, mut attack: ResMut<Attack>, cameras: Query<(&Camera, &GlobalTransform)>, client_id: Res<MyClientId>, keyboard_input: Res<ButtonInput<KeyCode>>, mouse_input: Res<ButtonInput<MouseButton>>, - mut input_manager: ResMut<InputManager<Inputs>>, + mut connection_manager: ResMut<ConnectionManager<MyProtocol>>, windows: Query<&Window>, champion: Res<MyChampion>, - tick_manager: Res<TickManager>, ) { if mouse_input.just_pressed(MouseButton::Left) { match attack.0 { @@ -340,10 +332,12 @@ fn buffer_input( return; }; let direction = world_position - position.0; - input_manager.add_input( - Inputs::Imperative(Imperative::AttackDirection(ability_slot, direction)), - tick_manager.tick(), - ); + connection_manager + .send_message::<Input, Imperative>(Imperative::AttackDirection( + ability_slot, + direction, + )) + .unwrap(); attack.0 = None; } Ability::Targeted(_) => { @@ -356,10 +350,12 @@ fn buffer_input( ) else { return; }; - input_manager.add_input( - Inputs::Imperative(Imperative::AttackTarget(ability_slot, target_player)), - tick_manager.tick(), - ); + connection_manager + .send_message::<Input, Imperative>(Imperative::AttackTarget( + ability_slot, + target_player, + )) + .unwrap(); attack.0 = None; } }, @@ -371,16 +367,17 @@ fn buffer_input( &players, &windows, ) { - input_manager.add_input( - Inputs::Imperative(Imperative::AttackTarget(AbilitySlot::A, target_player)), - tick_manager.tick(), - ); + connection_manager + .send_message::<Input, Imperative>(Imperative::AttackTarget( + AbilitySlot::A, + target_player, + )) + .unwrap(); } else { if let Some(world_position) = cursor_world_position(&windows, &cameras) { - input_manager.add_input( - Inputs::Imperative(Imperative::WalkTo(world_position)), - tick_manager.tick(), - ); + connection_manager + .send_message::<Input, Imperative>(Imperative::WalkTo(world_position)) + .unwrap(); } } } @@ -389,18 +386,17 @@ fn buffer_input( match attack.0 { None => { if let Some(world_position) = cursor_world_position(&windows, &cameras) { - input_manager.add_input( - Inputs::Imperative(Imperative::WalkTo(world_position)), - tick_manager.tick(), - ); + connection_manager + .send_message::<Input, Imperative>(Imperative::WalkTo(world_position)) + .unwrap(); } } Some(_) => {} } } else if keyboard_input.just_pressed(KeyCode::KeyS) { - input_manager.add_input(Inputs::Imperative(Imperative::Idle), tick_manager.tick()); - } else { - input_manager.add_input(Inputs::None, tick_manager.tick()); + connection_manager + .send_message::<Input, Imperative>(Imperative::Idle) + .unwrap(); } } @@ -409,8 +405,7 @@ fn choose_attack( keyboard_input: Res<ButtonInput<KeyCode>>, mouse_input: Res<ButtonInput<MouseButton>>, mut attack: ResMut<Attack>, - mut input_manager: ResMut<InputManager<Inputs>>, - tick_manager: Res<TickManager>, + mut connection_manager: ResMut<ConnectionManager<MyProtocol>>, ) { if keyboard_input.just_pressed(KeyCode::KeyA) { attack.0 = Some(AbilitySlot::A); @@ -436,10 +431,9 @@ fn choose_attack( match attack.0 { Some(ability_slot) => match champion.0.ability(ability_slot) { Ability::Activated(_) => { - input_manager.add_input( - Inputs::Activation(Activation::Activate(ability_slot)), - tick_manager.tick(), - ); + connection_manager + .send_message::<Input, Activation>(Activation::Activate(ability_slot)) + .unwrap(); attack.0 = None; } _ => {} diff --git a/src/protocol.rs b/src/protocol.rs index f4c77bb..f75cfab 100644 --- a/src/protocol.rs +++ b/src/protocol.rs @@ -19,8 +19,6 @@ use serde::Serialize; #[derive(Serialize, Deserialize, Debug, PartialEq, Clone)] pub enum Inputs { - Imperative(Imperative), - Activation(Activation), None, } impl UserAction for Inputs {} @@ -39,6 +37,8 @@ pub enum Messages { SelectChampion(SelectChampion), SelectFaction(SelectFaction), HealthChanged(HealthChanged), + Imperative(Imperative), + Activation(Activation), } #[component_protocol(protocol = "MyProtocol")] @@ -60,7 +60,7 @@ pub enum Components { } #[derive(Channel)] -pub struct Channel1; +pub struct Input; protocolize! { Self = MyProtocol, @@ -71,8 +71,8 @@ protocolize! { pub fn protocol() -> MyProtocol { let mut protocol = MyProtocol::default(); - protocol.add_channel::<Channel1>(ChannelSettings { - mode: ChannelMode::UnorderedReliable(Default::default()), + protocol.add_channel::<Input>(ChannelSettings { + mode: ChannelMode::OrderedReliable(Default::default()), ..Default::default() }); protocol diff --git a/src/server.rs b/src/server.rs index a509f28..b6c23c6 100644 --- a/src/server.rs +++ b/src/server.rs @@ -68,6 +68,10 @@ impl Plugin for ServerPlugin { receive_message_select_faction, ), ) + .add_systems( + Update, + (receive_message_imperative, receive_message_activation), + ) .add_systems(FixedUpdate, timers_tick) .add_systems(FixedUpdate, effective_stats.after(buffs_despawn)) .add_systems(FixedUpdate, health_regen.after(timers_tick)) @@ -80,7 +84,8 @@ impl Plugin for ServerPlugin { activation.after(cooldown_decrement), imperative_walk_to, ) - .after(player_input), + .after(receive_message_activation) + .after(receive_message_imperative), ) .add_systems( FixedUpdate, @@ -101,7 +106,6 @@ impl Plugin for ServerPlugin { ) .add_systems(FixedUpdate, cooldown_decrement) .add_systems(FixedUpdate, (buffs_despawn, buffs_tick).chain()) - .add_systems(FixedUpdate, player_input) .add_systems( FixedUpdate, (tower_ai, (minion_ai_attack, minion_ai_walk).chain()) @@ -210,6 +214,36 @@ fn receive_message_select_faction( } } +fn receive_message_imperative( + entity_map: Res<EntityMap>, + mut commands: Commands, + mut reader: EventReader<MessageEvent<Imperative>>, +) { + for event in reader.read() { + let client_id = event.context(); + let imperative = event.message(); + let Some(entity) = entity_map.0.get(client_id) else { + continue; + }; + commands.entity(*entity).insert(*imperative); + } +} + +fn receive_message_activation( + entity_map: Res<EntityMap>, + mut commands: Commands, + mut reader: EventReader<MessageEvent<Activation>>, +) { + for event in reader.read() { + let client_id = event.context(); + let activation = event.message(); + let Some(entity) = entity_map.0.get(client_id) else { + continue; + }; + commands.entity(*entity).insert(*activation); + } +} + fn disconnects( mut commands: Commands, mut disconnects: EventReader<server::DisconnectEvent>, @@ -223,34 +257,6 @@ fn disconnects( } } -fn player_input( - entity_map: Res<EntityMap>, - mut input_reader: EventReader<server::InputEvent<Inputs>>, - mut imperatives: Query<&mut Imperative>, - mut activations: Query<&mut Activation>, -) { - for input in input_reader.read() { - let client_id = input.context(); - if let Some(input) = input.input() { - if let Some(entity_id) = entity_map.0.get(client_id) { - match input { - Inputs::Imperative(new_imperative) => { - if let Ok(mut imperative) = imperatives.get_mut(*entity_id) { - *imperative = *new_imperative; - } - } - Inputs::Activation(new_activation) => { - if let Ok(mut activation) = activations.get_mut(*entity_id) { - *activation = *new_activation; - } - } - Inputs::None => {} - } - } - } - } -} - fn imperative_walk_to( mut players: Query<(Entity, &mut Imperative, &EffectiveStats)>, mut positions: Query<&mut PlayerPosition>, @@ -615,7 +621,7 @@ fn projectile_despawn( }; let applied_damage = damage.apply(hit_effective_stats.0); hit_health.apply_damage(applied_damage); - let _ = connection_manager.send_message_to_target::<Channel1, HealthChanged>( + let _ = connection_manager.send_message_to_target::<Input, HealthChanged>( HealthChanged(HealthEvent { target_player: *hit_player_id, health_gained: -applied_damage, @@ -647,7 +653,7 @@ fn health_regen( if health_regen_timer.0.just_finished() { for (target_player, mut health, effective_stats) in healths.iter_mut() { health.heal(HEALTH_REGEN, effective_stats.0.max_health); - let _ = connection_manager.send_message_to_target::<Channel1, HealthChanged>( + let _ = connection_manager.send_message_to_target::<Input, HealthChanged>( HealthChanged(HealthEvent { target_player: *target_player, health_gained: HEALTH_REGEN, |