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 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)>,
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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();
|
||||||
{
|
{
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Add table
Reference in a new issue