From d930583e12c8c8a40f0f8ed4995e163f0ad4380b Mon Sep 17 00:00:00 2001 From: Adam <24621027+adoyle0@users.noreply.github.com> Date: Mon, 5 Aug 2024 00:08:29 -0400 Subject: [PATCH] reserved usernames --- client/src/components/auth.rs | 2 +- readme.md | 42 +++++++++++++++++-------------- server/src/api.rs | 4 +-- server/src/api/message_handler.rs | 13 ++++++++++ server/src/lib.rs | 3 ++- server/src/main.rs | 4 ++- 6 files changed, 44 insertions(+), 24 deletions(-) diff --git a/client/src/components/auth.rs b/client/src/components/auth.rs index 3057751..bca2d39 100644 --- a/client/src/components/auth.rs +++ b/client/src/components/auth.rs @@ -31,7 +31,7 @@ pub fn Auth() -> impl IntoView { }) .unwrap(), ); - set_username.set(input.value()); + set_username.set("".to_string()); input.set_value(""); } } diff --git a/readme.md b/readme.md index c2f2e04..ca3bc39 100644 --- a/readme.md +++ b/readme.md @@ -4,12 +4,13 @@ A game master server for the popular game [Cards Against Humanity](https://www.c This started as a problem trying to play games with friends who are all on different platforms. This shall be as cross-platform as it gets. I want it to work on anything that can establish a connection and allow anyone to write any front-end they can dream up whether it be a web page, chat bot, VR, etc. Any clients who share a master server can play together across any platform. - ## Test/Dev: ### Server + With auto-reload: -* [Install cargo-watch](https://github.com/watchexec/cargo-watch?tab=readme-ov-file#install) + +- [Install cargo-watch](https://github.com/watchexec/cargo-watch?tab=readme-ov-file#install) Then: @@ -19,14 +20,15 @@ watch -i client -cx run or if you're lazy like me just run `./test` - Without auto-reload: + ```sh cargo run ``` ### Client -* [Install Trunk](https://trunkrs.dev/#install) + +- [Install Trunk](https://trunkrs.dev/#install) Then: @@ -37,31 +39,33 @@ trunk serve --open ## Build: ### Client + ```sh trunk build --release ``` ### Server + ```sh cargo build ``` ----------- -* The server automatically serves the built client from `/dist` at `0.0.0.0:3030` +--- -* Configure any custom clients to connect to `ws://0.0.0.0:3030/websocket` +- The server automatically serves the built client from `/dist` at `0.0.0.0:3030` +- Configure any custom clients to connect to `ws://0.0.0.0:3030/websocket` ## TODO: -* finish game logic -* prevent multiple users claiming the same identity and then all losing it - may create a zombie -* prevent zombie users after browser crash -* figure out proper auth - client's problem? -* use db -* test bincode and probably move away from json -* make typescript sdk -* support card text editing -* prevent import of cards that have been seen already and edited -* handle duplicates -* efficiency -* make demo clients for multiple platforms and screens + +- finish game logic +- prevent zombie users after browser crash +- figure out proper auth - client's problem? +- use db +- test bincode and probably move away from json +- make typescript sdk +- support card text editing +- prevent import of cards that have been seen already and edited +- handle duplicates +- efficiency +- make demo clients for multiple platforms and screens diff --git a/server/src/api.rs b/server/src/api.rs index bdb5a5a..9ed8c9f 100644 --- a/server/src/api.rs +++ b/server/src/api.rs @@ -44,7 +44,7 @@ pub async fn on_websocket_connection(stream: WebSocket, state: Arc, ad // Subscribe to receive from global broadcast channel let mut rx = state.tx.subscribe(); - // Submit new messages from this client to broadcast + // Send messages to this client let mut send_task = tokio::spawn(async move { while let Ok(msg) = rx.recv().await { if sender.send(Message::Text(msg)).await.is_err() { @@ -53,7 +53,7 @@ pub async fn on_websocket_connection(stream: WebSocket, state: Arc, ad } }); - // Pass messages from broadcast down to this client + // Receive messages from this client let mut recv_task = tokio::spawn(async move { while let Some(Ok(message)) = receiver.next().await { message_handler(state.clone(), addr, message) diff --git a/server/src/api/message_handler.rs b/server/src/api/message_handler.rs index edc23af..c5248b1 100644 --- a/server/src/api/message_handler.rs +++ b/server/src/api/message_handler.rs @@ -132,6 +132,8 @@ fn handle_user_log_in( }; tracing::debug!("{msg}"); + } else if state.reserved_names.read().unwrap().contains(&new_name) { + tracing::debug!("name is taken"); } else { state .online_users @@ -149,8 +151,16 @@ fn handle_user_log_in( new_name }; + // Reserve name + state + .reserved_names + .write() + .unwrap() + .insert(new_name.clone()); + tracing::debug!("{msg}"); tx.send(to_string::(&ChatMessage { text: msg })?)?; + tracing::debug!("Name {} reserved.", &new_name); } tracing::debug!( @@ -159,6 +169,9 @@ fn handle_user_log_in( state.offline_users.read().unwrap().len() ); tx.send(games_update(state))?; + tx.send(chat_meta_update(state))?; + + // send the user their new name Ok(()) } diff --git a/server/src/lib.rs b/server/src/lib.rs index 51178eb..280e46b 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -4,7 +4,7 @@ use rand::prelude::IteratorRandom; use rand::thread_rng; use serde::Deserialize; use std::{ - collections::HashMap, + collections::{HashMap, HashSet}, fs::{read_to_string, File}, io::{BufRead, BufReader}, net::SocketAddr, @@ -339,6 +339,7 @@ pub fn load_names(path: &str) -> Result> { // Our shared state pub struct AppState { // We require unique usernames. This tracks which usernames have been taken. + pub reserved_names: RwLock>, pub online_users: RwLock>>>, pub offline_users: RwLock>>>, // Channel used to send messages to all connected clients. diff --git a/server/src/main.rs b/server/src/main.rs index 48beb13..cdf8e48 100644 --- a/server/src/main.rs +++ b/server/src/main.rs @@ -3,7 +3,7 @@ use anyhow::{Context, Result}; use axum::{routing::get, Router}; use std::{ - collections::HashMap, + collections::{HashMap, HashSet}, net::SocketAddr, sync::{Arc, RwLock}, }; @@ -28,6 +28,7 @@ async fn main() -> Result<()> { // Set up application state for use with with_state(). // Main Broadcast Channel let (tx, _rx) = broadcast::channel(100); + let reserved_names = RwLock::new(HashSet::::new()); let online_users = RwLock::new(HashMap::>>::new()); let offline_users = RwLock::new(HashMap::>>::new()); let (packs, packs_meta) = load_cards_from_json("data/cah-cards-full.json")?; @@ -36,6 +37,7 @@ async fn main() -> Result<()> { let last_names = load_names("data/last.txt")?; let app_state = Arc::new(AppState { + reserved_names, online_users, offline_users, tx,