let user handler handle name stuff

This commit is contained in:
Adam 2024-11-19 22:22:00 -05:00
parent 3900ec55d4
commit 5695d2919d
4 changed files with 57 additions and 68 deletions

View file

@ -8,8 +8,7 @@ use lib::*;
use serde::Deserialize; use serde::Deserialize;
use std::{ use std::{
collections::{HashMap, HashSet}, collections::{HashMap, HashSet},
fs::{read_to_string, File}, fs::read_to_string,
io::{BufRead, BufReader},
net::SocketAddr, net::SocketAddr,
sync::{Arc, RwLock}, sync::{Arc, RwLock},
}; };
@ -46,30 +45,13 @@ impl User {
} }
} }
/// Parse name list
pub fn load_names(path: &str) -> Result<Vec<String>> {
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 // Our shared state
pub struct AppState { pub struct AppState {
pub first_names: Vec<String>,
pub games_by_user: RwLock<HashMap<String, Vec<String>>>, pub games_by_user: RwLock<HashMap<String, Vec<String>>>,
pub last_names: Vec<String>,
pub offline_users: RwLock<HashMap<String, Arc<RwLock<User>>>>, pub offline_users: RwLock<HashMap<String, Arc<RwLock<User>>>>,
pub online_users: RwLock<HashMap<SocketAddr, Arc<RwLock<User>>>>, pub online_users: RwLock<HashMap<SocketAddr, Arc<RwLock<User>>>>,
pub packs: CardPacks, pub packs: CardPacks,
pub packs_meta: CardPacksMeta, pub packs_meta: CardPacksMeta,
pub reserved_names: RwLock<HashSet<String>>,
pub tx_broadcast: broadcast::Sender<Message>, pub tx_broadcast: broadcast::Sender<Message>,
pub tx_game_handler: mpsc::Sender<GameHandlerMessage>, pub tx_game_handler: mpsc::Sender<GameHandlerMessage>,
pub tx_incoming_message_handler: mpsc::Sender<(SocketAddr, Message)>, pub tx_incoming_message_handler: mpsc::Sender<(SocketAddr, Message)>,

View file

@ -6,7 +6,7 @@ use axum::{routing::get, Router};
use clap::{arg, command}; use clap::{arg, command};
use server::*; use server::*;
use std::{ use std::{
collections::{HashMap, HashSet}, collections::HashMap,
net::SocketAddr, net::SocketAddr,
sync::{Arc, RwLock}, sync::{Arc, RwLock},
time::Duration, time::Duration,
@ -73,22 +73,16 @@ async fn main() -> Result<()> {
let (tx_game_handler, mut rx_game_handler) = mpsc::channel(32); 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_incoming_message_handler, mut rx_incoming_message_handler) = mpsc::channel(32);
let (tx_user_handler, mut rx_user_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::<String, Vec<String>>::new()); let games_by_user = RwLock::new(HashMap::<String, Vec<String>>::new());
let last_names = load_names("data/last.txt")?;
let offline_users = RwLock::new(HashMap::<String, Arc<RwLock<User>>>::new()); let offline_users = RwLock::new(HashMap::<String, Arc<RwLock<User>>>::new());
let online_users = RwLock::new(HashMap::<SocketAddr, Arc<RwLock<User>>>::new()); let online_users = RwLock::new(HashMap::<SocketAddr, Arc<RwLock<User>>>::new());
let reserved_names = RwLock::new(HashSet::<String>::new());
let app_state = Arc::new(AppState { let app_state = Arc::new(AppState {
first_names,
games_by_user, games_by_user,
last_names,
offline_users, offline_users,
online_users, online_users,
packs, packs,
packs_meta, packs_meta,
reserved_names,
tx_broadcast, tx_broadcast,
tx_game_handler, tx_game_handler,
tx_incoming_message_handler, tx_incoming_message_handler,

View file

@ -4,17 +4,39 @@ use crate::SendUserMessage::*;
use crate::User; use crate::User;
use crate::UserHandlerMessage::*; use crate::UserHandlerMessage::*;
use crate::*; use crate::*;
use rand::prelude::SliceRandom;
use serde_json::to_string; use serde_json::to_string;
use std::net::SocketAddr; use std::{
use std::sync::{Arc, RwLock}; fs::File,
io::{BufRead, BufReader},
net::SocketAddr,
sync::{Arc, RwLock},
};
// TODO: clean up all this tx/msg mess // TODO: clean up all this tx/msg mess
/// Parse name list
pub fn load_names(path: &str) -> Result<Vec<String>> {
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 /// Handles users
pub struct UserHandler { pub struct UserHandler {
/// Pointer to global state /// Pointer to global state
state: Arc<AppState>, state: Arc<AppState>,
users_by_id: HashMap<String, Arc<RwLock<User>>>, users_by_id: HashMap<String, Arc<RwLock<User>>>,
first_names: Vec<String>,
last_names: Vec<String>,
reserved_names: HashSet<String>,
} }
pub enum DmUserMethod { pub enum DmUserMethod {
@ -24,7 +46,7 @@ pub enum DmUserMethod {
/// For interacting with the user handler /// For interacting with the user handler
pub enum UserHandlerMessage { pub enum UserHandlerMessage {
NewUser(User, SocketAddr), NewUser(Sender<Message>, SocketAddr),
UserLogIn(UserLogInRequest, SocketAddr), UserLogIn(UserLogInRequest, SocketAddr),
DmUser(SendUserMessage, DmUserMethod), DmUser(SendUserMessage, DmUserMethod),
Cleanup(SocketAddr), Cleanup(SocketAddr),
@ -44,15 +66,18 @@ impl UserHandler {
UserHandler { UserHandler {
state, state,
users_by_id: HashMap::<String, Arc<RwLock<User>>>::new(), users_by_id: HashMap::<String, Arc<RwLock<User>>>::new(),
first_names: load_names("data/first.txt").unwrap(),
last_names: load_names("data/last.txt").unwrap(),
reserved_names: HashSet::<String>::new(),
} }
} }
/// Handles incoming messages /// Handles incoming messages
pub async fn handle(&mut self, message: UserHandlerMessage) { pub async fn handle(&mut self, message: UserHandlerMessage) {
match message { match message {
NewUser(user, addr) => { NewUser(dm_tx, addr) => {
// TODO: make this not async // 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, UserLogIn(request, addr) => self.login(request, addr).await,
DmUser(message, method) => self.dm_user(message, method).await, DmUser(message, method) => self.dm_user(message, method).await,
@ -117,12 +142,26 @@ impl UserHandler {
} }
/// Create, register, and hydrate new user /// Create, register, and hydrate new user
async fn set_up_new_user(&mut self, user: User, addr: SocketAddr) { async fn set_up_new_user(&mut self, dm_tx: Sender<Message>, addr: SocketAddr) {
let user_tx = user.tx.clone(); // Roll for username and re-roll if taken
let new_user = Arc::new(RwLock::new(user)); 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 // Notify client of new username
let tx = user_tx.clone(); let tx = dm_tx.clone();
let msg = user_client_self_update(&new_user); let msg = user_client_self_update(&new_user);
tokio::spawn(async move { tx.send(msg).await }); tokio::spawn(async move { tx.send(msg).await });
@ -141,7 +180,7 @@ impl UserHandler {
let tx = self.state.tx_game_handler.clone(); let tx = self.state.tx_game_handler.clone();
let msg = GameHandlerMessage::BroadcastGamesUpdate(); let msg = GameHandlerMessage::BroadcastGamesUpdate();
tokio::spawn(async move { tx.send(msg).await }); 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); let msg = meta_new_game_card_packs(&self.state);
tokio::spawn(async move { tx.send(msg).await }); tokio::spawn(async move { tx.send(msg).await });
@ -158,12 +197,12 @@ impl UserHandler {
tokio::spawn(async move { tx.send(msg) }); tokio::spawn(async move { tx.send(msg) });
// TODO: this races the broadcasts but if it's done last it'll probably show up last... // 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 }); tokio::spawn(async move { tx.send(meta_motd()).await });
} }
/// Handle user login /// 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 username_max_len = 66; // This is the longest name the generator may produce right now
let broadcast_tx = self.state.tx_broadcast.clone(); let broadcast_tx = self.state.tx_broadcast.clone();
let new_name; let new_name;
@ -222,13 +261,7 @@ impl UserHandler {
tokio::spawn(async move { tx.send(Message::Text(msg)) }); tokio::spawn(async move { tx.send(Message::Text(msg)) });
} }
// Check if name is taken by an online user // Check if name is taken by an online user
else if self else if self.reserved_names.contains(&new_name) {
.state
.reserved_names
.read()
.unwrap()
.contains(&new_name)
{
self.dm_user( self.dm_user(
SendChatMessage(ChatMessage { SendChatMessage(ChatMessage {
text: "Name is taken".to_string(), text: "Name is taken".to_string(),
@ -245,11 +278,7 @@ impl UserHandler {
.await; .await;
} else { } else {
// Reserve name // Reserve name
self.state self.reserved_names.insert(new_name.clone());
.reserved_names
.write()
.unwrap()
.insert(new_name.clone());
// Change user's name // Change user's name
if let Some(user) = self.state.online_users.write().unwrap().get_mut(&addr) { if let Some(user) = self.state.online_users.write().unwrap().get_mut(&addr) {
@ -279,6 +308,7 @@ impl UserHandler {
) )
.await; .await;
// Update games this user is in // 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) { if let Some(user) = &self.state.online_users.read().unwrap().get(&addr) {
let user_id = user.read().unwrap().uuid.to_string(); let user_id = user.read().unwrap().uuid.to_string();
{ {

View file

@ -1,6 +1,5 @@
use crate::user_handler::*; use crate::user_handler::*;
use crate::AppState; use crate::AppState;
use crate::User;
use axum::{ use axum::{
extract::{ extract::{
ws::{Message, WebSocket}, ws::{Message, WebSocket},
@ -9,7 +8,6 @@ use axum::{
response::IntoResponse, response::IntoResponse,
}; };
use futures::{SinkExt, StreamExt}; use futures::{SinkExt, StreamExt};
use rand::prelude::SliceRandom;
use std::collections::HashMap; use std::collections::HashMap;
use std::net::SocketAddr; use std::net::SocketAddr;
use std::sync::Arc; use std::sync::Arc;
@ -36,23 +34,8 @@ pub async fn websocket_on_connection(stream: WebSocket, state: Arc<AppState>, ad
let mut map = HashMap::new(); let mut map = HashMap::new();
map.insert(addr, dm_tx.clone()); 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 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") }); tokio::spawn(async move { tx.send(msg).await.expect("User handler is down") });
// Subscribe to receive from global broadcast channel // Subscribe to receive from global broadcast channel