aboutsummaryrefslogtreecommitdiff
path: root/server/src/commands.rs
diff options
context:
space:
mode:
Diffstat (limited to 'server/src/commands.rs')
-rw-r--r--server/src/commands.rs168
1 files changed, 168 insertions, 0 deletions
diff --git a/server/src/commands.rs b/server/src/commands.rs
new file mode 100644
index 00000000..e2d40f05
--- /dev/null
+++ b/server/src/commands.rs
@@ -0,0 +1,168 @@
+/*
+ Hurry Curry! - a game about cooking
+ Copyright 2024 metamuffin
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, version 3 of the License only.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+*/
+use crate::{entity::bot::BotDriver, server::Server};
+use anyhow::{anyhow, bail, Result};
+use clap::{Parser, ValueEnum};
+use hurrycurry_bot::algos::ALGO_CONSTRUCTORS;
+use hurrycurry_protocol::{Message, PacketC, PlayerID};
+use std::time::Duration;
+
+#[derive(Parser)]
+#[clap(multicall = true)]
+enum Command {
+ /// Start a new game
+ Start {
+ /// Gamedata specification
+ #[arg(default_value = "junior")]
+ spec: String,
+ /// Duration in seconds
+ #[arg(default_value = "420")]
+ timer: u64,
+ },
+ /// Abort the current game
+ End,
+ /// Download recipe/map's source declaration
+ Download {
+ /// Resource kind
+ #[arg(value_enum)]
+ r#type: DownloadType,
+ /// Name
+ name: String,
+ },
+ /// List all recipes and maps
+ List,
+ /// Send an effect
+ Effect { name: String },
+ /// Send an item
+ Item { name: String },
+ /// Reload the resource index
+ ReloadIndex,
+ #[clap(alias = "summon-bot", alias = "spawn-bot")]
+ CreateBot { algo: String, name: Option<String> },
+ /// Reload the current map
+ #[clap(alias = "r")]
+ Reload,
+}
+
+#[derive(ValueEnum, Clone)]
+enum DownloadType {
+ Map,
+ Recipes,
+}
+
+impl Server {
+ pub async fn handle_command_parse(&mut self, player: PlayerID, command: &str) -> Result<()> {
+ for line in command.split("\n") {
+ self.handle_command(
+ player,
+ Command::try_parse_from(
+ shlex::split(line)
+ .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).await?;
+ self.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.load(self.index.generate("lobby").await?, None);
+ }
+ Command::Reload => {
+ if self.count_chefs() > 1 {
+ bail!("must be at most one player to reload");
+ }
+ self.load(
+ self.index.generate(&self.game.data.current_map).await?,
+ None,
+ );
+ }
+ Command::ReloadIndex => {
+ self.index.reload().await?;
+ }
+ Command::Download { r#type, name } => {
+ let source = match r#type {
+ DownloadType::Map => self.index.read_map(&name).await,
+ DownloadType::Recipes => self.index.read_recipes(&name).await,
+ }?;
+ bail!("{source}");
+ }
+ Command::List => {
+ bail!(
+ "Maps: {:?}\nRecipes: {:?}",
+ self.index.maps.keys().collect::<Vec<_>>(),
+ self.index.recipes
+ )
+ }
+ Command::Effect { name } => {
+ self.tx
+ .send(PacketC::Communicate {
+ player,
+ message: Some(Message::Effect(name)),
+ timeout: None,
+ })
+ .ok();
+ }
+ Command::Item { name } => {
+ let item = self
+ .game
+ .data
+ .get_item_by_name(&name)
+ .ok_or(anyhow!("unknown item"))?;
+ self.tx
+ .send(PacketC::Communicate {
+ player,
+ message: Some(Message::Item(item)),
+ timeout: None,
+ })
+ .ok();
+ }
+ Command::CreateBot { algo, name } => {
+ let (aname, cons) = ALGO_CONSTRUCTORS
+ .iter()
+ .find(|(name, _)| *name == algo.as_str())
+ .ok_or(anyhow!("algo name unknown"))?;
+ let algo = cons();
+ self.entities.push(Box::new(BotDriver::new(
+ format!("{}-bot", name.unwrap_or((*aname).to_owned())),
+ 51,
+ algo,
+ )));
+ }
+ }
+ Ok(())
+ }
+}