2024-07-30 03:22:32 -04:00
|
|
|
#![feature(if_let_guard)]
|
|
|
|
|
2024-05-05 05:32:16 -04:00
|
|
|
use anyhow::{Context, Result};
|
2024-07-28 02:50:26 -04:00
|
|
|
use axum::{routing::get, Router};
|
2024-07-21 02:35:13 -04:00
|
|
|
use lib::models::*;
|
2024-04-28 04:53:00 -04:00
|
|
|
use std::{
|
2024-07-27 02:51:04 -04:00
|
|
|
collections::HashMap,
|
2024-07-27 23:23:26 -04:00
|
|
|
fs::{read_to_string, File},
|
|
|
|
io::{BufRead, BufReader},
|
2024-05-03 23:17:39 -04:00
|
|
|
net::SocketAddr,
|
2024-08-03 00:42:32 -04:00
|
|
|
sync::{Arc, RwLock},
|
2024-04-28 04:53:00 -04:00
|
|
|
};
|
|
|
|
use tokio::sync::broadcast;
|
2024-07-21 00:52:47 -04:00
|
|
|
use tower_http::services::ServeDir;
|
2024-04-28 04:53:00 -04:00
|
|
|
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
|
2024-04-27 18:22:35 -04:00
|
|
|
pub mod api;
|
|
|
|
use crate::api::*;
|
|
|
|
|
2024-04-06 22:38:00 -04:00
|
|
|
/// Parse json for card data
|
2024-08-04 03:13:34 -04:00
|
|
|
fn load_cards_from_json(path: &str) -> Result<(CardPacks, CardPacksMeta)> {
|
2024-05-05 05:32:16 -04:00
|
|
|
let data: String =
|
2024-07-27 23:23:26 -04:00
|
|
|
read_to_string(path).with_context(|| format!("Invalid JSON path: \"{}\"", path))?;
|
2024-08-04 03:13:34 -04:00
|
|
|
let jayson: Vec<CardPack> = serde_json::from_str(&data)
|
|
|
|
.with_context(|| format!("The contents of \"{path}\" is not valid JSON."))?;
|
2024-04-05 22:38:41 -04:00
|
|
|
|
2024-08-04 03:13:34 -04:00
|
|
|
let mut official: Vec<CardPack> = vec![];
|
|
|
|
let mut unofficial: Vec<CardPack> = vec![];
|
2024-08-03 00:38:01 -04:00
|
|
|
|
2024-08-04 03:13:34 -04:00
|
|
|
let mut official_meta: Vec<CardPackMeta> = vec![];
|
2024-08-03 00:38:01 -04:00
|
|
|
let mut unofficial_meta: Vec<CardPackMeta> = vec![];
|
|
|
|
|
|
|
|
for set in jayson {
|
|
|
|
let mut num_white = 0;
|
|
|
|
let mut num_black = 0;
|
|
|
|
let mut pack: Option<u8> = Option::None;
|
|
|
|
|
|
|
|
if let Some(white) = set.white {
|
|
|
|
num_white = white.len();
|
|
|
|
if num_white > 0 {
|
|
|
|
pack = Some(white[0].pack)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if let Some(black) = set.black {
|
|
|
|
num_black = black.len();
|
|
|
|
if num_black > 0 {
|
|
|
|
pack = Some(black[0].pack)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
let meta = CardPackMeta {
|
|
|
|
name: set.name,
|
|
|
|
pack: pack.expect("No card pack number!"),
|
|
|
|
num_white,
|
|
|
|
num_black,
|
|
|
|
};
|
|
|
|
|
|
|
|
if set.official {
|
|
|
|
official_meta.push(meta);
|
|
|
|
} else {
|
|
|
|
unofficial_meta.push(meta);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
tracing::debug!("{} official", official.len());
|
|
|
|
tracing::debug!("{} official meta", official_meta.len());
|
|
|
|
tracing::debug!("{} unofficial", unofficial.len());
|
|
|
|
tracing::debug!("{} unofficial meta", unofficial_meta.len());
|
|
|
|
tracing::debug!("{:#?}", official_meta[0]);
|
|
|
|
tracing::debug!("{:#?}", unofficial_meta[0]);
|
|
|
|
|
|
|
|
official.shrink_to_fit();
|
|
|
|
unofficial.shrink_to_fit();
|
2024-08-04 03:13:34 -04:00
|
|
|
|
|
|
|
let packs = CardPacks {
|
|
|
|
official,
|
|
|
|
unofficial,
|
|
|
|
};
|
|
|
|
|
|
|
|
official_meta.shrink_to_fit();
|
2024-08-03 00:38:01 -04:00
|
|
|
unofficial_meta.shrink_to_fit();
|
|
|
|
|
2024-08-04 03:13:34 -04:00
|
|
|
let packs_meta = CardPacksMeta {
|
|
|
|
official_meta,
|
|
|
|
unofficial_meta,
|
|
|
|
};
|
|
|
|
|
|
|
|
Ok((packs, packs_meta))
|
2024-04-05 22:38:41 -04:00
|
|
|
}
|
|
|
|
|
2024-07-29 00:34:17 -04:00
|
|
|
/// Parse name list
|
|
|
|
fn load_names(path: &str) -> Result<Vec<String>> {
|
|
|
|
let f = File::open(path).with_context(|| format!("Invalid names path: \"{}\"", path))?;
|
2024-07-27 23:23:26 -04:00
|
|
|
let f = BufReader::new(f);
|
|
|
|
|
|
|
|
let mut buf = vec![];
|
|
|
|
|
|
|
|
for line in f.lines() {
|
2024-07-29 00:34:17 -04:00
|
|
|
buf.push(line?)
|
2024-07-27 23:23:26 -04:00
|
|
|
}
|
|
|
|
|
2024-07-29 00:34:17 -04:00
|
|
|
Ok(buf)
|
2024-04-27 01:03:20 -04:00
|
|
|
}
|
|
|
|
|
2024-04-30 02:28:43 -04:00
|
|
|
// Our shared state
|
|
|
|
pub struct AppState {
|
|
|
|
// We require unique usernames. This tracks which usernames have been taken.
|
2024-08-03 00:42:32 -04:00
|
|
|
online_users: RwLock<HashMap<SocketAddr, Arc<RwLock<User>>>>,
|
|
|
|
offline_users: RwLock<HashMap<String, Arc<RwLock<User>>>>,
|
2024-04-30 02:28:43 -04:00
|
|
|
// Channel used to send messages to all connected clients.
|
|
|
|
tx: broadcast::Sender<String>,
|
|
|
|
// Master card decks
|
2024-08-04 03:13:34 -04:00
|
|
|
packs: CardPacks,
|
|
|
|
packs_meta: CardPacksMeta,
|
2024-04-30 02:28:43 -04:00
|
|
|
// Games list
|
2024-08-03 00:42:32 -04:00
|
|
|
games: RwLock<Vec<Game>>,
|
2024-08-01 23:47:28 -04:00
|
|
|
// chatrooms: Mutex<HashMap<String, ChatRoom<'user>>>,
|
2024-07-27 23:23:26 -04:00
|
|
|
first_names: Vec<String>,
|
|
|
|
last_names: Vec<String>,
|
2024-04-30 02:28:43 -04:00
|
|
|
}
|
|
|
|
|
2024-04-27 02:34:28 -04:00
|
|
|
#[tokio::main]
|
2024-05-05 05:32:16 -04:00
|
|
|
async fn main() -> Result<()> {
|
2024-05-04 03:52:53 -04:00
|
|
|
// stuff for logging
|
2024-04-28 04:53:00 -04:00
|
|
|
tracing_subscriber::registry()
|
|
|
|
.with(
|
|
|
|
tracing_subscriber::EnvFilter::try_from_default_env()
|
2024-07-21 02:35:13 -04:00
|
|
|
.unwrap_or_else(|_| "server=trace,tower_http=trace,lib=trace".into()),
|
2024-04-28 04:53:00 -04:00
|
|
|
)
|
|
|
|
.with(tracing_subscriber::fmt::layer())
|
|
|
|
.init();
|
|
|
|
|
|
|
|
// Set up application state for use with with_state().
|
2024-08-01 23:47:28 -04:00
|
|
|
// Main Broadcast Channel
|
2024-04-28 04:53:00 -04:00
|
|
|
let (tx, _rx) = broadcast::channel(100);
|
2024-08-03 00:42:32 -04:00
|
|
|
let online_users = RwLock::new(HashMap::<SocketAddr, Arc<RwLock<User>>>::new());
|
|
|
|
let offline_users = RwLock::new(HashMap::<String, Arc<RwLock<User>>>::new());
|
2024-08-04 03:13:34 -04:00
|
|
|
let (packs, packs_meta) = load_cards_from_json("data/cah-cards-full.json")?;
|
2024-08-03 00:42:32 -04:00
|
|
|
let games = RwLock::new(vec![]);
|
2024-07-29 00:34:17 -04:00
|
|
|
let first_names = load_names("data/first.txt")?;
|
|
|
|
let last_names = load_names("data/last.txt")?;
|
|
|
|
|
2024-04-30 02:28:43 -04:00
|
|
|
let app_state = Arc::new(AppState {
|
2024-07-31 21:19:29 -04:00
|
|
|
online_users,
|
|
|
|
offline_users,
|
2024-04-30 02:28:43 -04:00
|
|
|
tx,
|
2024-08-04 03:13:34 -04:00
|
|
|
packs,
|
|
|
|
packs_meta,
|
2024-04-30 02:28:43 -04:00
|
|
|
games,
|
2024-07-27 23:23:26 -04:00
|
|
|
first_names,
|
|
|
|
last_names,
|
2024-04-30 02:28:43 -04:00
|
|
|
});
|
|
|
|
|
2024-05-04 03:52:53 -04:00
|
|
|
// set routes and apply state
|
2024-04-28 04:53:00 -04:00
|
|
|
let app = Router::new()
|
2024-07-22 01:32:09 -04:00
|
|
|
.route("/websocket", get(websocket_connection_handler))
|
2024-07-20 23:00:19 -04:00
|
|
|
.nest_service("/", ServeDir::new("dist"))
|
2024-04-28 04:53:00 -04:00
|
|
|
.with_state(app_state);
|
|
|
|
|
2024-05-04 03:52:53 -04:00
|
|
|
// send it
|
2024-05-05 05:32:16 -04:00
|
|
|
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))?;
|
2024-08-02 02:41:42 -04:00
|
|
|
tracing::info!("listening on {}", listener.local_addr()?);
|
2024-05-03 23:17:39 -04:00
|
|
|
axum::serve(
|
|
|
|
listener,
|
|
|
|
app.into_make_service_with_connect_info::<SocketAddr>(),
|
|
|
|
)
|
|
|
|
.await?;
|
2024-04-30 02:28:43 -04:00
|
|
|
|
|
|
|
Ok(())
|
2024-04-28 04:53:00 -04:00
|
|
|
}
|