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 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<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
pub struct AppState {
pub first_names: 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 online_users: RwLock<HashMap<SocketAddr, Arc<RwLock<User>>>>,
pub packs: CardPacks,
pub packs_meta: CardPacksMeta,
pub reserved_names: RwLock<HashSet<String>>,
pub tx_broadcast: broadcast::Sender<Message>,
pub tx_game_handler: mpsc::Sender<GameHandlerMessage>,
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 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::<String, Vec<String>>::new());
let last_names = load_names("data/last.txt")?;
let offline_users = RwLock::new(HashMap::<String, 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 {
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,

View file

@ -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<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
pub struct UserHandler {
/// Pointer to global state
state: Arc<AppState>,
users_by_id: HashMap<String, Arc<RwLock<User>>>,
first_names: Vec<String>,
last_names: Vec<String>,
reserved_names: HashSet<String>,
}
pub enum DmUserMethod {
@ -24,7 +46,7 @@ pub enum DmUserMethod {
/// For interacting with the user handler
pub enum UserHandlerMessage {
NewUser(User, SocketAddr),
NewUser(Sender<Message>, SocketAddr),
UserLogIn(UserLogInRequest, SocketAddr),
DmUser(SendUserMessage, DmUserMethod),
Cleanup(SocketAddr),
@ -44,15 +66,18 @@ impl UserHandler {
UserHandler {
state,
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
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<Message>, 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();
{

View file

@ -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<AppState>, 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