library

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs

commit b8e5176a875a15b95b15908f7a40baaa0fa1ae33
parent f7b8519646a7ea44b3d9d375bc2f7ce225694070
Author: Bradley Gannon <bradley@bradleygannon.com>
Date:   Sat, 15 Aug 2026 10:21:40 -0400

Give every player an ID

Diffstat:
MCargo.lock | 156++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
Mclient/Cargo.toml | 1+
Mclient/src/game.rs | 11++++++-----
Mclient/src/main.rs | 28+++++++++++++---------------
Mclient/src/net.rs | 22+++++++++++-----------
Mclient/src/render.rs | 13+++++--------
Mcommon/Cargo.toml | 1+
Mcommon/src/game.rs | 23+++++++++++++----------
Mserver/src/main.rs | 22+++++++++++-----------
Mserver/src/net.rs | 8++++----
10 files changed, 219 insertions(+), 66 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock @@ -33,6 +33,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] +name = "bumpalo" +version = "3.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" + +[[package]] name = "bytemuck" version = "1.25.2" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -60,6 +66,7 @@ dependencies = [ "mio", "postcard", "serde", + "uuid", ] [[package]] @@ -85,6 +92,7 @@ dependencies = [ "glam", "postcard", "serde", + "uuid", ] [[package]] @@ -159,6 +167,41 @@ dependencies = [ ] [[package]] +name = "futures-core" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92d699e522242e69e3003b94ecc1f960f3a5e015aa7c5d7486e65ad01dd94f5e" + +[[package]] +name = "futures-task" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd417de3d1d015fc3bfd2b1ea46dfc7bab72ef86f1cc7cc9c78e728b34a6d1fd" + +[[package]] +name = "futures-util" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d50a92467f8ba5dd6e3ee5d4bd04d73ab2e4e1c44474a0674821dfce14b79bc" +dependencies = [ + "futures-core", + "futures-task", + "pin-project-lite", + "slab", +] + +[[package]] +name = "getrandom" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099" +dependencies = [ + "cfg-if", + "libc", + "r-efi", +] + +[[package]] name = "glam" version = "0.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -192,6 +235,17 @@ dependencies = [ ] [[package]] +name = "js-sys" +version = "0.3.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e0c1080212aad755ea003d18543e8768dd432c48819efd73a7bf1e39b7a5a3a" +dependencies = [ + "cfg-if", + "futures-util", + "wasm-bindgen", +] + +[[package]] name = "libc" version = "0.2.189" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -297,6 +351,18 @@ dependencies = [ ] [[package]] +name = "once_cell" +version = "1.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" + +[[package]] +name = "pin-project-lite" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" + +[[package]] name = "png" version = "0.17.16" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -346,6 +412,18 @@ dependencies = [ ] [[package]] +name = "r-efi" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" + +[[package]] +name = "rustversion" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f" + +[[package]] name = "serde" version = "1.0.229" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -372,7 +450,7 @@ checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 3.0.3", ] [[package]] @@ -394,6 +472,23 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3a219298ac11a56ea9a6d2120044824d6f01aeb034955e7af7bc16858527deea" [[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + +[[package]] +name = "syn" +version = "2.0.119" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] name = "syn" version = "3.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -421,7 +516,7 @@ checksum = "bc04cd3e1236dd4a98afca4569f2deb3f120e5422a4023be2cb683f8486292af" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 3.0.3", ] [[package]] @@ -440,12 +535,69 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" [[package]] +name = "uuid" +version = "1.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2cefc03fd367c0c6d4305de1b312cf00248c4114f4a0418ce6a6af769e3b0bd9" +dependencies = [ + "getrandom", + "js-sys", + "serde_core", + "wasm-bindgen", +] + +[[package]] name = "wasi" version = "0.11.1+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] +name = "wasm-bindgen" +version = "0.2.127" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b70935747edd64d89de3efa29d73789b806c15798f8e7dca4d8ac356b50ce70" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.127" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77775f8f3f7217702089053b94958f8f54061a3f663417df76e19cbdcca29bc1" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.127" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e11d33f857dc2fb11b8bc75aee111aa9cbeb12cd9f25efd3d4c2a3dd4e235284" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn 2.0.119", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.127" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ef64dbcc55df09c7e5a46182d181c2cfa3e925f3da937ea764728b4bbb9dcbf" +dependencies = [ + "unicode-ident", +] + +[[package]] name = "winapi" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" diff --git a/client/Cargo.toml b/client/Cargo.toml @@ -11,3 +11,4 @@ macroquad = { version = "0.4.16", features = ["glam-serde"] } mio = { version = "1.2.2", features = ["net", "os-poll"] } postcard = { version = "1.1.3", default-features = false, features = ["use-std"] } serde = { version = "1.0.229", features = ["derive"] } +uuid = { version = "1.24.1", features = ["serde", "v4"] } diff --git a/client/src/game.rs b/client/src/game.rs @@ -1,7 +1,8 @@ -use common::game::{PlayerAction, PlayerActionKind}; +use common::game::{Action, ActionKind}; use macroquad::prelude::*; +use uuid::Uuid; -pub(crate) fn handle_input(player_name: &str) -> Option<PlayerAction> { +pub(crate) fn handle_input(local_player_id: &Uuid) -> Option<Action> { let velocity_x = if is_key_down(KeyCode::A) { -1. } else if is_key_down(KeyCode::D) { @@ -17,9 +18,9 @@ pub(crate) fn handle_input(player_name: &str) -> Option<PlayerAction> { 0. }; if velocity_x != 0. || velocity_y != 0. { - Some(PlayerAction::new( - player_name.to_string(), - PlayerActionKind::Move(Vec2::new(velocity_x, velocity_y)), + Some(Action::new( + local_player_id.clone(), + ActionKind::Move(Vec2::new(velocity_x, velocity_y)), )) } else { None diff --git a/client/src/main.rs b/client/src/main.rs @@ -6,12 +6,10 @@ use std::{ use common::{ codec::Codec, - game::{GameState, PlayerAction, PlayerActionKind}, -}; -use macroquad::{ - prelude::*, - rand::{gen_range, srand}, + game::{Action, ActionKind, GameState}, }; +use macroquad::{prelude::*, rand::srand}; +use uuid::Uuid; use crate::{ game::handle_input, @@ -46,20 +44,20 @@ async fn main() { .subsec_nanos() as u64, ); - // choose a random three-letter lowercase string as our player name (for now) - let local_player_name = (0..3) - .map(|_| gen_range('a' as u8, 'z' as u8)) - .fold(String::new(), |acc, el| acc + &(el as char).to_string()); + // TODO: generate this on the server + let local_player_id = Uuid::new_v4(); - let _ = client_to_server_tx.send(PlayerAction::new( - local_player_name.to_string(), - PlayerActionKind::Join, + let _ = client_to_server_tx.send(Action::new( + local_player_id, + ActionKind::Join { + name: local_player_id.to_string().chars().take(3).collect(), + }, )); let mut camera = Camera::default(); let mut codec = Codec::default(); let mut game = GameState::default(); - let _on_shutdown = OnShutdown::new(client_to_server_tx.clone(), local_player_name.to_string()); + let _on_shutdown = OnShutdown::new(client_to_server_tx.clone(), local_player_id); loop { let bytes: Vec<u8> = server_to_client_rx.try_iter().flatten().collect(); @@ -68,12 +66,12 @@ async fn main() { game = g; } } - if let Some(action) = handle_input(&local_player_name) { + if let Some(action) = handle_input(&local_player_id) { if let Err(_) = client_to_server_tx.send(action) { return; } } - follow_local_player(&mut camera, &game, &local_player_name); + follow_local_player(&mut camera, &game, &local_player_id); render(&camera, &game, &spritesheet); next_frame().await; } diff --git a/client/src/net.rs b/client/src/net.rs @@ -11,13 +11,14 @@ use std::{ use common::{ codec::Codec, - game::{PlayerAction, PlayerActionKind}, + game::{Action, ActionKind}, }; use mio::{Events, Interest, Poll, Token, net::TcpStream}; +use uuid::Uuid; const TOKEN: Token = Token(0); -pub(crate) fn handle_connection(addr: SocketAddr, tx: Sender<Vec<u8>>, rx: Receiver<PlayerAction>) { +pub(crate) fn handle_connection(addr: SocketAddr, tx: Sender<Vec<u8>>, rx: Receiver<Action>) { let mut stream = TcpStream::connect(addr).unwrap(); let poll = Poll::new().unwrap(); poll.registry() @@ -56,7 +57,7 @@ pub(crate) fn handle_server_read( } } -fn handle_server_write(stream: Arc<Mutex<TcpStream>>, rx: Receiver<PlayerAction>) { +fn handle_server_write(stream: Arc<Mutex<TcpStream>>, rx: Receiver<Action>) { let mut all_sent; loop { match rx.recv() { @@ -90,24 +91,23 @@ fn handle_server_write(stream: Arc<Mutex<TcpStream>>, rx: Receiver<PlayerAction> } pub(crate) struct OnShutdown { - tx: Sender<PlayerAction>, - local_player_name: String, + tx: Sender<Action>, + local_player_id: Uuid, } impl OnShutdown { - pub(crate) fn new(tx: Sender<PlayerAction>, local_player_name: String) -> Self { + pub(crate) fn new(tx: Sender<Action>, local_player_id: Uuid) -> Self { OnShutdown { tx, - local_player_name, + local_player_id, } } } impl Drop for OnShutdown { fn drop(&mut self) { - let _ = self.tx.send(PlayerAction::new( - self.local_player_name.clone(), - PlayerActionKind::Leave, - )); + let _ = self + .tx + .send(Action::new(self.local_player_id, ActionKind::Leave)); } } diff --git a/client/src/render.rs b/client/src/render.rs @@ -1,5 +1,6 @@ use common::game::{GameState, SpriteIndex}; use macroquad::prelude::*; +use uuid::Uuid; #[derive(Debug, Default)] pub(crate) struct Camera { @@ -57,14 +58,14 @@ pub(crate) fn load_sprites() -> Spritesheet { pub(crate) fn render(camera: &Camera, game: &GameState, spritesheet: &Spritesheet) { clear_background(MAGENTA); - for (name, player) in game.players.iter() { + for player in game.players.values() { let sprite = match player.sprite_index { SpriteIndex::Meeple => spritesheet.meeple.clone(), }; let position_camera = player.position - camera.position; sprite.render(position_camera); draw_text( - name, + player.name.clone(), position_camera.x + 12., position_camera.y + sprite.size.y + 16., 16., @@ -73,13 +74,9 @@ pub(crate) fn render(camera: &Camera, game: &GameState, spritesheet: &Spriteshee } } -pub(crate) fn follow_local_player(camera: &mut Camera, game: &GameState, local_player_name: &str) { +pub(crate) fn follow_local_player(camera: &mut Camera, game: &GameState, local_player_id: &Uuid) { let screen_center = Vec2::new(screen_width() / 2., screen_height() / 2.); - if let Some((_, player)) = game - .players - .iter() - .find(|(name, _)| *name == local_player_name) - { + if let Some((_, player)) = game.players.iter().find(|(id, _)| *id == local_player_id) { camera.position = player.position - screen_center; } } diff --git a/common/Cargo.toml b/common/Cargo.toml @@ -8,3 +8,4 @@ anyhow = "1.0.104" glam = { version = "0.27.0", features = ["scalar-math", "serde"] } postcard = { version = "1.1.3", default-features = false, features = ["use-std"] } serde = { version = "1.0.229", features = ["derive"] } +uuid = { version = "1.24.1", features = ["v4", "serde"] } diff --git a/common/src/game.rs b/common/src/game.rs @@ -2,18 +2,21 @@ use std::{collections::HashMap, time::SystemTime}; use glam::Vec2; use serde::{Deserialize, Serialize}; +use uuid::Uuid; #[derive(Serialize, Deserialize, Debug)] pub struct GameState { pub timestamp: SystemTime, - pub players: HashMap<String, Player>, + pub sequence_number: u32, + pub players: HashMap<Uuid, Player>, } impl Default for GameState { fn default() -> Self { Self { timestamp: SystemTime::now(), - players: Default::default(), + sequence_number: 0, + players: HashMap::with_capacity(32), } } } @@ -26,20 +29,20 @@ pub struct Player { } #[derive(Serialize, Deserialize, Debug)] -pub struct PlayerAction { - pub player_name: String, - pub kind: PlayerActionKind, +pub struct Action { + pub player_id: Uuid, + pub kind: ActionKind, } -impl PlayerAction { - pub fn new(player_name: String, kind: PlayerActionKind) -> Self { - PlayerAction { player_name, kind } +impl Action { + pub fn new(player_id: Uuid, kind: ActionKind) -> Self { + Action { player_id, kind } } } #[derive(Serialize, Deserialize, Debug)] -pub enum PlayerActionKind { - Join, +pub enum ActionKind { + Join { name: String }, Move(Vec2), Leave, } diff --git a/server/src/main.rs b/server/src/main.rs @@ -7,7 +7,7 @@ use std::{ use common::{ codec::Codec, - game::{GameState, Player, PlayerActionKind, SpriteIndex}, + game::{ActionKind, GameState, Player, SpriteIndex}, }; use glam::Vec2; use postcard::to_stdvec; @@ -29,26 +29,26 @@ fn main() { let mut send_game_status = false; // Process all pending player actions - for player_action in handler_to_main_rx.try_iter() { + for action in handler_to_main_rx.try_iter() { send_game_status = true; - match player_action.kind { - PlayerActionKind::Join => { + match action.kind { + ActionKind::Join { name } => { game.players.insert( - player_action.player_name.clone(), + action.player_id.clone(), Player { - name: player_action.player_name.clone(), + name, position: Vec2::ZERO, sprite_index: SpriteIndex::Meeple, }, ); } - PlayerActionKind::Move(vec2) => { - if let Some(player) = game.players.get_mut(&player_action.player_name) { - player.position += vec2; + ActionKind::Move(velocity) => { + if let Some(player) = game.players.get_mut(&action.player_id) { + player.position += velocity; } } - PlayerActionKind::Leave => { - game.players.remove(&player_action.player_name); + ActionKind::Leave => { + game.players.remove(&action.player_id); } } } diff --git a/server/src/net.rs b/server/src/net.rs @@ -10,7 +10,7 @@ use std::{ time::Duration, }; -use common::{codec::Codec, game::PlayerAction}; +use common::{codec::Codec, game::Action}; use mio::{ Events, Interest, Poll, Token, net::{TcpListener, TcpStream}, @@ -25,7 +25,7 @@ pub(crate) struct Handler { pub tx: Sender<Vec<u8>>, } -pub(crate) fn listen(handlers: Arc<Mutex<LinkedList<Handler>>>, tx: Sender<PlayerAction>) { +pub(crate) fn listen(handlers: Arc<Mutex<LinkedList<Handler>>>, tx: Sender<Action>) { let mut poll = Poll::new().unwrap(); let mut events = Events::with_capacity(128); let mut listener = TcpListener::bind("0.0.0.0:6767".parse().unwrap()).unwrap(); @@ -52,7 +52,7 @@ pub(crate) fn listen(handlers: Arc<Mutex<LinkedList<Handler>>>, tx: Sender<Playe } } -fn handle_connection(mut stream: TcpStream, rx: Receiver<Vec<u8>>, tx: Sender<PlayerAction>) { +fn handle_connection(mut stream: TcpStream, rx: Receiver<Vec<u8>>, tx: Sender<Action>) { let poll = Poll::new().unwrap(); poll.registry() .register(&mut stream, TOKEN, Interest::READABLE) @@ -67,7 +67,7 @@ fn handle_connection(mut stream: TcpStream, rx: Receiver<Vec<u8>>, tx: Sender<Pl } } -fn handle_client_read(mut poll: Poll, stream: Arc<Mutex<TcpStream>>, tx: Sender<PlayerAction>) { +fn handle_client_read(mut poll: Poll, stream: Arc<Mutex<TcpStream>>, tx: Sender<Action>) { let mut events = Events::with_capacity(128); let mut codec = Codec::default(); let mut buffer = [0x00; 1024];