diff options
-rw-r--r-- | Cargo.lock | 117 | ||||
-rw-r--r-- | Cargo.toml | 7 | ||||
-rw-r--r-- | README.md | 14 | ||||
-rw-r--r-- | agame.nix | 31 | ||||
-rw-r--r-- | default.nix | 5 | ||||
-rw-r--r-- | src/client.rs | 73 | ||||
-rw-r--r-- | src/client/network.rs | 7 | ||||
-rw-r--r-- | src/main.rs | 51 | ||||
-rw-r--r-- | src/shared.rs | 1 |
9 files changed, 264 insertions, 42 deletions
@@ -102,6 +102,7 @@ name = "agame" version = "0.1.0" dependencies = [ "bevy", + "clap", "crossbeam-channel", "lightyear", "rand", @@ -206,6 +207,54 @@ dependencies = [ ] [[package]] +name = "anstream" +version = "0.6.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d96bd03f33fe50a863e394ee9718a706f988b9079b20c3784fb726e7678b62fb" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc" + +[[package]] +name = "anstyle-parse" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7" +dependencies = [ + "anstyle", + "windows-sys 0.52.0", +] + +[[package]] name = "anyhow" version = "1.0.81" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1419,6 +1468,46 @@ dependencies = [ ] [[package]] +name = "clap" +version = "4.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b230ab84b0ffdf890d5a10abdbc8b83ae1c4918275daea1ab8801f71536b2651" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim 0.11.0", +] + +[[package]] +name = "clap_derive" +version = "4.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "307bc0538d5f0f83b8248db3087aa92fe504e4691294d0c96c0eabc33f47ba47" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 2.0.52", +] + +[[package]] +name = "clap_lex" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" + +[[package]] name = "codespan-reporting" version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1435,6 +1524,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" [[package]] +name = "colorchoice" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" + +[[package]] name = "com" version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1697,7 +1792,7 @@ dependencies = [ "ident_case", "proc-macro2", "quote", - "strsim", + "strsim 0.10.0", "syn 2.0.52", ] @@ -2169,7 +2264,7 @@ dependencies = [ "vec_map", "wasm-bindgen", "web-sys", - "windows 0.54.0", + "windows 0.48.0", ] [[package]] @@ -2388,6 +2483,12 @@ dependencies = [ ] [[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] name = "hexasphere" version = "10.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -3739,6 +3840,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" [[package]] +name = "strsim" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ee073c9e4cd00e28217186dbe12796d692868f432bf2e97ee73bed0c56dfa01" + +[[package]] name = "subtle" version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -4019,6 +4126,12 @@ dependencies = [ ] [[package]] +name = "utf8parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" + +[[package]] name = "uuid" version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -4,12 +4,17 @@ version = "0.1.0" edition = "2021" [dependencies] -bevy = { version = "0.13.0", features = ["dynamic_linking"] } +bevy = { version = "0.13.0" } +clap = { version = "4.5.2", features = ["derive"] } crossbeam-channel = { version = "0.5.12" } lightyear = { version = "0.12.0" } rand = { version = "0.8.5" } serde = { version = "1.0.197" } +[features] +default = ["fast-compile"] +fast-compile = ["bevy/dynamic_linking"] + [profile.dev] opt-level = 1 @@ -6,6 +6,18 @@ cargo run ``` +## run client (multi player) + +```console +cargo run -- --connect-to 49.13.201.137:16384 +``` + +## run server (headless) + +```console +cargo run -- --server +``` + ## controls -- **left mouse** teleport +- **left mouse** walk / attack diff --git a/agame.nix b/agame.nix new file mode 100644 index 0000000..993d768 --- /dev/null +++ b/agame.nix @@ -0,0 +1,31 @@ +{ alsa-lib +, lib +, libxkbcommon +, pkg-config +, rustPlatform +, udev +, vulkan-loader +, wayland +, xorg +}: +rustPlatform.buildRustPackage rec { + name = "agame"; + src = ./.; + cargoLock.lockFile = ./Cargo.lock; + buildNoDefaultFeatures = true; + nativeBuildInputs = [ + pkg-config + ]; + buildInputs = [ + alsa-lib + libxkbcommon + udev + vulkan-loader + wayland + xorg.libX11 + xorg.libXcursor + xorg.libXi + xorg.libXrandr + ]; + doCheck = false; +} diff --git a/default.nix b/default.nix new file mode 100644 index 0000000..118527b --- /dev/null +++ b/default.nix @@ -0,0 +1,5 @@ +{ pkgs ? import (import ./nix/sources.nix).nixpkgs { } +}: +{ + agame = pkgs.callPackage ./agame.nix { }; +} diff --git a/src/client.rs b/src/client.rs index 8793c4a..a5a7845 100644 --- a/src/client.rs +++ b/src/client.rs @@ -8,6 +8,7 @@ use bevy::prelude::*; use bevy::sprite::{MaterialMesh2dBundle, Mesh2dHandle}; use lightyear::client::input::InputSystemSet; use lightyear::prelude::*; +use std::net::SocketAddr; mod network; @@ -15,30 +16,42 @@ const PLAYER_RADIUS: f32 = 10.; const PLAYER_HOVER_INDICATOR_RADIUS: f32 = 13.; const PLAYER_HOVER_RADIUS: f32 = 20.; -pub fn main(transport: TransportConfig) { +pub fn main(server_addr: Option<SocketAddr>, client_id: u64, transport: TransportConfig) { App::new() .add_plugins(DefaultPlugins) - .add_plugins(ClientPlugin { transport }) + .add_plugins(ClientPlugin { + server_addr, + client_id, + transport, + }) .run(); } +#[derive(Resource)] +struct ClientId(pub u64); + struct ClientPlugin { + pub server_addr: Option<SocketAddr>, + pub client_id: u64, pub transport: TransportConfig, } impl Plugin for ClientPlugin { fn build(&self, app: &mut App) { - app.add_plugins(NetworkPlugin { - transport: self.transport.clone(), - }) - .add_systems(Startup, setup) - .add_systems(Update, (render_players, render_projectiles)) - .add_systems(Update, (move_players, move_projectiles)) - .add_systems( - FixedPreUpdate, - buffer_input.in_set(InputSystemSet::BufferInputs), - ) - .add_systems(Last, (gizmos_hover_indicator, gizmos_attack_indicator)); + app.insert_resource(ClientId(self.client_id)) + .add_plugins(NetworkPlugin { + server_addr: self.server_addr.clone(), + client_id: self.client_id, + transport: self.transport.clone(), + }) + .add_systems(Startup, setup) + .add_systems(Update, (render_players, render_projectiles)) + .add_systems(Update, (move_players, move_projectiles)) + .add_systems( + FixedPreUpdate, + buffer_input.in_set(InputSystemSet::BufferInputs), + ) + .add_systems(Last, (gizmos_hover_indicator, gizmos_attack_indicator)); } } @@ -98,12 +111,15 @@ fn move_players(mut players: Query<(&mut Transform, &PlayerPosition), Changed<Pl fn buffer_input( attackables: Query<(&PlayerId, &PlayerPosition)>, cameras: Query<(&Camera, &GlobalTransform)>, + client_id: Res<ClientId>, mouse_input: Res<ButtonInput<MouseButton>>, mut client: ClientMut, windows: Query<&Window>, ) { if mouse_input.just_pressed(MouseButton::Left) { - if let Some((target_player, _)) = hovered_other_player(&windows, &cameras, &attackables) { + if let Some((target_player, _)) = + hovered_other_player(&cameras, &client_id, &attackables, &windows) + { client.add_input(Inputs::Imperative(Imperative::Attack(target_player))); } else { if let Some(world_position) = cursor_world_position(&windows, &cameras) { @@ -114,12 +130,14 @@ fn buffer_input( } fn gizmos_hover_indicator( - mut gizmos: Gizmos, - hoverables: Query<(&PlayerId, &PlayerPosition)>, cameras: Query<(&Camera, &GlobalTransform)>, + client_id: Res<ClientId>, + hoverables: Query<(&PlayerId, &PlayerPosition)>, + mut gizmos: Gizmos, windows: Query<&Window>, ) { - let Some((_, position)) = hovered_other_player(&windows, &cameras, &hoverables) else { + let Some((_, position)) = hovered_other_player(&cameras, &client_id, &hoverables, &windows) + else { return; }; gizmos.circle_2d(position.0, PLAYER_HOVER_INDICATOR_RADIUS, Color::GREEN); @@ -138,23 +156,24 @@ fn cursor_world_position( } fn hovered_other_player( - windows: &Query<&Window>, cameras: &Query<(&Camera, &GlobalTransform)>, + client_id: &Res<ClientId>, hoverables: &Query<(&PlayerId, &PlayerPosition)>, + windows: &Query<&Window>, ) -> Option<(PlayerId, PlayerPosition)> { - let Some((id, position)) = hovered_player(windows, cameras, hoverables) else { + let Some((id, position)) = hovered_player(cameras, hoverables, windows) else { return None; }; - if id.0 == 0 { + if id.0 == client_id.0 { return None; } Some((id, position)) } fn hovered_player( - windows: &Query<&Window>, cameras: &Query<(&Camera, &GlobalTransform)>, hoverables: &Query<(&PlayerId, &PlayerPosition)>, + windows: &Query<&Window>, ) -> Option<(PlayerId, PlayerPosition)> { let Some(world_position) = cursor_world_position(&windows, &cameras) else { return None; @@ -170,22 +189,26 @@ fn hovered_player( fn gizmos_attack_indicator( cameras: Query<(&Camera, &GlobalTransform)>, + client_id: Res<ClientId>, hoverables: Query<(&PlayerId, &PlayerPosition)>, mut gizmos: Gizmos, players: Query<(&PlayerId, &PlayerPosition)>, windows: Query<&Window>, ) { - let Some(position) = player_position(&players) else { + let Some(position) = player_position(&client_id, &players) else { return; }; - if hovered_other_player(&windows, &cameras, &hoverables).is_some() { + if hovered_other_player(&cameras, &client_id, &hoverables, &windows).is_some() { gizmos.circle_2d(position.0, ATTACK_RANGE, Color::YELLOW); } } -fn player_position(players: &Query<(&PlayerId, &PlayerPosition)>) -> Option<PlayerPosition> { +fn player_position( + client_id: &Res<ClientId>, + players: &Query<(&PlayerId, &PlayerPosition)>, +) -> Option<PlayerPosition> { for (id, position) in players.iter() { - if id.0 == 0 { + if id.0 == client_id.0 { return Some(*position); } } diff --git a/src/client/network.rs b/src/client/network.rs index 4d1a128..df55cc9 100644 --- a/src/client/network.rs +++ b/src/client/network.rs @@ -8,8 +8,11 @@ use lightyear::client::resource::Authentication; use lightyear::prelude::client::NetConfig; use lightyear::prelude::*; use lightyear::transport::LOCAL_SOCKET; +use std::net::SocketAddr; pub struct NetworkPlugin { + pub server_addr: Option<SocketAddr>, + pub client_id: u64, pub transport: TransportConfig, } @@ -20,8 +23,8 @@ impl Plugin for NetworkPlugin { net: NetConfig::Netcode { config: Default::default(), auth: Authentication::Manual { - server_addr: LOCAL_SOCKET, - client_id: CLIENT_ID, + server_addr: self.server_addr.unwrap_or(LOCAL_SOCKET), + client_id: self.client_id, private_key: KEY, protocol_id: PROTOCOL_ID, }, diff --git a/src/main.rs b/src/main.rs index cdd7c17..f213197 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,6 @@ +use clap::Parser; use lightyear::transport::io::TransportConfig; +use rand::Rng; use shared::SERVER_PORT; use std::net::Ipv4Addr; use std::net::SocketAddr; @@ -9,19 +11,48 @@ mod protocol; mod server; mod shared; +#[derive(Parser, Debug)] +struct Cli { + #[arg(long, value_name = "HOST:PORT")] + connect_to: Option<SocketAddr>, + #[arg(long, num_args = 0)] + server: bool, +} + fn main() { - let (from_server_send, from_server_recv) = crossbeam_channel::unbounded(); - let (to_server_send, to_server_recv) = crossbeam_channel::unbounded(); + let cli = Cli::parse(); + + let mut rng = rand::thread_rng(); + let client_id = rng.gen_range(1..=SERVER_PORT); - thread::spawn(|| { + if cli.connect_to.is_some() && cli.server { + panic!("cannot specify both `--connect-to` and `--server`"); + } + + if cli.server { let server_addr = SocketAddr::new(Ipv4Addr::new(0, 0, 0, 0).into(), SERVER_PORT); - server::main(TransportConfig::Channels { - channels: [(server_addr, to_server_recv, from_server_send)].to_vec(), + server::main(TransportConfig::UdpSocket(server_addr)); + } else if let Some(server_addr) = cli.connect_to { + let client_addr = SocketAddr::new(Ipv4Addr::UNSPECIFIED.into(), SERVER_PORT + client_id); + client::main(Some(server_addr), client_id as u64, TransportConfig::UdpSocket(client_addr)); + } else { + let (from_server_send, from_server_recv) = crossbeam_channel::unbounded(); + let (to_server_send, to_server_recv) = crossbeam_channel::unbounded(); + + thread::spawn(|| { + let server_addr = SocketAddr::new(Ipv4Addr::new(0, 0, 0, 0).into(), SERVER_PORT); + server::main(TransportConfig::Channels { + channels: [(server_addr, to_server_recv, from_server_send)].to_vec(), + }); }); - }); - client::main(TransportConfig::LocalChannel { - recv: from_server_recv, - send: to_server_send, - }); + client::main( + None, + client_id as u64, + TransportConfig::LocalChannel { + recv: from_server_recv, + send: to_server_send, + }, + ); + } } diff --git a/src/shared.rs b/src/shared.rs index 4db5224..b3a7cc4 100644 --- a/src/shared.rs +++ b/src/shared.rs @@ -11,7 +11,6 @@ pub mod cooldown; pub mod imperative; pub mod projectile; -pub const CLIENT_ID: u64 = 0; pub const KEY: [u8; 32] = [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ]; |