use crate::meta::*; use crate::user::*; use crate::websocket::*; use anyhow::{Context, Result}; use axum::extract::ws::Message; use axum::{routing::get, Router}; use server::*; use std::{ collections::{HashMap, HashSet}, net::SocketAddr, sync::{Arc, RwLock}, }; use tokio::sync::{broadcast, mpsc}; use tower_http::services::ServeDir; use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; #[tokio::main] async fn main() -> Result<()> { // stuff for logging tracing_subscriber::registry() .with( tracing_subscriber::EnvFilter::try_from_default_env() .unwrap_or_else(|_| "server=trace,tower_http=trace,lib=trace".into()), ) .with(tracing_subscriber::fmt::layer()) .init(); // Set up state let (tx, _rx) = broadcast::channel(100); let (users_tx, mut users_rx) = mpsc::channel(100); let first_names = load_names("data/first.txt")?; let last_names = load_names("data/last.txt")?; let reserved_names = RwLock::new(HashSet::::new()); let online_users = RwLock::new(HashMap::>>::new()); let offline_users = RwLock::new(HashMap::>>::new()); let (packs, packs_meta) = load_cards_from_json("data/cah-cards-full.json")?; let games = RwLock::new(HashMap::new()); let app_state = Arc::new(AppState { tx, users_tx, first_names, last_names, reserved_names, online_users, offline_users, packs, packs_meta, games, }); let cloned_state = app_state.clone(); let _user_handler = tokio::spawn(async move { while let Some(message) = users_rx.recv().await { // // Create, Register, and Hydrate new user // // Create let new_user = Arc::new(RwLock::new(User::new(&cloned_state))); // Notify client of new username message .sender .send(Message::Text(user_client_self_update(&new_user))) .await .unwrap(); // Register using `addr` as key until something longer lived exists cloned_state .online_users .write() .unwrap() .insert(message.addr, new_user.clone()); // Hydrate client // this should probably be combined and sent as one message .sender .send(Message::Text(meta_chat_update(&cloned_state))) .await .unwrap(); message .sender .send(Message::Text(meta_motd())) .await .unwrap(); message .sender .send(Message::Text(meta_server_summary_update(&cloned_state))) .await .unwrap(); message .sender .send(Message::Text(meta_games_browser_update(&cloned_state))) .await .unwrap(); message .sender .send(Message::Text(meta_new_game_card_packs(&cloned_state))) .await .unwrap(); // Broadcast new user's existence // this should probably be combined and sent as one let _ = &cloned_state .tx .send(meta_announce_user_join(&cloned_state, &message.addr)) .unwrap(); let _ = &cloned_state .tx .send(meta_server_summary_update(&cloned_state)) .unwrap(); let _ = &cloned_state .tx .send(meta_chat_update(&cloned_state)) .unwrap(); } }); // Router let app = Router::new() .route("/websocket", get(websocket_connection_handler)) .nest_service("/", ServeDir::new("dist")) .with_state(app_state); // send it let address = "0.0.0.0:3030"; let listener = tokio::net::TcpListener::bind(address) .await .with_context(|| format!("{} is not a valid bind address.", address))?; tracing::info!("listening on {}", listener.local_addr()?); axum::serve( listener, app.into_make_service_with_connect_info::(), ) .await?; Ok(()) }