cards/server/src/main.rs
2024-07-21 00:52:47 -04:00

153 lines
4.4 KiB
Rust

use anyhow::{Context, Result};
use axum::{response::Html, routing::get, Router};
use axum_extra::response::Css;
use std::{
// collections::HashSet,
fs,
net::SocketAddr,
sync::{Arc, Mutex},
};
use tokio::sync::broadcast;
use tower_http::services::ServeDir;
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
use libcards::*;
pub mod api;
use crate::api::*;
/// Parse json for card data
fn load_json(path: &str) -> Result<Vec<CAHCardSet>> {
let data: String =
fs::read_to_string(path).with_context(|| format!("Invalid JSON path: \"{}\"", path))?;
let jayson: Vec<CAHCardSet> =
serde_json::from_str(&data).with_context(|| format!("\"{path}\" is invalid json"))?;
Ok(jayson)
}
// this is still around just for reference
#[allow(dead_code)]
fn test() -> Result<()> {
// choose decks
let cards_input_path: &str = "../data/cah-cards-full.json";
// TODO: this should be a master card database and pointers
// to the cards should be passed to the game instead of actual cards
let chosen_packs: Vec<CAHCardSet> = load_json(cards_input_path)?;
println!("{}", &chosen_packs.len());
let test_player0 = CAHPlayer {
name: "Adam".to_string(),
role: PlayerRole::Host,
white: vec![],
black: vec![],
};
let test_player1 = CAHPlayer {
name: "Ferris".to_string(),
role: PlayerRole::Player,
white: vec![],
black: vec![],
};
// make some games
// use hashmap?
let mut games: Vec<CAHGame> = vec![];
// create game with/for player 0
let test_game0 = NewGameRequest {
name: "Test0".to_string(),
host: test_player0,
// packs: chosen_packs,
packs: vec![0],
};
games.push(CAHGame::new(test_game0)?);
// a new game request struct but this player is a player
games[0].create_player(test_player1)?;
// start round
games[0].game_start()?;
println!("----------------------");
for card in &games[0].players[0].white {
println!("{}", card.text);
}
Ok(())
}
// Our shared state
pub struct AppState {
// We require unique usernames. This tracks which usernames have been taken.
// user_set: Mutex<HashSet<String>>,
// Channel used to send messages to all connected clients.
tx: broadcast::Sender<String>,
// Master card decks
all_cards: Mutex<Vec<CAHCardSet>>,
// Games list
games: Mutex<Vec<CAHGame>>,
}
// Include utf-8 files at **compile** time.
async fn test_client() -> Html<&'static str> {
Html(std::include_str!("../public/test_client.html"))
}
async fn spawn_clients() -> Html<&'static str> {
Html(std::include_str!("../public/spawn_clients.html"))
}
async fn reference_client() -> Html<&'static str> {
Html(std::include_str!("../public/reference_client.html"))
}
async fn css() -> Css<&'static str> {
Css(std::include_str!("../public/main.css"))
}
#[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".into()),
)
.with(tracing_subscriber::fmt::layer())
.init();
// Set up application state for use with with_state().
// let user_set = Mutex::new(HashSet::new());
let (tx, _rx) = broadcast::channel(100);
let cards_input_path: &str = "data/cah-cards-full.json";
let all_cards = Mutex::new(load_json(cards_input_path)?);
let games = Mutex::new(vec![]);
let app_state = Arc::new(AppState {
// user_set,
tx,
all_cards,
games,
});
// set routes and apply state
let app = Router::new()
.route("/spawn_clients", get(spawn_clients))
.route("/test_client", get(test_client))
.route("/reference_client", get(reference_client))
.route("/websocket", get(websocket_handler))
.route("/css", get(css))
.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::debug!("listening on {}", listener.local_addr()?);
axum::serve(
listener,
app.into_make_service_with_connect_info::<SocketAddr>(),
)
.await?;
Ok(())
}