2024-05-05 05:32:16 -04:00
|
|
|
use anyhow::{Context, Result};
|
2024-05-04 02:23:40 -04:00
|
|
|
use axum::{response::Html, routing::get, Router};
|
2024-07-18 04:00:56 -04:00
|
|
|
use axum_extra::response::Css;
|
2024-07-21 02:35:13 -04:00
|
|
|
use lib::models::*;
|
2024-04-28 04:53:00 -04:00
|
|
|
use std::{
|
2024-05-04 02:23:40 -04:00
|
|
|
// collections::HashSet,
|
|
|
|
fs,
|
2024-05-03 23:17:39 -04:00
|
|
|
net::SocketAddr,
|
2024-04-28 04:53:00 -04:00
|
|
|
sync::{Arc, Mutex},
|
|
|
|
};
|
|
|
|
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-05-05 05:32:16 -04:00
|
|
|
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"))?;
|
2024-04-05 22:38:41 -04:00
|
|
|
|
|
|
|
Ok(jayson)
|
|
|
|
}
|
|
|
|
|
2024-05-04 21:28:42 -04:00
|
|
|
// this is still around just for reference
|
2024-07-21 01:08:54 -04:00
|
|
|
fn _test() -> Result<()> {
|
2024-04-06 22:38:00 -04:00
|
|
|
// choose decks
|
2024-07-18 04:00:56 -04:00
|
|
|
let cards_input_path: &str = "../data/cah-cards-full.json";
|
2024-04-12 18:23:33 -04:00
|
|
|
|
|
|
|
// TODO: this should be a master card database and pointers
|
|
|
|
// to the cards should be passed to the game instead of actual cards
|
2024-04-12 02:04:58 -04:00
|
|
|
let chosen_packs: Vec<CAHCardSet> = load_json(cards_input_path)?;
|
2024-04-12 18:23:33 -04:00
|
|
|
println!("{}", &chosen_packs.len());
|
2024-04-05 22:38:41 -04:00
|
|
|
|
2024-04-12 02:04:58 -04:00
|
|
|
let test_player0 = CAHPlayer {
|
2024-04-30 02:28:43 -04:00
|
|
|
name: "Adam".to_string(),
|
2024-04-12 18:35:13 -04:00
|
|
|
role: PlayerRole::Host,
|
|
|
|
white: vec![],
|
|
|
|
black: vec![],
|
|
|
|
};
|
2024-04-12 02:04:58 -04:00
|
|
|
|
|
|
|
let test_player1 = CAHPlayer {
|
2024-04-30 02:28:43 -04:00
|
|
|
name: "Ferris".to_string(),
|
2024-04-12 18:35:13 -04:00
|
|
|
role: PlayerRole::Player,
|
|
|
|
white: vec![],
|
|
|
|
black: vec![],
|
|
|
|
};
|
2024-04-12 02:04:58 -04:00
|
|
|
|
|
|
|
// make some games
|
2024-04-12 18:23:33 -04:00
|
|
|
// use hashmap?
|
2024-04-12 02:04:58 -04:00
|
|
|
let mut games: Vec<CAHGame> = vec![];
|
|
|
|
|
|
|
|
// create game with/for player 0
|
2024-04-12 18:35:13 -04:00
|
|
|
let test_game0 = NewGameRequest {
|
|
|
|
name: "Test0".to_string(),
|
|
|
|
host: test_player0,
|
2024-05-01 04:56:58 -04:00
|
|
|
// packs: chosen_packs,
|
|
|
|
packs: vec![0],
|
2024-04-12 18:35:13 -04:00
|
|
|
};
|
|
|
|
|
2024-04-13 21:04:42 -04:00
|
|
|
games.push(CAHGame::new(test_game0)?);
|
2024-04-12 02:04:58 -04:00
|
|
|
|
2024-04-12 18:23:33 -04:00
|
|
|
// a new game request struct but this player is a player
|
|
|
|
games[0].create_player(test_player1)?;
|
2024-04-10 04:18:31 -04:00
|
|
|
|
2024-04-12 02:35:35 -04:00
|
|
|
// start round
|
2024-04-12 18:23:33 -04:00
|
|
|
games[0].game_start()?;
|
2024-04-13 21:04:42 -04:00
|
|
|
|
2024-04-24 02:24:10 -04:00
|
|
|
println!("----------------------");
|
|
|
|
for card in &games[0].players[0].white {
|
|
|
|
println!("{}", card.text);
|
2024-04-27 01:03:20 -04:00
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
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-05-04 02:23:40 -04:00
|
|
|
// user_set: Mutex<HashSet<String>>,
|
2024-04-30 02:28:43 -04:00
|
|
|
// 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>>,
|
|
|
|
}
|
|
|
|
|
2024-05-04 03:52:53 -04:00
|
|
|
// Include utf-8 files at **compile** time.
|
2024-06-22 02:16:35 -04:00
|
|
|
async fn test_client() -> Html<&'static str> {
|
2024-05-04 21:28:42 -04:00
|
|
|
Html(std::include_str!("../public/test_client.html"))
|
2024-05-04 02:49:17 -04:00
|
|
|
}
|
2024-06-22 02:16:35 -04:00
|
|
|
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"))
|
2024-05-04 02:49:17 -04:00
|
|
|
}
|
2024-06-23 04:49:32 -04:00
|
|
|
async fn css() -> Css<&'static str> {
|
|
|
|
Css(std::include_str!("../public/main.css"))
|
|
|
|
}
|
2024-05-04 02:49:17 -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-05-04 02:23:40 -04:00
|
|
|
// let user_set = Mutex::new(HashSet::new());
|
2024-04-28 04:53:00 -04:00
|
|
|
let (tx, _rx) = broadcast::channel(100);
|
2024-04-30 02:28:43 -04:00
|
|
|
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 {
|
2024-05-04 02:23:40 -04:00
|
|
|
// user_set,
|
2024-04-30 02:28:43 -04:00
|
|
|
tx,
|
|
|
|
all_cards,
|
|
|
|
games,
|
|
|
|
});
|
|
|
|
|
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-06-22 02:16:35 -04:00
|
|
|
.route("/spawn_clients", get(spawn_clients))
|
|
|
|
.route("/test_client", get(test_client))
|
|
|
|
.route("/reference_client", get(reference_client))
|
2024-04-28 04:53:00 -04:00
|
|
|
.route("/websocket", get(websocket_handler))
|
2024-06-23 04:49:32 -04:00
|
|
|
.route("/css", get(css))
|
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-05-02 01:00:47 -04:00
|
|
|
tracing::debug!("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
|
|
|
}
|