summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--locale/en.ini11
-rw-r--r--server/src/commands.rs91
-rw-r--r--server/src/message.rs25
-rw-r--r--server/src/state.rs2
4 files changed, 86 insertions, 43 deletions
diff --git a/locale/en.ini b/locale/en.ini
index 9f54861e..b92dbe29 100644
--- a/locale/en.ini
+++ b/locale/en.ini
@@ -8,9 +8,9 @@ c.credits.title=Hurry Curry! - a game about cooking
c.credits.translations=Translations
c.error.cannot_cancel_no_game=Cannot cancel game since no game is running.
c.error.empty_username=Username cannot be empty.
-c.error.select_hairstyle=You must select a hairstyle.
c.error.must_join_to_cancel=You must join in order to be able to cancel the current game.
c.error.placeholder=This should be the error message.
+c.error.select_hairstyle=You must select a hairstyle.
c.error.server=Error from server: {0}
c.error.version_mismatch=Server and client versions do not match. Server: {0}.{1}, Client: {2}.{3}.%nAre you sure the game is up to date?
c.error.websocket.unavailable=unavailable
@@ -49,6 +49,7 @@ c.menu.lobby.mapname=Map name
c.menu.lobby.players=Players
c.menu.lobby.start=Start game
c.menu.my_chef=My Chef
+c.menu.play.allow_query_registry=To show a public server list a registry
c.menu.play.connect=Connect
c.menu.play.fetching_list=Fetching server list...
c.menu.play.list_item={0} ({1} players)
@@ -65,7 +66,6 @@ c.menu.play.server=Server
c.menu.play=Play
c.menu.quit=Quit
c.menu.settings=Settings
-c.play.allow_query_registry=Do you want to query the global registry for public servers?
c.score.acceptable=Acceptable service
c.score.completed=Completed
c.score.excellent=Excellent service
@@ -202,16 +202,23 @@ s.bot.waiter=Waiter
s.campaign.condition.stars=Reach at least {0} stars in {1}
s.campaign.list_helper=- {0}%n - {1}
s.campaign.unlock_condition=To unlock: %n%n{0}
+s.error.algo_not_found=The algorhythm "{0}" does not exist.
s.error.already_interacting=already interacting
s.error.conn_too_many_players=Players-per-connection limit exceeded.
s.error.customer_interact=You shall not interact with customers.
s.error.interacting_too_far=interacting too far from player
+s.error.item_not_found=The item "{0}" does not exist.
s.error.map_load=Map failed to load: {0}
+s.error.must_be_alone=You must be alone in this server to reload
+s.error.no_info=No information available.
s.error.no_player=Player does not exist.
s.error.no_tile=Tile does not exist.
s.error.packet_not_supported=Packet not supported in this session.
s.error.packet_sender_invalid=Packet sent to a player that is not owned by this connection.
+s.error.quoting_invalid=Comand quoting invalid
s.error.self_interact=Interacting with yourself. This is impossible.
+s.error.tutorial_already_running=Tutorial already running
+s.error.tutorial_no_running=No tutorial running
s.replay.cannot_join=Replays cannot be joined.
s.state.abort_no_players=Game was aborted due to a lack of players.
s.state.game_aborted=Game was aborted by {0}.
diff --git a/server/src/commands.rs b/server/src/commands.rs
index 478d88e3..feb6ab65 100644
--- a/server/src/commands.rs
+++ b/server/src/commands.rs
@@ -17,10 +17,11 @@
*/
use crate::{
entity::{bot::BotDriver, tutorial::Tutorial},
+ message::TrError,
server::Server,
- trm,
+ tre, trm,
};
-use anyhow::{anyhow, bail, Result};
+use anyhow::Result;
use clap::{Parser, ValueEnum};
use hurrycurry_bot::algos::ALGO_CONSTRUCTORS;
use hurrycurry_protocol::{Menu, Message, PacketC, PlayerClass, PlayerID};
@@ -103,16 +104,17 @@ impl Server {
&mut self,
player: PlayerID,
command: &str,
- ) -> Result<Vec<PacketC>> {
+ ) -> Result<Vec<PacketC>, TrError> {
let mut replies = Vec::new();
for line in command.split("\n") {
self.handle_command(
player,
Command::try_parse_from(
shlex::split(line)
- .ok_or(anyhow!("quoting invalid"))?
+ .ok_or(tre!("s.error.quoting_invalid"))?
.into_iter(),
- )?,
+ )
+ .map_err(|e| TrError::Plain(e.to_string()))?,
&mut replies,
)
.await?;
@@ -124,7 +126,7 @@ impl Server {
player: PlayerID,
command: Command,
replies: &mut Vec<PacketC>,
- ) -> Result<()> {
+ ) -> Result<(), TrError> {
match command {
Command::Start { spec, timer } => {
if !self.game.lobby {
@@ -136,7 +138,7 @@ impl Server {
.game
.players
.get(&player)
- .ok_or(anyhow!("player missing"))?
+ .ok_or(tre!("s.error.no_player"))?
.name
.clone()
),
@@ -144,7 +146,11 @@ impl Server {
})
.ok();
}
- let data = self.index.generate(&spec).await?;
+ let data = self
+ .index
+ .generate(&spec)
+ .await
+ .map_err(|e| TrError::Plain(e.to_string()))?;
self.load(data, timer.map(Duration::from_secs));
}
Command::End => {
@@ -156,42 +162,59 @@ impl Server {
.game
.players
.get(&player)
- .ok_or(anyhow!("player missing"))?
+ .ok_or(tre!("s.error.no_player"))?
.name
.clone()
),
error: false,
})
.ok();
- self.load(self.index.generate("lobby").await?, None);
+ self.load(
+ self.index
+ .generate("lobby")
+ .await
+ .map_err(|e| TrError::Plain(e.to_string()))?,
+ None,
+ );
}
Command::Reload => {
if self.count_chefs() > 1 {
- bail!("must be at most one player to reload");
+ return Err(tre!("s.error.must_be_alone"));
}
self.load(
- self.index.generate(&self.game.data.current_map).await?,
+ self.index
+ .generate(&self.game.data.current_map)
+ .await
+ .map_err(|e| TrError::Plain(e.to_string()))?,
None,
);
}
Command::ReloadIndex => {
- self.index.reload().await?;
+ self.index
+ .reload()
+ .await
+ .map_err(|e| TrError::Plain(e.to_string()))?;
}
Command::Book => replies.push(PacketC::Menu(Menu::Book)),
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}");
+ }
+ .map_err(|e| TrError::Plain(e.to_string()))?;
+ replies.push(PacketC::ServerMessage {
+ message: Message::Text(source),
+ error: false,
+ });
}
- Command::List => {
- bail!(
+ Command::List => replies.push(PacketC::ServerMessage {
+ message: Message::Text(format!(
"Maps: {:?}\nRecipes: {:?}",
self.index.maps.keys().collect::<Vec<_>>(),
self.index.recipes
- )
- }
+ )),
+ error: false,
+ }),
Command::Effect { name } => {
self.tx.send(PacketC::Effect { name, player }).ok();
}
@@ -200,7 +223,7 @@ impl Server {
.game
.data
.get_item_by_name(&name)
- .ok_or(anyhow!("unknown item"))?;
+ .ok_or(tre!("s.error.item_not_found", s = name))?;
self.tx
.send(PacketC::Communicate {
player,
@@ -213,7 +236,7 @@ impl Server {
let (aname, cons) = ALGO_CONSTRUCTORS
.iter()
.find(|(name, _)| *name == algo.as_str())
- .ok_or(anyhow!("algo name unknown"))?;
+ .ok_or(tre!("s.error.algo_not_found", s = algo))?;
let algo = cons();
self.entities.push(Box::new(BotDriver::new(
format!("{}-bot", name.unwrap_or((*aname).to_owned())),
@@ -235,7 +258,10 @@ impl Server {
)
.unwrap();
}
- bail!("{o}");
+ replies.push(PacketC::ServerMessage {
+ message: Message::Text(o),
+ error: false,
+ });
}
}
Command::Info { map } => {
@@ -244,20 +270,21 @@ impl Server {
.index
.maps
.get(mapname)
- .ok_or(anyhow!("no information available"))?;
- bail!(
- "{:?}\nRecommended player count: {}\nDifficulty: {}",
- info.name,
- info.difficulty,
- info.players
- )
+ .ok_or(tre!("s.error.no_info"))?;
+ replies.push(PacketC::ServerMessage {
+ message: Message::Text(format!(
+ "{:?}\nRecommended player count: {}\nDifficulty: {}",
+ info.name, info.difficulty, info.players
+ )),
+ error: false,
+ });
}
Command::StartTutorial { item } => {
let item = self
.game
.data
.get_item_by_name(&item)
- .ok_or(anyhow!("unknown item"))?;
+ .ok_or(tre!("s.error.item_not_found", s = item))?;
#[cfg(not(test))] // TODO rust-analyser does not undestand trait upcasting
if self
.entities
@@ -268,7 +295,7 @@ impl Server {
})
.is_some()
{
- bail!("Tutorial already running")
+ return Err(tre!("s.error.tutorial_already_running"));
}
self.entities.push(Box::new(Tutorial::new(player, item)));
}
@@ -281,7 +308,7 @@ impl Server {
{
tutorial.ended = true;
} else {
- bail!("No tutorial running")
+ return Err(tre!("s.error.tutorial_no_running"));
}
}
Command::TranslateMessage {
diff --git a/server/src/message.rs b/server/src/message.rs
index c248fff8..79c004a2 100644
--- a/server/src/message.rs
+++ b/server/src/message.rs
@@ -46,28 +46,37 @@ macro_rules! trm_param {
};
}
-pub struct TrError {
- pub id: &'static str,
- pub params: Vec<Message>,
+pub enum TrError {
+ Tr {
+ id: &'static str,
+ params: Vec<Message>,
+ },
+ Plain(String),
}
impl From<TrError> for Message {
fn from(value: TrError) -> Self {
- Self::Translation {
- id: value.id.to_owned(),
- params: value.params,
+ match value {
+ TrError::Tr { id, params } => Self::Translation {
+ id: id.to_owned(),
+ params,
+ },
+ TrError::Plain(s) => Self::Text(s),
}
}
}
impl Debug for TrError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- write!(f, "{} {:?}", tr(self.id), self.params)
+ match self {
+ TrError::Tr { id, params } => write!(f, "{} {:?}", tr(id), params),
+ TrError::Plain(s) => write!(f, "{s}"),
+ }
}
}
#[macro_export]
macro_rules! tre {
($id:literal $(, $typ:ident = $param:expr)*) => {
- crate::message::TrError {
+ crate::message::TrError::Tr {
id: $id,
params: vec![$(crate::tre_param!($typ, $param)),*]
}
diff --git a/server/src/state.rs b/server/src/state.rs
index 068b45a8..20e2bf80 100644
--- a/server/src/state.rs
+++ b/server/src/state.rs
@@ -59,7 +59,7 @@ impl Server {
Ok(packets) => return Ok(packets),
Err(e) => {
return Ok(vec![PacketC::ServerMessage {
- message: Message::Text(format!("{e}")), // TODO localize
+ message: e.into(),
error: true,
}]);
}