rust/hedgewars-network-protocol/src/messages.rs
changeset 15804 747278149393
child 15810 ee84e417d8d0
equal deleted inserted replaced
15803:b06b33cf0a89 15804:747278149393
       
     1 use crate::types::{GameCfg, ServerVar, TeamInfo, VoteType};
       
     2 use std::iter::once;
       
     3 
       
     4 #[derive(PartialEq, Eq, Clone, Debug)]
       
     5 pub enum HwProtocolMessage {
       
     6     // common messages
       
     7     Ping,
       
     8     Pong,
       
     9     Quit(Option<String>),
       
    10     Global(String),
       
    11     Watch(u32),
       
    12     ToggleServerRegisteredOnly,
       
    13     SuperPower,
       
    14     Info(String),
       
    15     // anteroom messages
       
    16     Nick(String),
       
    17     Proto(u16),
       
    18     Password(String, String),
       
    19     Checker(u16, String, String),
       
    20     // lobby messages
       
    21     List,
       
    22     Chat(String),
       
    23     CreateRoom(String, Option<String>),
       
    24     JoinRoom(String, Option<String>),
       
    25     Follow(String),
       
    26     Rnd(Vec<String>),
       
    27     Kick(String),
       
    28     Ban(String, String, u32),
       
    29     BanIp(String, String, u32),
       
    30     BanNick(String, String, u32),
       
    31     BanList,
       
    32     Unban(String),
       
    33     SetServerVar(ServerVar),
       
    34     GetServerVar,
       
    35     RestartServer,
       
    36     Stats,
       
    37     // room messages
       
    38     Part(Option<String>),
       
    39     Cfg(GameCfg),
       
    40     AddTeam(Box<TeamInfo>),
       
    41     RemoveTeam(String),
       
    42     SetHedgehogsNumber(String, u8),
       
    43     SetTeamColor(String, u8),
       
    44     ToggleReady,
       
    45     StartGame,
       
    46     EngineMessage(String),
       
    47     RoundFinished,
       
    48     ToggleRestrictJoin,
       
    49     ToggleRestrictTeams,
       
    50     ToggleRegisteredOnly,
       
    51     RoomName(String),
       
    52     Delegate(String),
       
    53     TeamChat(String),
       
    54     MaxTeams(u8),
       
    55     Fix,
       
    56     Unfix,
       
    57     Greeting(Option<String>),
       
    58     CallVote(Option<VoteType>),
       
    59     Vote(bool),
       
    60     ForceVote(bool),
       
    61     Save(String, String),
       
    62     Delete(String),
       
    63     SaveRoom(String),
       
    64     LoadRoom(String),
       
    65 }
       
    66 
       
    67 #[derive(Debug, Clone, Copy)]
       
    68 pub enum ProtocolFlags {
       
    69     InRoom,
       
    70     RoomMaster,
       
    71     Ready,
       
    72     InGame,
       
    73     Registered,
       
    74     Admin,
       
    75     Contributor,
       
    76 }
       
    77 
       
    78 impl ProtocolFlags {
       
    79     #[inline]
       
    80     fn flag_char(&self) -> char {
       
    81         match self {
       
    82             ProtocolFlags::InRoom => 'i',
       
    83             ProtocolFlags::RoomMaster => 'h',
       
    84             ProtocolFlags::Ready => 'r',
       
    85             ProtocolFlags::InGame => 'g',
       
    86             ProtocolFlags::Registered => 'u',
       
    87             ProtocolFlags::Admin => 'a',
       
    88             ProtocolFlags::Contributor => 'c',
       
    89         }
       
    90     }
       
    91 
       
    92     #[inline]
       
    93     fn format(prefix: char, flags: &[ProtocolFlags]) -> String {
       
    94         once(prefix)
       
    95             .chain(flags.iter().map(|f| f.flag_char()))
       
    96             .collect()
       
    97     }
       
    98 }
       
    99 
       
   100 #[inline]
       
   101 pub fn add_flags(flags: &[ProtocolFlags]) -> String {
       
   102     ProtocolFlags::format('+', flags)
       
   103 }
       
   104 
       
   105 #[inline]
       
   106 pub fn remove_flags(flags: &[ProtocolFlags]) -> String {
       
   107     ProtocolFlags::format('-', flags)
       
   108 }
       
   109 
       
   110 #[derive(Debug)]
       
   111 pub enum HwServerMessage {
       
   112     Connected(u32),
       
   113     Redirect(u16),
       
   114 
       
   115     Ping,
       
   116     Pong,
       
   117     Bye(String),
       
   118 
       
   119     Nick(String),
       
   120     Proto(u16),
       
   121     AskPassword(String),
       
   122     ServerAuth(String),
       
   123     LogonPassed,
       
   124 
       
   125     LobbyLeft(String, String),
       
   126     LobbyJoined(Vec<String>),
       
   127     ChatMsg { nick: String, msg: String },
       
   128     ClientFlags(String, Vec<String>),
       
   129     Rooms(Vec<String>),
       
   130     RoomAdd(Vec<String>),
       
   131     RoomJoined(Vec<String>),
       
   132     RoomLeft(String, String),
       
   133     RoomRemove(String),
       
   134     RoomUpdated(String, Vec<String>),
       
   135     Joining(String),
       
   136     TeamAdd(Vec<String>),
       
   137     TeamRemove(String),
       
   138     TeamAccepted(String),
       
   139     TeamColor(String, u8),
       
   140     HedgehogsNumber(String, u8),
       
   141     ConfigEntry(String, Vec<String>),
       
   142     Kicked,
       
   143     RunGame,
       
   144     ForwardEngineMessage(Vec<String>),
       
   145     RoundFinished,
       
   146     ReplayStart,
       
   147 
       
   148     Info(Vec<String>),
       
   149     ServerMessage(String),
       
   150     ServerVars(Vec<String>),
       
   151     Notice(String),
       
   152     Warning(String),
       
   153     Error(String),
       
   154     Unreachable,
       
   155 
       
   156     //Deprecated messages
       
   157     LegacyReady(bool, Vec<String>),
       
   158 }
       
   159 
       
   160 fn special_chat(nick: &str, msg: String) -> HwServerMessage {
       
   161     HwServerMessage::ChatMsg {
       
   162         nick: nick.to_string(),
       
   163         msg,
       
   164     }
       
   165 }
       
   166 
       
   167 pub fn server_chat(msg: String) -> HwServerMessage {
       
   168     special_chat("[server]", msg)
       
   169 }
       
   170 
       
   171 pub fn global_chat(msg: String) -> HwServerMessage {
       
   172     special_chat("(global notice)", msg)
       
   173 }
       
   174 
       
   175 impl ServerVar {
       
   176     pub fn to_protocol(&self) -> Vec<String> {
       
   177         use ServerVar::*;
       
   178         match self {
       
   179             MOTDNew(s) => vec!["MOTD_NEW".to_string(), s.clone()],
       
   180             MOTDOld(s) => vec!["MOTD_OLD".to_string(), s.clone()],
       
   181             LatestProto(n) => vec!["LATEST_PROTO".to_string(), n.to_string()],
       
   182         }
       
   183     }
       
   184 }
       
   185 
       
   186 impl VoteType {
       
   187     pub fn to_protocol(&self) -> Vec<String> {
       
   188         use VoteType::*;
       
   189         match self {
       
   190             Kick(nick) => vec!["KICK".to_string(), nick.clone()],
       
   191             Map(None) => vec!["MAP".to_string()],
       
   192             Map(Some(name)) => vec!["MAP".to_string(), name.clone()],
       
   193             Pause => vec!["PAUSE".to_string()],
       
   194             NewSeed => vec!["NEWSEED".to_string()],
       
   195             HedgehogsPerTeam(count) => vec!["HEDGEHOGS".to_string(), count.to_string()],
       
   196         }
       
   197     }
       
   198 }
       
   199 
       
   200 impl GameCfg {
       
   201     pub fn to_protocol(&self) -> (String, Vec<String>) {
       
   202         use GameCfg::*;
       
   203         match self {
       
   204             FeatureSize(s) => ("FEATURE_SIZE".to_string(), vec![s.to_string()]),
       
   205             MapType(t) => ("MAP".to_string(), vec![t.to_string()]),
       
   206             MapGenerator(g) => ("MAPGEN".to_string(), vec![g.to_string()]),
       
   207             MazeSize(s) => ("MAZE_SIZE".to_string(), vec![s.to_string()]),
       
   208             Seed(s) => ("SEED".to_string(), vec![s.to_string()]),
       
   209             Template(t) => ("TEMPLATE".to_string(), vec![t.to_string()]),
       
   210 
       
   211             Ammo(n, None) => ("AMMO".to_string(), vec![n.to_string()]),
       
   212             Ammo(n, Some(s)) => ("AMMO".to_string(), vec![n.to_string(), s.to_string()]),
       
   213             Scheme(n, s) if s.is_empty() => ("SCHEME".to_string(), vec![n.to_string()]),
       
   214             Scheme(n, s) => ("SCHEME".to_string(), {
       
   215                 let mut v = vec![n.to_string()];
       
   216                 v.extend(s.clone());
       
   217                 v
       
   218             }),
       
   219             Script(s) => ("SCRIPT".to_string(), vec![s.to_string()]),
       
   220             Theme(t) => ("THEME".to_string(), vec![t.to_string()]),
       
   221             DrawnMap(m) => ("DRAWNMAP".to_string(), vec![m.to_string()]),
       
   222         }
       
   223     }
       
   224 
       
   225     pub fn to_server_msg(&self) -> HwServerMessage {
       
   226         let (name, args) = self.to_protocol();
       
   227         HwServerMessage::ConfigEntry(name, args)
       
   228     }
       
   229 }
       
   230 
       
   231 impl TeamInfo {
       
   232     pub fn to_protocol(&self) -> Vec<String> {
       
   233         let mut info = vec![
       
   234             self.name.clone(),
       
   235             self.grave.clone(),
       
   236             self.fort.clone(),
       
   237             self.voice_pack.clone(),
       
   238             self.flag.clone(),
       
   239             self.owner.clone(),
       
   240             self.difficulty.to_string(),
       
   241         ];
       
   242         let hogs = self
       
   243             .hedgehogs
       
   244             .iter()
       
   245             .flat_map(|h| once(h.name.clone()).chain(once(h.hat.clone())));
       
   246         info.extend(hogs);
       
   247         info
       
   248     }
       
   249 }
       
   250 
       
   251 macro_rules! const_braces {
       
   252     ($e: expr) => {
       
   253         "{}\n"
       
   254     };
       
   255 }
       
   256 
       
   257 macro_rules! msg {
       
   258     [$($part: expr),*] => {
       
   259         format!(concat!($(const_braces!($part)),*, "\n"), $($part),*);
       
   260     };
       
   261 }
       
   262 
       
   263 impl HwProtocolMessage {
       
   264     /** Converts the message to a raw `String`, which can be sent over the network.
       
   265      *
       
   266      * This is the inverse of the `message` parser.
       
   267      */
       
   268     pub fn to_raw_protocol(&self) -> String {
       
   269         use self::HwProtocolMessage::*;
       
   270         match self {
       
   271             Ping => msg!["PING"],
       
   272             Pong => msg!["PONG"],
       
   273             Quit(None) => msg!["QUIT"],
       
   274             Quit(Some(msg)) => msg!["QUIT", msg],
       
   275             Global(msg) => msg!["CMD", format!("GLOBAL {}", msg)],
       
   276             Watch(name) => msg!["CMD", format!("WATCH {}", name)],
       
   277             ToggleServerRegisteredOnly => msg!["CMD", "REGISTERED_ONLY"],
       
   278             SuperPower => msg!["CMD", "SUPER_POWER"],
       
   279             Info(info) => msg!["CMD", format!("INFO {}", info)],
       
   280             Nick(nick) => msg!("NICK", nick),
       
   281             Proto(version) => msg!["PROTO", version],
       
   282             Password(p, s) => msg!["PASSWORD", p, s],
       
   283             Checker(i, n, p) => msg!["CHECKER", i, n, p],
       
   284             List => msg!["LIST"],
       
   285             Chat(msg) => msg!["CHAT", msg],
       
   286             CreateRoom(name, None) => msg!["CREATE_ROOM", name],
       
   287             CreateRoom(name, Some(password)) => msg!["CREATE_ROOM", name, password],
       
   288             JoinRoom(name, None) => msg!["JOIN_ROOM", name],
       
   289             JoinRoom(name, Some(password)) => msg!["JOIN_ROOM", name, password],
       
   290             Follow(name) => msg!["FOLLOW", name],
       
   291             Rnd(args) => {
       
   292                 if args.is_empty() {
       
   293                     msg!["CMD", "RND"]
       
   294                 } else {
       
   295                     msg!["CMD", format!("RND {}", args.join(" "))]
       
   296                 }
       
   297             }
       
   298             Kick(name) => msg!["KICK", name],
       
   299             Ban(name, reason, time) => msg!["BAN", name, reason, time],
       
   300             BanIp(ip, reason, time) => msg!["BAN_IP", ip, reason, time],
       
   301             BanNick(nick, reason, time) => msg!("BAN_NICK", nick, reason, time),
       
   302             BanList => msg!["BANLIST"],
       
   303             Unban(name) => msg!["UNBAN", name],
       
   304             SetServerVar(var) => construct_message(&["SET_SERVER_VAR"], &var.to_protocol()),
       
   305             GetServerVar => msg!["GET_SERVER_VAR"],
       
   306             RestartServer => msg!["CMD", "RESTART_SERVER YES"],
       
   307             Stats => msg!["CMD", "STATS"],
       
   308             Part(None) => msg!["PART"],
       
   309             Part(Some(msg)) => msg!["PART", msg],
       
   310             Cfg(config) => {
       
   311                 let (name, args) = config.to_protocol();
       
   312                 msg!["CFG", name, args.join("\n")]
       
   313             }
       
   314             AddTeam(info) => msg![
       
   315                 "ADD_TEAM",
       
   316                 info.name,
       
   317                 info.color,
       
   318                 info.grave,
       
   319                 info.fort,
       
   320                 info.voice_pack,
       
   321                 info.flag,
       
   322                 info.difficulty,
       
   323                 &(info.hedgehogs.iter())
       
   324                     .flat_map(|h| [&h.name[..], &h.hat[..]])
       
   325                     .collect::<Vec<_>>()
       
   326                     .join("\n")
       
   327             ],
       
   328             RemoveTeam(name) => msg!["REMOVE_TEAM", name],
       
   329             SetHedgehogsNumber(team, number) => msg!["HH_NUM", team, number],
       
   330             SetTeamColor(team, color) => msg!["TEAM_COLOR", team, color],
       
   331             ToggleReady => msg!["TOGGLE_READY"],
       
   332             StartGame => msg!["START_GAME"],
       
   333             EngineMessage(msg) => msg!["EM", msg],
       
   334             RoundFinished => msg!["ROUNDFINISHED"],
       
   335             ToggleRestrictJoin => msg!["TOGGLE_RESTRICT_JOINS"],
       
   336             ToggleRestrictTeams => msg!["TOGGLE_RESTRICT_TEAMS"],
       
   337             ToggleRegisteredOnly => msg!["TOGGLE_REGISTERED_ONLY"],
       
   338             RoomName(name) => msg!["ROOM_NAME", name],
       
   339             Delegate(name) => msg!["CMD", format!("DELEGATE {}", name)],
       
   340             TeamChat(msg) => msg!["TEAMCHAT", msg],
       
   341             MaxTeams(count) => msg!["CMD", format!("MAXTEAMS {}", count)],
       
   342             Fix => msg!["CMD", "FIX"],
       
   343             Unfix => msg!["CMD", "UNFIX"],
       
   344             Greeting(None) => msg!["CMD", "GREETING"],
       
   345             Greeting(Some(msg)) => msg!["CMD", format!("GREETING {}", msg)],
       
   346             CallVote(None) => msg!["CMD", "CALLVOTE"],
       
   347             CallVote(Some(vote)) => {
       
   348                 msg!["CMD", format!("CALLVOTE {}", &vote.to_protocol().join(" "))]
       
   349             }
       
   350             Vote(msg) => msg!["CMD", format!("VOTE {}", if *msg { "YES" } else { "NO" })],
       
   351             ForceVote(msg) => msg!["CMD", format!("FORCE {}", if *msg { "YES" } else { "NO" })],
       
   352             Save(name, location) => msg!["CMD", format!("SAVE {} {}", name, location)],
       
   353             Delete(name) => msg!["CMD", format!("DELETE {}", name)],
       
   354             SaveRoom(name) => msg!["CMD", format!("SAVEROOM {}", name)],
       
   355             LoadRoom(name) => msg!["CMD", format!("LOADROOM {}", name)],
       
   356         }
       
   357     }
       
   358 }
       
   359 
       
   360 fn construct_message(header: &[&str], msg: &[String]) -> String {
       
   361     let mut v: Vec<_> = header.iter().cloned().collect();
       
   362     v.extend(msg.iter().map(|s| &s[..]));
       
   363     v.push("\n");
       
   364     v.join("\n")
       
   365 }
       
   366 
       
   367 impl HwServerMessage {
       
   368     pub fn to_raw_protocol(&self) -> String {
       
   369         use self::HwServerMessage::*;
       
   370         match self {
       
   371             Ping => msg!["PING"],
       
   372             Pong => msg!["PONG"],
       
   373             Connected(protocol_version) => msg![
       
   374                 "CONNECTED",
       
   375                 "Hedgewars server https://www.hedgewars.org/",
       
   376                 protocol_version
       
   377             ],
       
   378             Redirect(port) => msg!["REDIRECT", port],
       
   379             Bye(msg) => msg!["BYE", msg],
       
   380             Nick(nick) => msg!["NICK", nick],
       
   381             Proto(proto) => msg!["PROTO", proto],
       
   382             AskPassword(salt) => msg!["ASKPASSWORD", salt],
       
   383             ServerAuth(hash) => msg!["SERVER_AUTH", hash],
       
   384             LogonPassed => msg!["LOGONPASSED"],
       
   385             LobbyLeft(nick, msg) => msg!["LOBBY:LEFT", nick, msg],
       
   386             LobbyJoined(nicks) => construct_message(&["LOBBY:JOINED"], &nicks),
       
   387             ClientFlags(flags, nicks) => construct_message(&["CLIENT_FLAGS", flags], &nicks),
       
   388             Rooms(info) => construct_message(&["ROOMS"], &info),
       
   389             RoomAdd(info) => construct_message(&["ROOM", "ADD"], &info),
       
   390             RoomJoined(nicks) => construct_message(&["JOINED"], &nicks),
       
   391             RoomLeft(nick, msg) => msg!["LEFT", nick, msg],
       
   392             RoomRemove(name) => msg!["ROOM", "DEL", name],
       
   393             RoomUpdated(name, info) => construct_message(&["ROOM", "UPD", name], &info),
       
   394             Joining(name) => msg!["JOINING", name],
       
   395             TeamAdd(info) => construct_message(&["ADD_TEAM"], &info),
       
   396             TeamRemove(name) => msg!["REMOVE_TEAM", name],
       
   397             TeamAccepted(name) => msg!["TEAM_ACCEPTED", name],
       
   398             TeamColor(name, color) => msg!["TEAM_COLOR", name, color],
       
   399             HedgehogsNumber(name, number) => msg!["HH_NUM", name, number],
       
   400             ConfigEntry(name, values) => construct_message(&["CFG", name], &values),
       
   401             Kicked => msg!["KICKED"],
       
   402             RunGame => msg!["RUN_GAME"],
       
   403             ForwardEngineMessage(em) => construct_message(&["EM"], &em),
       
   404             RoundFinished => msg!["ROUND_FINISHED"],
       
   405             ChatMsg { nick, msg } => msg!["CHAT", nick, msg],
       
   406             Info(info) => construct_message(&["INFO"], &info),
       
   407             ServerMessage(msg) => msg!["SERVER_MESSAGE", msg],
       
   408             ServerVars(vars) => construct_message(&["SERVER_VARS"], &vars),
       
   409             Notice(msg) => msg!["NOTICE", msg],
       
   410             Warning(msg) => msg!["WARNING", msg],
       
   411             Error(msg) => msg!["ERROR", msg],
       
   412             ReplayStart => msg!["REPLAY_START"],
       
   413 
       
   414             LegacyReady(is_ready, nicks) => {
       
   415                 construct_message(&[if *is_ready { "READY" } else { "NOT_READY" }], &nicks)
       
   416             }
       
   417 
       
   418             _ => msg!["ERROR", "UNIMPLEMENTED"],
       
   419         }
       
   420     }
       
   421 }