diff -r b06b33cf0a89 -r 747278149393 rust/hedgewars-server/src/protocol/messages.rs --- a/rust/hedgewars-server/src/protocol/messages.rs Wed Jun 23 15:32:48 2021 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,425 +0,0 @@ -use crate::core::types::{GameCfg, HedgehogInfo, ServerVar, TeamInfo, VoteType}; -use std::{convert::From, iter::once, ops}; - -#[derive(PartialEq, Eq, Clone, Debug)] -pub enum HwProtocolMessage { - // common messages - Ping, - Pong, - Quit(Option), - Global(String), - Watch(u32), - ToggleServerRegisteredOnly, - SuperPower, - Info(String), - // anteroom messages - Nick(String), - Proto(u16), - Password(String, String), - Checker(u16, String, String), - // lobby messages - List, - Chat(String), - CreateRoom(String, Option), - JoinRoom(String, Option), - Follow(String), - Rnd(Vec), - Kick(String), - Ban(String, String, u32), - BanIp(String, String, u32), - BanNick(String, String, u32), - BanList, - Unban(String), - SetServerVar(ServerVar), - GetServerVar, - RestartServer, - Stats, - // room messages - Part(Option), - Cfg(GameCfg), - AddTeam(Box), - RemoveTeam(String), - SetHedgehogsNumber(String, u8), - SetTeamColor(String, u8), - ToggleReady, - StartGame, - EngineMessage(String), - RoundFinished, - ToggleRestrictJoin, - ToggleRestrictTeams, - ToggleRegisteredOnly, - RoomName(String), - Delegate(String), - TeamChat(String), - MaxTeams(u8), - Fix, - Unfix, - Greeting(Option), - CallVote(Option), - Vote(bool), - ForceVote(bool), - Save(String, String), - Delete(String), - SaveRoom(String), - LoadRoom(String), -} - -#[derive(Debug, Clone, Copy)] -pub enum ProtocolFlags { - InRoom, - RoomMaster, - Ready, - InGame, - Registered, - Admin, - Contributor, -} - -impl ProtocolFlags { - #[inline] - fn flag_char(&self) -> char { - match self { - ProtocolFlags::InRoom => 'i', - ProtocolFlags::RoomMaster => 'h', - ProtocolFlags::Ready => 'r', - ProtocolFlags::InGame => 'g', - ProtocolFlags::Registered => 'u', - ProtocolFlags::Admin => 'a', - ProtocolFlags::Contributor => 'c', - } - } - - #[inline] - fn format(prefix: char, flags: &[ProtocolFlags]) -> String { - once(prefix) - .chain(flags.iter().map(|f| f.flag_char())) - .collect() - } -} - -#[inline] -pub fn add_flags(flags: &[ProtocolFlags]) -> String { - ProtocolFlags::format('+', flags) -} - -#[inline] -pub fn remove_flags(flags: &[ProtocolFlags]) -> String { - ProtocolFlags::format('-', flags) -} - -#[derive(Debug)] -pub enum HwServerMessage { - Connected(u32), - Redirect(u16), - - Ping, - Pong, - Bye(String), - - Nick(String), - Proto(u16), - AskPassword(String), - ServerAuth(String), - LogonPassed, - - LobbyLeft(String, String), - LobbyJoined(Vec), - ChatMsg { nick: String, msg: String }, - ClientFlags(String, Vec), - Rooms(Vec), - RoomAdd(Vec), - RoomJoined(Vec), - RoomLeft(String, String), - RoomRemove(String), - RoomUpdated(String, Vec), - Joining(String), - TeamAdd(Vec), - TeamRemove(String), - TeamAccepted(String), - TeamColor(String, u8), - HedgehogsNumber(String, u8), - ConfigEntry(String, Vec), - Kicked, - RunGame, - ForwardEngineMessage(Vec), - RoundFinished, - ReplayStart, - - Info(Vec), - ServerMessage(String), - ServerVars(Vec), - Notice(String), - Warning(String), - Error(String), - Unreachable, - - //Deprecated messages - LegacyReady(bool, Vec), -} - -fn special_chat(nick: &str, msg: String) -> HwServerMessage { - HwServerMessage::ChatMsg { - nick: nick.to_string(), - msg, - } -} - -pub fn server_chat(msg: String) -> HwServerMessage { - special_chat("[server]", msg) -} - -pub fn global_chat(msg: String) -> HwServerMessage { - special_chat("(global notice)", msg) -} - -impl ServerVar { - pub fn to_protocol(&self) -> Vec { - use ServerVar::*; - match self { - MOTDNew(s) => vec!["MOTD_NEW".to_string(), s.clone()], - MOTDOld(s) => vec!["MOTD_OLD".to_string(), s.clone()], - LatestProto(n) => vec!["LATEST_PROTO".to_string(), n.to_string()], - } - } -} - -impl VoteType { - pub fn to_protocol(&self) -> Vec { - use VoteType::*; - match self { - Kick(nick) => vec!["KICK".to_string(), nick.clone()], - Map(None) => vec!["MAP".to_string()], - Map(Some(name)) => vec!["MAP".to_string(), name.clone()], - Pause => vec!["PAUSE".to_string()], - NewSeed => vec!["NEWSEED".to_string()], - HedgehogsPerTeam(count) => vec!["HEDGEHOGS".to_string(), count.to_string()], - } - } -} - -impl GameCfg { - pub fn to_protocol(&self) -> (String, Vec) { - use GameCfg::*; - match self { - FeatureSize(s) => ("FEATURE_SIZE".to_string(), vec![s.to_string()]), - MapType(t) => ("MAP".to_string(), vec![t.to_string()]), - MapGenerator(g) => ("MAPGEN".to_string(), vec![g.to_string()]), - MazeSize(s) => ("MAZE_SIZE".to_string(), vec![s.to_string()]), - Seed(s) => ("SEED".to_string(), vec![s.to_string()]), - Template(t) => ("TEMPLATE".to_string(), vec![t.to_string()]), - - Ammo(n, None) => ("AMMO".to_string(), vec![n.to_string()]), - Ammo(n, Some(s)) => ("AMMO".to_string(), vec![n.to_string(), s.to_string()]), - Scheme(n, s) if s.is_empty() => ("SCHEME".to_string(), vec![n.to_string()]), - Scheme(n, s) => ("SCHEME".to_string(), { - let mut v = vec![n.to_string()]; - v.extend(s.clone()); - v - }), - Script(s) => ("SCRIPT".to_string(), vec![s.to_string()]), - Theme(t) => ("THEME".to_string(), vec![t.to_string()]), - DrawnMap(m) => ("DRAWNMAP".to_string(), vec![m.to_string()]), - } - } - - pub fn to_server_msg(&self) -> HwServerMessage { - use self::HwServerMessage::ConfigEntry; - let (name, args) = self.to_protocol(); - HwServerMessage::ConfigEntry(name, args) - } -} - -impl TeamInfo { - pub fn to_protocol(&self) -> Vec { - let mut info = vec![ - self.name.clone(), - self.grave.clone(), - self.fort.clone(), - self.voice_pack.clone(), - self.flag.clone(), - self.owner.clone(), - self.difficulty.to_string(), - ]; - let hogs = self - .hedgehogs - .iter() - .flat_map(|h| once(h.name.clone()).chain(once(h.hat.clone()))); - info.extend(hogs); - info - } -} - -macro_rules! const_braces { - ($e: expr) => { - "{}\n" - }; -} - -macro_rules! msg { - [$($part: expr),*] => { - format!(concat!($(const_braces!($part)),*, "\n"), $($part),*); - }; -} - -impl HwProtocolMessage { - /** Converts the message to a raw `String`, which can be sent over the network. - * - * This is the inverse of the `message` parser. - */ - #[cfg(test)] - pub(crate) fn to_raw_protocol(&self) -> String { - use self::HwProtocolMessage::*; - match self { - Ping => msg!["PING"], - Pong => msg!["PONG"], - Quit(None) => msg!["QUIT"], - Quit(Some(msg)) => msg!["QUIT", msg], - Global(msg) => msg!["CMD", format!("GLOBAL {}", msg)], - Watch(name) => msg!["CMD", format!("WATCH {}", name)], - ToggleServerRegisteredOnly => msg!["CMD", "REGISTERED_ONLY"], - SuperPower => msg!["CMD", "SUPER_POWER"], - Info(info) => msg!["CMD", format!("INFO {}", info)], - Nick(nick) => msg!("NICK", nick), - Proto(version) => msg!["PROTO", version], - Password(p, s) => msg!["PASSWORD", p, s], - Checker(i, n, p) => msg!["CHECKER", i, n, p], - List => msg!["LIST"], - Chat(msg) => msg!["CHAT", msg], - CreateRoom(name, None) => msg!["CREATE_ROOM", name], - CreateRoom(name, Some(password)) => msg!["CREATE_ROOM", name, password], - JoinRoom(name, None) => msg!["JOIN_ROOM", name], - JoinRoom(name, Some(password)) => msg!["JOIN_ROOM", name, password], - Follow(name) => msg!["FOLLOW", name], - Rnd(args) => { - if args.is_empty() { - msg!["CMD", "RND"] - } else { - msg!["CMD", format!("RND {}", args.join(" "))] - } - } - Kick(name) => msg!["KICK", name], - Ban(name, reason, time) => msg!["BAN", name, reason, time], - BanIp(ip, reason, time) => msg!["BAN_IP", ip, reason, time], - BanNick(nick, reason, time) => msg!("BAN_NICK", nick, reason, time), - BanList => msg!["BANLIST"], - Unban(name) => msg!["UNBAN", name], - SetServerVar(var) => construct_message(&["SET_SERVER_VAR"], &var.to_protocol()), - GetServerVar => msg!["GET_SERVER_VAR"], - RestartServer => msg!["CMD", "RESTART_SERVER YES"], - Stats => msg!["CMD", "STATS"], - Part(None) => msg!["PART"], - Part(Some(msg)) => msg!["PART", msg], - Cfg(config) => { - let (name, args) = config.to_protocol(); - msg!["CFG", name, args.join("\n")] - } - AddTeam(info) => msg![ - "ADD_TEAM", - info.name, - info.color, - info.grave, - info.fort, - info.voice_pack, - info.flag, - info.difficulty, - info.hedgehogs - .iter() - .flat_map(|h| [&h.name[..], &h.hat[..]]) - .collect::>() - .join("\n") - ], - RemoveTeam(name) => msg!["REMOVE_TEAM", name], - SetHedgehogsNumber(team, number) => msg!["HH_NUM", team, number], - SetTeamColor(team, color) => msg!["TEAM_COLOR", team, color], - ToggleReady => msg!["TOGGLE_READY"], - StartGame => msg!["START_GAME"], - EngineMessage(msg) => msg!["EM", msg], - RoundFinished => msg!["ROUNDFINISHED"], - ToggleRestrictJoin => msg!["TOGGLE_RESTRICT_JOINS"], - ToggleRestrictTeams => msg!["TOGGLE_RESTRICT_TEAMS"], - ToggleRegisteredOnly => msg!["TOGGLE_REGISTERED_ONLY"], - RoomName(name) => msg!["ROOM_NAME", name], - Delegate(name) => msg!["CMD", format!("DELEGATE {}", name)], - TeamChat(msg) => msg!["TEAMCHAT", msg], - MaxTeams(count) => msg!["CMD", format!("MAXTEAMS {}", count)], - Fix => msg!["CMD", "FIX"], - Unfix => msg!["CMD", "UNFIX"], - Greeting(None) => msg!["CMD", "GREETING"], - Greeting(Some(msg)) => msg!["CMD", format!("GREETING {}", msg)], - CallVote(None) => msg!["CMD", "CALLVOTE"], - CallVote(Some(vote)) => { - msg!["CMD", format!("CALLVOTE {}", &vote.to_protocol().join(" "))] - } - Vote(msg) => msg!["CMD", format!("VOTE {}", if *msg { "YES" } else { "NO" })], - ForceVote(msg) => msg!["CMD", format!("FORCE {}", if *msg { "YES" } else { "NO" })], - Save(name, location) => msg!["CMD", format!("SAVE {} {}", name, location)], - Delete(name) => msg!["CMD", format!("DELETE {}", name)], - SaveRoom(name) => msg!["CMD", format!("SAVEROOM {}", name)], - LoadRoom(name) => msg!["CMD", format!("LOADROOM {}", name)], - _ => panic!("Protocol message not yet implemented"), - } - } -} - -fn construct_message(header: &[&str], msg: &[String]) -> String { - let mut v: Vec<_> = header.iter().cloned().collect(); - v.extend(msg.iter().map(|s| &s[..])); - v.push("\n"); - v.join("\n") -} - -impl HwServerMessage { - pub fn to_raw_protocol(&self) -> String { - use self::HwServerMessage::*; - match self { - Ping => msg!["PING"], - Pong => msg!["PONG"], - Connected(protocol_version) => msg![ - "CONNECTED", - "Hedgewars server https://www.hedgewars.org/", - protocol_version - ], - Redirect(port) => msg!["REDIRECT", port], - Bye(msg) => msg!["BYE", msg], - Nick(nick) => msg!["NICK", nick], - Proto(proto) => msg!["PROTO", proto], - AskPassword(salt) => msg!["ASKPASSWORD", salt], - ServerAuth(hash) => msg!["SERVER_AUTH", hash], - LogonPassed => msg!["LOGONPASSED"], - LobbyLeft(nick, msg) => msg!["LOBBY:LEFT", nick, msg], - LobbyJoined(nicks) => construct_message(&["LOBBY:JOINED"], &nicks), - ClientFlags(flags, nicks) => construct_message(&["CLIENT_FLAGS", flags], &nicks), - Rooms(info) => construct_message(&["ROOMS"], &info), - RoomAdd(info) => construct_message(&["ROOM", "ADD"], &info), - RoomJoined(nicks) => construct_message(&["JOINED"], &nicks), - RoomLeft(nick, msg) => msg!["LEFT", nick, msg], - RoomRemove(name) => msg!["ROOM", "DEL", name], - RoomUpdated(name, info) => construct_message(&["ROOM", "UPD", name], &info), - Joining(name) => msg!["JOINING", name], - TeamAdd(info) => construct_message(&["ADD_TEAM"], &info), - TeamRemove(name) => msg!["REMOVE_TEAM", name], - TeamAccepted(name) => msg!["TEAM_ACCEPTED", name], - TeamColor(name, color) => msg!["TEAM_COLOR", name, color], - HedgehogsNumber(name, number) => msg!["HH_NUM", name, number], - ConfigEntry(name, values) => construct_message(&["CFG", name], &values), - Kicked => msg!["KICKED"], - RunGame => msg!["RUN_GAME"], - ForwardEngineMessage(em) => construct_message(&["EM"], &em), - RoundFinished => msg!["ROUND_FINISHED"], - ChatMsg { nick, msg } => msg!["CHAT", nick, msg], - Info(info) => construct_message(&["INFO"], &info), - ServerMessage(msg) => msg!["SERVER_MESSAGE", msg], - ServerVars(vars) => construct_message(&["SERVER_VARS"], &vars), - Notice(msg) => msg!["NOTICE", msg], - Warning(msg) => msg!["WARNING", msg], - Error(msg) => msg!["ERROR", msg], - ReplayStart => msg!["REPLAY_START"], - - LegacyReady(is_ready, nicks) => { - construct_message(&[if *is_ready { "READY" } else { "NOT_READY" }], &nicks) - } - - _ => msg!["ERROR", "UNIMPLEMENTED"], - } - } -}