use std::{ error::Error, net::SocketAddr, sync::Arc, }; use warp::Filter; mod types; mod conn; mod minesweeper; use types::*; use tokio::sync::RwLock; const FONT_FILE: &[u8] = include_bytes!("./VT323-Regular.ttf"); fn main() -> Result<(), Box> { let conf = Config { cert_path: "./cert.pem".to_owned(), pkey_path: "./cert.rsa".to_owned(), page_path: "./page.html".to_owned(), socket_addr: ([0,0,0,0],31235).into(), }; tokio_main(conf) } #[tokio::main] async fn tokio_main(conf: Config) -> Result<(), Box> { // Start the temporary single room let room = Arc::new(RwLock::new({ let name = "Testing room".to_string(); let players = PlayerMap::default(); let bconf = BoardConf { w: 75, h: 35, mine_ratio: (1, 8) }; let (cmd_tx, cmd_rx) = tokio::sync::mpsc::unbounded_channel(); let handle = tokio::spawn(gameloop(cmd_rx, players.clone(), bconf)); Room { name, players, peer_limit: 32, board_conf: bconf, cmd_stream: cmd_tx, driver: handle, } })); let page_route = { use warp::*; get() .and(path("font.ttf")) .map(|| FONT_FILE) .or(fs::file(conf.page_path.clone())) }; let websocket_route = { let room = room.clone(); use warp::*; path("ws") .and(ws()) .and(addr::remote()) .map(move |ws: warp::ws::Ws, saddr: Option| { let room = room.clone(); println!("conn from {saddr:?}"); ws.on_upgrade(move |socket| { conn::lobby(socket, saddr.expect("socket without address"), room.clone()) }) }) }; let routes = websocket_route.or(page_route); let server = warp::serve(routes) .tls() .cert_path(conf.cert_path) .key_path(conf.pkey_path) .run(conf.socket_addr); println!("Serving on {}", conf.socket_addr); server.await; Ok(()) } // If a move is made, broadcast new board, else just send current board async fn gameloop(mut move_rx: tokio::sync::mpsc::UnboundedReceiver, players: PlayerMapData, bconf: BoardConf) { use minesweeper::*; let mine_cnt = (bconf.w * bconf.h * bconf.mine_ratio.0)/(bconf.mine_ratio.1); let mut game = Game::new(Board::new(bconf.w, bconf.h), mine_cnt); let mut latest_player_name = None; while let Some(req) = move_rx.recv().await { let done = game.phase == Phase::Die || game.phase == Phase::Win; match req { MetaMove::Move(m, o) => if !done { game = game.act(m); if game.phase == Phase::Win || game.phase == Phase::Die { game.board = game.board.grade(); } latest_player_name = players.read().await.get(&o).map(|p| p.name.clone()); }, MetaMove::Dump => (), MetaMove::Reset => { game = Game::new(Board::new(bconf.w, bconf.h), mine_cnt); }, } use warp::ws::Message; let mut reply = vec![Message::binary(game.board.render())]; let lpname = latest_player_name.as_ref().map(|s| s.as_str()).unwrap_or("unknown player"); match game.phase { Phase::Win => { reply.push(Message::text(format!("win {lpname}"))); }, Phase::Die => { reply.push(Message::text(format!("lose {lpname}"))); }, _ => (), } { let peers = players.read().await; for (addr, p) in peers.iter() { for r in reply.iter() { if let Err(e) = p.conn.tx.send(r.clone()) { println!("couldn't send game update {r:?} to {addr}: {e}"); } } } } } }