use std::time::Duration; use crate::{ data::DataIndex, game::Game, protocol::{Message, PacketC, PacketS, PlayerID}, }; use anyhow::{anyhow, bail, Result}; use clap::Parser; use log::debug; use tokio::sync::broadcast::Sender; pub struct State { index: DataIndex, tx: Sender, pub game: Game, } #[derive(Parser)] #[clap(multicall = true)] enum Command { Start { #[arg(default_value = "small-default-default")] spec: String, #[arg(default_value = "420")] timer: u64, }, List, Effect { name: String, }, Reload, End, } impl State { pub fn new(tx: Sender) -> Result { let mut index = DataIndex::default(); index.reload()?; let mut game = Game::new(); game.load(index.generate("lobby-none-none".to_string())?, None); Ok(Self { game, index, tx }) } pub async fn tick(&mut self, dt: f32) -> anyhow::Result<()> { if self.game.tick(dt) { self.tx .send(PacketC::ServerMessage { text: format!("Game finished. You reached {} points.", self.game.points), }) .ok(); self.game .load(self.index.generate("lobby-none-none".to_string())?, None); } while let Some(p) = self.game.packet_out() { debug!("-> {p:?}"); let _ = self.tx.send(p); } Ok(()) } pub async fn packet_in(&mut self, player: PlayerID, packet: PacketS) -> Result> { match &packet { PacketS::Communicate { message: Some(Message::Text(text)), persist: false, } if let Some(command) = text.strip_prefix("/") => { match self.handle_command_parse(player, command).await { Ok(()) => return Ok(vec![]), Err(e) => { return Ok(vec![PacketC::ServerMessage { text: format!("{e}"), }]); } } } _ => (), } self.game.packet_in(player, packet)?; Ok(vec![]) } async fn handle_command_parse(&mut self, player: PlayerID, command: &str) -> Result<()> { self.handle_command( player, Command::try_parse_from( shlex::split(command) .ok_or(anyhow!("quoting invalid"))? .into_iter(), )?, ) .await?; Ok(()) } async fn handle_command(&mut self, player: PlayerID, command: Command) -> Result<()> { match command { Command::Start { spec, timer } => { let data = self.index.generate(spec)?; self.game.load(data, Some(Duration::from_secs(timer))); } Command::End => { self.tx .send(PacketC::ServerMessage { text: format!( "Game was aborted by {}.", self.game .players .get(&player) .ok_or(anyhow!("player missing"))? .name ), }) .ok(); self.game .load(self.index.generate("lobby-none-none".to_string())?, None); } Command::Reload => { self.index.reload()?; } Command::List => { bail!( "Maps: {:?}\nDemands: {:?}\nRecipes: {:?}", self.index.maps, self.index.demands, self.index.recipes ) } Command::Effect { name } => { self.tx .send(PacketC::Communicate { player, message: Some(Message::Effect(name)), persist: false, }) .ok(); } } Ok(()) } }