diff --git a/server/src/lib.rs b/server/src/lib.rs index 4eaa3a1..db74312 100644 --- a/server/src/lib.rs +++ b/server/src/lib.rs @@ -8,8 +8,7 @@ use lib::*; use serde::Deserialize; use std::{ collections::{HashMap, HashSet}, - fs::{read_to_string, File}, - io::{BufRead, BufReader}, + fs::read_to_string, net::SocketAddr, sync::{Arc, RwLock}, }; @@ -46,30 +45,13 @@ impl User { } } -/// Parse name list -pub fn load_names(path: &str) -> Result> { - let f = File::open(path).with_context(|| format!("Invalid names path: \"{}\"", path))?; - let f = BufReader::new(f); - - let mut buf = vec![]; - - for line in f.lines() { - buf.push(line?) - } - - Ok(buf) -} - // Our shared state pub struct AppState { - pub first_names: Vec, pub games_by_user: RwLock>>, - pub last_names: Vec, pub offline_users: RwLock>>>, pub online_users: RwLock>>>, pub packs: CardPacks, pub packs_meta: CardPacksMeta, - pub reserved_names: RwLock>, pub tx_broadcast: broadcast::Sender, pub tx_game_handler: mpsc::Sender, pub tx_incoming_message_handler: mpsc::Sender<(SocketAddr, Message)>, diff --git a/server/src/main.rs b/server/src/main.rs index 8285627..22c5c32 100644 --- a/server/src/main.rs +++ b/server/src/main.rs @@ -6,7 +6,7 @@ use axum::{routing::get, Router}; use clap::{arg, command}; use server::*; use std::{ - collections::{HashMap, HashSet}, + collections::HashMap, net::SocketAddr, sync::{Arc, RwLock}, time::Duration, @@ -73,22 +73,16 @@ async fn main() -> Result<()> { let (tx_game_handler, mut rx_game_handler) = mpsc::channel(32); let (tx_incoming_message_handler, mut rx_incoming_message_handler) = mpsc::channel(32); let (tx_user_handler, mut rx_user_handler) = mpsc::channel(32); - let first_names = load_names("data/first.txt")?; let games_by_user = RwLock::new(HashMap::>::new()); - let last_names = load_names("data/last.txt")?; let offline_users = RwLock::new(HashMap::>>::new()); let online_users = RwLock::new(HashMap::>>::new()); - let reserved_names = RwLock::new(HashSet::::new()); let app_state = Arc::new(AppState { - first_names, games_by_user, - last_names, offline_users, online_users, packs, packs_meta, - reserved_names, tx_broadcast, tx_game_handler, tx_incoming_message_handler, diff --git a/server/src/user_handler.rs b/server/src/user_handler.rs index 6a4f931..0211568 100644 --- a/server/src/user_handler.rs +++ b/server/src/user_handler.rs @@ -4,17 +4,39 @@ use crate::SendUserMessage::*; use crate::User; use crate::UserHandlerMessage::*; use crate::*; +use rand::prelude::SliceRandom; use serde_json::to_string; -use std::net::SocketAddr; -use std::sync::{Arc, RwLock}; +use std::{ + fs::File, + io::{BufRead, BufReader}, + net::SocketAddr, + sync::{Arc, RwLock}, +}; // TODO: clean up all this tx/msg mess +/// Parse name list +pub fn load_names(path: &str) -> Result> { + let f = File::open(path).with_context(|| format!("Invalid names path: \"{}\"", path))?; + let f = BufReader::new(f); + + let mut buf = vec![]; + + for line in f.lines() { + buf.push(line?) + } + + Ok(buf) +} + /// Handles users pub struct UserHandler { /// Pointer to global state state: Arc, users_by_id: HashMap>>, + first_names: Vec, + last_names: Vec, + reserved_names: HashSet, } pub enum DmUserMethod { @@ -24,7 +46,7 @@ pub enum DmUserMethod { /// For interacting with the user handler pub enum UserHandlerMessage { - NewUser(User, SocketAddr), + NewUser(Sender, SocketAddr), UserLogIn(UserLogInRequest, SocketAddr), DmUser(SendUserMessage, DmUserMethod), Cleanup(SocketAddr), @@ -44,15 +66,18 @@ impl UserHandler { UserHandler { state, users_by_id: HashMap::>>::new(), + first_names: load_names("data/first.txt").unwrap(), + last_names: load_names("data/last.txt").unwrap(), + reserved_names: HashSet::::new(), } } /// Handles incoming messages pub async fn handle(&mut self, message: UserHandlerMessage) { match message { - NewUser(user, addr) => { + NewUser(dm_tx, addr) => { // TODO: make this not async - self.set_up_new_user(user, addr).await + self.set_up_new_user(dm_tx, addr).await } UserLogIn(request, addr) => self.login(request, addr).await, DmUser(message, method) => self.dm_user(message, method).await, @@ -117,12 +142,26 @@ impl UserHandler { } /// Create, register, and hydrate new user - async fn set_up_new_user(&mut self, user: User, addr: SocketAddr) { - let user_tx = user.tx.clone(); - let new_user = Arc::new(RwLock::new(user)); + async fn set_up_new_user(&mut self, dm_tx: Sender, addr: SocketAddr) { + // Roll for username and re-roll if taken + let mut name; + + loop { + name = format!( + "{} {}", + self.first_names.choose(&mut rand::thread_rng()).unwrap(), + self.last_names.choose(&mut rand::thread_rng()).unwrap(), + ); + + if !self.reserved_names.contains(&name) { + break; + } + } + + let new_user = Arc::new(RwLock::new(User::new(name, dm_tx.clone()))); // Notify client of new username - let tx = user_tx.clone(); + let tx = dm_tx.clone(); let msg = user_client_self_update(&new_user); tokio::spawn(async move { tx.send(msg).await }); @@ -141,7 +180,7 @@ impl UserHandler { let tx = self.state.tx_game_handler.clone(); let msg = GameHandlerMessage::BroadcastGamesUpdate(); tokio::spawn(async move { tx.send(msg).await }); - let tx = user_tx.clone(); + let tx = dm_tx.clone(); let msg = meta_new_game_card_packs(&self.state); tokio::spawn(async move { tx.send(msg).await }); @@ -158,12 +197,12 @@ impl UserHandler { tokio::spawn(async move { tx.send(msg) }); // TODO: this races the broadcasts but if it's done last it'll probably show up last... - let tx = user_tx.clone(); + let tx = dm_tx.clone(); tokio::spawn(async move { tx.send(meta_motd()).await }); } /// Handle user login - async fn login(&self, request: UserLogInRequest, addr: SocketAddr) { + async fn login(&mut self, request: UserLogInRequest, addr: SocketAddr) { let username_max_len = 66; // This is the longest name the generator may produce right now let broadcast_tx = self.state.tx_broadcast.clone(); let new_name; @@ -222,13 +261,7 @@ impl UserHandler { tokio::spawn(async move { tx.send(Message::Text(msg)) }); } // Check if name is taken by an online user - else if self - .state - .reserved_names - .read() - .unwrap() - .contains(&new_name) - { + else if self.reserved_names.contains(&new_name) { self.dm_user( SendChatMessage(ChatMessage { text: "Name is taken".to_string(), @@ -245,11 +278,7 @@ impl UserHandler { .await; } else { // Reserve name - self.state - .reserved_names - .write() - .unwrap() - .insert(new_name.clone()); + self.reserved_names.insert(new_name.clone()); // Change user's name if let Some(user) = self.state.online_users.write().unwrap().get_mut(&addr) { @@ -279,6 +308,7 @@ impl UserHandler { ) .await; // Update games this user is in + // TODO: game handler maybe should own games_by_user if let Some(user) = &self.state.online_users.read().unwrap().get(&addr) { let user_id = user.read().unwrap().uuid.to_string(); { diff --git a/server/src/websocket.rs b/server/src/websocket.rs index 5edc66e..d863e81 100644 --- a/server/src/websocket.rs +++ b/server/src/websocket.rs @@ -1,6 +1,5 @@ use crate::user_handler::*; use crate::AppState; -use crate::User; use axum::{ extract::{ ws::{Message, WebSocket}, @@ -9,7 +8,6 @@ use axum::{ response::IntoResponse, }; use futures::{SinkExt, StreamExt}; -use rand::prelude::SliceRandom; use std::collections::HashMap; use std::net::SocketAddr; use std::sync::Arc; @@ -36,23 +34,8 @@ pub async fn websocket_on_connection(stream: WebSocket, state: Arc, ad let mut map = HashMap::new(); map.insert(addr, dm_tx.clone()); - // Roll for username and re-roll if taken - let mut username; - - loop { - username = format!( - "{} {}", - state.first_names.choose(&mut rand::thread_rng()).unwrap(), - state.last_names.choose(&mut rand::thread_rng()).unwrap(), - ); - - if !state.reserved_names.read().unwrap().contains(&username) { - break; - } - } - let tx = state.tx_user_handler.clone(); - let msg = UserHandlerMessage::NewUser(User::new(username, dm_tx.clone()), addr); + let msg = UserHandlerMessage::NewUser(dm_tx.clone(), addr); tokio::spawn(async move { tx.send(msg).await.expect("User handler is down") }); // Subscribe to receive from global broadcast channel