1 use crate::server::coretypes::{ |
|
2 ServerVar, GameCfg, TeamInfo, |
|
3 HedgehogInfo, VoteType |
|
4 }; |
|
5 use std::{ops, convert::From, iter::once}; |
|
6 |
|
7 #[derive(PartialEq, Eq, Clone, Debug)] |
|
8 pub enum HWProtocolMessage { |
|
9 // core |
|
10 Ping, |
|
11 Pong, |
|
12 Quit(Option<String>), |
|
13 //Cmd(String, Vec<String>), |
|
14 Global(String), |
|
15 Watch(String), |
|
16 ToggleServerRegisteredOnly, |
|
17 SuperPower, |
|
18 Info(String), |
|
19 // not entered state |
|
20 Nick(String), |
|
21 Proto(u16), |
|
22 Password(String, String), |
|
23 Checker(u16, String, String), |
|
24 // lobby |
|
25 List, |
|
26 Chat(String), |
|
27 CreateRoom(String, Option<String>), |
|
28 JoinRoom(String, Option<String>), |
|
29 Follow(String), |
|
30 Rnd(Vec<String>), |
|
31 Kick(String), |
|
32 Ban(String, String, u32), |
|
33 BanIP(String, String, u32), |
|
34 BanNick(String, String, u32), |
|
35 BanList, |
|
36 Unban(String), |
|
37 SetServerVar(ServerVar), |
|
38 GetServerVar, |
|
39 RestartServer, |
|
40 Stats, |
|
41 // in room |
|
42 Part(Option<String>), |
|
43 Cfg(GameCfg), |
|
44 AddTeam(Box<TeamInfo>), |
|
45 RemoveTeam(String), |
|
46 SetHedgehogsNumber(String, u8), |
|
47 SetTeamColor(String, u8), |
|
48 ToggleReady, |
|
49 StartGame, |
|
50 EngineMessage(String), |
|
51 RoundFinished, |
|
52 ToggleRestrictJoin, |
|
53 ToggleRestrictTeams, |
|
54 ToggleRegisteredOnly, |
|
55 RoomName(String), |
|
56 Delegate(String), |
|
57 TeamChat(String), |
|
58 MaxTeams(u8), |
|
59 Fix, |
|
60 Unfix, |
|
61 Greeting(String), |
|
62 CallVote(Option<VoteType>), |
|
63 Vote(bool), |
|
64 ForceVote(bool), |
|
65 Save(String, String), |
|
66 Delete(String), |
|
67 SaveRoom(String), |
|
68 LoadRoom(String), |
|
69 Malformed, |
|
70 Empty, |
|
71 } |
|
72 |
|
73 #[derive(Debug)] |
|
74 pub enum HWServerMessage { |
|
75 Ping, |
|
76 Pong, |
|
77 Bye(String), |
|
78 Nick(String), |
|
79 Proto(u16), |
|
80 ServerAuth(String), |
|
81 LobbyLeft(String, String), |
|
82 LobbyJoined(Vec<String>), |
|
83 ChatMsg {nick: String, msg: String}, |
|
84 ClientFlags(String, Vec<String>), |
|
85 Rooms(Vec<String>), |
|
86 RoomAdd(Vec<String>), |
|
87 RoomJoined(Vec<String>), |
|
88 RoomLeft(String, String), |
|
89 RoomRemove(String), |
|
90 RoomUpdated(String, Vec<String>), |
|
91 TeamAdd(Vec<String>), |
|
92 TeamRemove(String), |
|
93 TeamAccepted(String), |
|
94 TeamColor(String, u8), |
|
95 HedgehogsNumber(String, u8), |
|
96 ConfigEntry(String, Vec<String>), |
|
97 Kicked, |
|
98 RunGame, |
|
99 ForwardEngineMessage(Vec<String>), |
|
100 RoundFinished, |
|
101 |
|
102 ServerMessage(String), |
|
103 Notice(String), |
|
104 Warning(String), |
|
105 Error(String), |
|
106 Connected(u32), |
|
107 Unreachable, |
|
108 |
|
109 //Deprecated messages |
|
110 LegacyReady(bool, Vec<String>) |
|
111 } |
|
112 |
|
113 pub fn server_chat(msg: String) -> HWServerMessage { |
|
114 HWServerMessage::ChatMsg{ nick: "[server]".to_string(), msg } |
|
115 } |
|
116 |
|
117 impl GameCfg { |
|
118 pub fn to_protocol(&self) -> (String, Vec<String>) { |
|
119 use crate::server::coretypes::GameCfg::*; |
|
120 match self { |
|
121 FeatureSize(s) => ("FEATURE_SIZE".to_string(), vec![s.to_string()]), |
|
122 MapType(t) => ("MAP".to_string(), vec![t.to_string()]), |
|
123 MapGenerator(g) => ("MAPGEN".to_string(), vec![g.to_string()]), |
|
124 MazeSize(s) => ("MAZE_SIZE".to_string(), vec![s.to_string()]), |
|
125 Seed(s) => ("SEED".to_string(), vec![s.to_string()]), |
|
126 Template(t) => ("TEMPLATE".to_string(), vec![t.to_string()]), |
|
127 |
|
128 Ammo(n, None) => ("AMMO".to_string(), vec![n.to_string()]), |
|
129 Ammo(n, Some(s)) => ("AMMO".to_string(), vec![n.to_string(), s.to_string()]), |
|
130 Scheme(n, s) if s.is_empty() => ("SCHEME".to_string(), vec![n.to_string()]), |
|
131 Scheme(n, s) => ("SCHEME".to_string(), { |
|
132 let mut v = vec![n.to_string()]; |
|
133 v.extend(s.clone().into_iter()); |
|
134 v |
|
135 }), |
|
136 Script(s) => ("SCRIPT".to_string(), vec![s.to_string()]), |
|
137 Theme(t) => ("THEME".to_string(), vec![t.to_string()]), |
|
138 DrawnMap(m) => ("DRAWNMAP".to_string(), vec![m.to_string()]) |
|
139 } |
|
140 } |
|
141 |
|
142 pub fn to_server_msg(&self) -> HWServerMessage { |
|
143 use self::HWServerMessage::ConfigEntry; |
|
144 let (name, args) = self.to_protocol(); |
|
145 HWServerMessage::ConfigEntry(name, args) |
|
146 } |
|
147 } |
|
148 |
|
149 macro_rules! const_braces { |
|
150 ($e: expr) => { "{}\n" } |
|
151 } |
|
152 |
|
153 macro_rules! msg { |
|
154 [$($part: expr),*] => { |
|
155 format!(concat!($(const_braces!($part)),*, "\n"), $($part),*); |
|
156 }; |
|
157 } |
|
158 |
|
159 #[cfg(test)] |
|
160 macro_rules! several { |
|
161 [$part: expr] => { once($part) }; |
|
162 [$part: expr, $($other: expr),*] => { once($part).chain(several![$($other),*]) }; |
|
163 } |
|
164 |
|
165 impl HWProtocolMessage { |
|
166 /** Converts the message to a raw `String`, which can be sent over the network. |
|
167 * |
|
168 * This is the inverse of the `message` parser. |
|
169 */ |
|
170 #[cfg(test)] |
|
171 pub(crate) fn to_raw_protocol(&self) -> String { |
|
172 use self::HWProtocolMessage::*; |
|
173 match self { |
|
174 Ping => msg!["PING"], |
|
175 Pong => msg!["PONG"], |
|
176 Quit(None) => msg!["QUIT"], |
|
177 Quit(Some(msg)) => msg!["QUIT", msg], |
|
178 Global(msg) => msg!["CMD", format!("GLOBAL {}", msg)], |
|
179 Watch(name) => msg!["CMD", format!("WATCH {}", name)], |
|
180 ToggleServerRegisteredOnly => msg!["CMD", "REGISTERED_ONLY"], |
|
181 SuperPower => msg!["CMD", "SUPER_POWER"], |
|
182 Info(info) => msg!["CMD", format!("INFO {}", info)], |
|
183 Nick(nick) => msg!("NICK", nick), |
|
184 Proto(version) => msg!["PROTO", version], |
|
185 Password(p, s) => msg!["PASSWORD", p, s], |
|
186 Checker(i, n, p) => msg!["CHECKER", i, n, p], |
|
187 List => msg!["LIST"], |
|
188 Chat(msg) => msg!["CHAT", msg], |
|
189 CreateRoom(name, None) => msg!["CREATE_ROOM", name], |
|
190 CreateRoom(name, Some(password)) => |
|
191 msg!["CREATE_ROOM", name, password], |
|
192 JoinRoom(name, None) => msg!["JOIN_ROOM", name], |
|
193 JoinRoom(name, Some(password)) => |
|
194 msg!["JOIN_ROOM", name, password], |
|
195 Follow(name) => msg!["FOLLOW", name], |
|
196 Rnd(args) => if args.is_empty() { |
|
197 msg!["CMD", "RND"] |
|
198 } else { |
|
199 msg!["CMD", format!("RND {}", args.join(" "))] |
|
200 }, |
|
201 Kick(name) => msg!["KICK", name], |
|
202 Ban(name, reason, time) => msg!["BAN", name, reason, time], |
|
203 BanIP(ip, reason, time) => msg!["BAN_IP", ip, reason, time], |
|
204 BanNick(nick, reason, time) => |
|
205 msg!("BAN_NICK", nick, reason, time), |
|
206 BanList => msg!["BANLIST"], |
|
207 Unban(name) => msg!["UNBAN", name], |
|
208 //SetServerVar(ServerVar), ??? |
|
209 GetServerVar => msg!["GET_SERVER_VAR"], |
|
210 RestartServer => msg!["CMD", "RESTART_SERVER YES"], |
|
211 Stats => msg!["CMD", "STATS"], |
|
212 Part(None) => msg!["PART"], |
|
213 Part(Some(msg)) => msg!["PART", msg], |
|
214 Cfg(config) => { |
|
215 let (name, args) = config.to_protocol(); |
|
216 msg!["CFG", name, args.join("\n")] |
|
217 }, |
|
218 AddTeam(info) => |
|
219 msg!["ADD_TEAM", info.name, info.color, info.grave, info.fort, |
|
220 info.voice_pack, info.flag, info.difficulty, |
|
221 info.hedgehogs.iter() |
|
222 .flat_map(|h| several![&h.name[..], &h.hat[..]]) |
|
223 .collect::<Vec<_>>().join("\n")], |
|
224 RemoveTeam(name) => msg!["REMOVE_TEAM", name], |
|
225 SetHedgehogsNumber(team, number) => msg!["HH_NUM", team, number], |
|
226 SetTeamColor(team, color) => msg!["TEAM_COLOR", team, color], |
|
227 ToggleReady => msg!["TOGGLE_READY"], |
|
228 StartGame => msg!["START_GAME"], |
|
229 EngineMessage(msg) => msg!["EM", msg], |
|
230 RoundFinished => msg!["ROUNDFINISHED"], |
|
231 ToggleRestrictJoin => msg!["TOGGLE_RESTRICT_JOINS"], |
|
232 ToggleRestrictTeams => msg!["TOGGLE_RESTRICT_TEAMS"], |
|
233 ToggleRegisteredOnly => msg!["TOGGLE_REGISTERED_ONLY"], |
|
234 RoomName(name) => msg!["ROOM_NAME", name], |
|
235 Delegate(name) => msg!["CMD", format!("DELEGATE {}", name)], |
|
236 TeamChat(msg) => msg!["TEAMCHAT", msg], |
|
237 MaxTeams(count) => msg!["CMD", format!("MAXTEAMS {}", count)] , |
|
238 Fix => msg!["CMD", "FIX"], |
|
239 Unfix => msg!["CMD", "UNFIX"], |
|
240 Greeting(msg) => msg!["CMD", format!("GREETING {}", msg)], |
|
241 //CallVote(Option<(String, Option<String>)>) =>, ?? |
|
242 Vote(msg) => msg!["CMD", format!("VOTE {}", if *msg {"YES"} else {"NO"})], |
|
243 ForceVote(msg) => msg!["CMD", format!("FORCE {}", if *msg {"YES"} else {"NO"})], |
|
244 Save(name, location) => msg!["CMD", format!("SAVE {} {}", name, location)], |
|
245 Delete(name) => msg!["CMD", format!("DELETE {}", name)], |
|
246 SaveRoom(name) => msg!["CMD", format!("SAVEROOM {}", name)], |
|
247 LoadRoom(name) => msg!["CMD", format!("LOADROOM {}", name)], |
|
248 Malformed => msg!["A", "QUICK", "BROWN", "HOG", "JUMPS", "OVER", "THE", "LAZY", "DOG"], |
|
249 Empty => msg![""], |
|
250 _ => panic!("Protocol message not yet implemented") |
|
251 } |
|
252 } |
|
253 } |
|
254 |
|
255 fn construct_message(header: &[&str], msg: &[String]) -> String { |
|
256 let mut v: Vec<_> = header.iter().cloned().collect(); |
|
257 v.extend(msg.iter().map(|s| &s[..])); |
|
258 v.push("\n"); |
|
259 v.join("\n") |
|
260 } |
|
261 |
|
262 impl HWServerMessage { |
|
263 pub fn to_raw_protocol(&self) -> String { |
|
264 use self::HWServerMessage::*; |
|
265 match self { |
|
266 Ping => msg!["PING"], |
|
267 Pong => msg!["PONG"], |
|
268 Connected(protocol_version) => msg![ |
|
269 "CONNECTED", |
|
270 "Hedgewars server https://www.hedgewars.org/", |
|
271 protocol_version], |
|
272 Bye(msg) => msg!["BYE", msg], |
|
273 Nick(nick) => msg!["NICK", nick], |
|
274 Proto(proto) => msg!["PROTO", proto], |
|
275 ServerAuth(hash) => msg!["SERVER_AUTH", hash], |
|
276 LobbyLeft(nick, msg) => msg!["LOBBY:LEFT", nick, msg], |
|
277 LobbyJoined(nicks) => |
|
278 construct_message(&["LOBBY:JOINED"], &nicks), |
|
279 ClientFlags(flags, nicks) => |
|
280 construct_message(&["CLIENT_FLAGS", flags], &nicks), |
|
281 Rooms(info) => |
|
282 construct_message(&["ROOMS"], &info), |
|
283 RoomAdd(info) => |
|
284 construct_message(&["ROOM", "ADD"], &info), |
|
285 RoomJoined(nicks) => |
|
286 construct_message(&["JOINED"], &nicks), |
|
287 RoomLeft(nick, msg) => msg!["LEFT", nick, msg], |
|
288 RoomRemove(name) => msg!["ROOM", "DEL", name], |
|
289 RoomUpdated(name, info) => |
|
290 construct_message(&["ROOM", "UPD", name], &info), |
|
291 TeamAdd(info) => |
|
292 construct_message(&["ADD_TEAM"], &info), |
|
293 TeamRemove(name) => msg!["REMOVE_TEAM", name], |
|
294 TeamAccepted(name) => msg!["TEAM_ACCEPTED", name], |
|
295 TeamColor(name, color) => msg!["TEAM_COLOR", name, color], |
|
296 HedgehogsNumber(name, number) => msg!["HH_NUM", name, number], |
|
297 ConfigEntry(name, values) => |
|
298 construct_message(&["CFG", name], &values), |
|
299 Kicked => msg!["KICKED"], |
|
300 RunGame => msg!["RUN_GAME"], |
|
301 ForwardEngineMessage(em) => |
|
302 construct_message(&["EM"], &em), |
|
303 RoundFinished => msg!["ROUND_FINISHED"], |
|
304 ChatMsg {nick, msg} => msg!["CHAT", nick, msg], |
|
305 ServerMessage(msg) => msg!["SERVER_MESSAGE", msg], |
|
306 Notice(msg) => msg!["NOTICE", msg], |
|
307 Warning(msg) => msg!["WARNING", msg], |
|
308 Error(msg) => msg!["ERROR", msg], |
|
309 |
|
310 LegacyReady(is_ready, nicks) => |
|
311 construct_message(&[if *is_ready {"READY"} else {"NOT_READY"}], &nicks), |
|
312 |
|
313 _ => msg!["ERROR", "UNIMPLEMENTED"], |
|
314 } |
|
315 } |
|
316 } |
|