aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLibravatar Alexander Foremny <aforemny@posteo.de>2024-04-08 12:56:26 +0200
committerLibravatar Alexander Foremny <aforemny@posteo.de>2024-04-08 12:56:28 +0200
commit6471203be63bb5dd7716aa9841014f7e9105d452 (patch)
treecc48a7407c7842201c3fe6dbd5c98c03c641fd64
parent7643fb90dcf148605e3309c2007cd8a4c27914fa (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.nix20
-rw-r--r--src/client.rs78
-rw-r--r--src/protocol.rs10
-rw-r--r--src/server.rs70
4 files changed, 98 insertions, 80 deletions
diff --git a/shell.nix b/shell.nix
index c70c2eb..7e73840 100644
--- a/shell.nix
+++ b/shell.nix
@@ -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,