let user handler handle name stuff
This commit is contained in:
parent
3900ec55d4
commit
5695d2919d
4 changed files with 57 additions and 68 deletions
|
@ -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)>,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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();
|
||||
{
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Add table
Reference in a new issue