author | alfadur |
Wed, 10 Apr 2019 23:56:53 +0300 | |
changeset 14806 | a1077e8d26f4 |
parent 14804 | b3adc030104b |
child 14816 | add191d825f4 |
permissions | -rw-r--r-- |
13431
6a818f9192f4
Implement parsing for rnd and add a little documentation
Marcin Mielniczuk <marmistrz.dev@zoho.eu>
parents:
13423
diff
changeset
|
1 |
/** The parsers for the chat and multiplayer protocol. The main parser is `message`. |
6a818f9192f4
Implement parsing for rnd and add a little documentation
Marcin Mielniczuk <marmistrz.dev@zoho.eu>
parents:
13423
diff
changeset
|
2 |
* # Protocol |
6a818f9192f4
Implement parsing for rnd and add a little documentation
Marcin Mielniczuk <marmistrz.dev@zoho.eu>
parents:
13423
diff
changeset
|
3 |
* All messages consist of `\n`-separated strings. The end of a message is |
6a818f9192f4
Implement parsing for rnd and add a little documentation
Marcin Mielniczuk <marmistrz.dev@zoho.eu>
parents:
13423
diff
changeset
|
4 |
* indicated by a double newline - `\n\n`. |
6a818f9192f4
Implement parsing for rnd and add a little documentation
Marcin Mielniczuk <marmistrz.dev@zoho.eu>
parents:
13423
diff
changeset
|
5 |
* |
6a818f9192f4
Implement parsing for rnd and add a little documentation
Marcin Mielniczuk <marmistrz.dev@zoho.eu>
parents:
13423
diff
changeset
|
6 |
* For example, a nullary command like PING will be actually sent as `PING\n\n`. |
6a818f9192f4
Implement parsing for rnd and add a little documentation
Marcin Mielniczuk <marmistrz.dev@zoho.eu>
parents:
13423
diff
changeset
|
7 |
* A unary command, such as `START_GAME nick` will be actually sent as `START_GAME\nnick\n\n`. |
6a818f9192f4
Implement parsing for rnd and add a little documentation
Marcin Mielniczuk <marmistrz.dev@zoho.eu>
parents:
13423
diff
changeset
|
8 |
*/ |
12133 | 9 |
use nom::*; |
14796 | 10 |
use std::{ |
11 |
num::ParseIntError, |
|
12 |
ops::Range, |
|
13 |
str, |
|
14 |
str::{FromStr, Utf8Error}, |
|
15 |
}; |
|
12133 | 16 |
|
14478 | 17 |
use super::messages::{HWProtocolMessage, HWProtocolMessage::*}; |
14804 | 18 |
use crate::server::coretypes::{ |
19 |
GameCfg, HedgehogInfo, ServerVar, TeamInfo, VoteType, MAX_HEDGEHOGS_PER_TEAM, |
|
20 |
}; |
|
14796 | 21 |
|
22 |
#[derive(Debug, PartialEq)] |
|
23 |
pub struct HWProtocolError {} |
|
24 |
||
25 |
impl HWProtocolError { |
|
26 |
fn new() -> Self { |
|
27 |
HWProtocolError {} |
|
28 |
} |
|
29 |
} |
|
30 |
||
31 |
impl<I> ParseError<I> for HWProtocolError { |
|
32 |
fn from_error_kind(input: I, kind: ErrorKind) -> Self { |
|
33 |
HWProtocolError::new() |
|
34 |
} |
|
35 |
||
36 |
fn append(input: I, kind: ErrorKind, other: Self) -> Self { |
|
37 |
HWProtocolError::new() |
|
38 |
} |
|
39 |
} |
|
40 |
||
41 |
impl From<Utf8Error> for HWProtocolError { |
|
42 |
fn from(_: Utf8Error) -> Self { |
|
43 |
HWProtocolError::new() |
|
44 |
} |
|
45 |
} |
|
46 |
||
47 |
impl From<ParseIntError> for HWProtocolError { |
|
48 |
fn from(_: ParseIntError) -> Self { |
|
49 |
HWProtocolError::new() |
|
50 |
} |
|
51 |
} |
|
52 |
||
53 |
pub type HWResult<'a, O> = IResult<&'a [u8], O, HWProtocolError>; |
|
54 |
||
55 |
fn end_of_message(input: &[u8]) -> HWResult<&[u8]> { |
|
56 |
tag("\n\n")(input) |
|
57 |
} |
|
58 |
||
59 |
fn convert_utf8(input: &[u8]) -> HWResult<&str> { |
|
60 |
match str::from_utf8(input) { |
|
61 |
Ok(str) => Ok((b"", str)), |
|
62 |
Err(utf_err) => Result::Err(Err::Failure(utf_err.into())), |
|
63 |
} |
|
64 |
} |
|
65 |
||
66 |
fn convert_from_str<T>(str: &str) -> HWResult<T> |
|
67 |
where |
|
68 |
T: FromStr<Err = ParseIntError>, |
|
69 |
{ |
|
70 |
match T::from_str(str) { |
|
71 |
Ok(x) => Ok((b"", x)), |
|
72 |
Err(format_err) => Result::Err(Err::Failure(format_err.into())), |
|
73 |
} |
|
74 |
} |
|
75 |
||
76 |
fn str_line(input: &[u8]) -> HWResult<&str> { |
|
77 |
let (i, text) = not_line_ending(input)?; |
|
78 |
Ok((i, convert_utf8(text)?.1)) |
|
79 |
} |
|
80 |
||
81 |
fn a_line(input: &[u8]) -> HWResult<String> { |
|
82 |
let (i, str) = str_line(input)?; |
|
83 |
Ok((i, str.to_string())) |
|
84 |
} |
|
85 |
||
86 |
fn hw_tag<'a>(tag_str: &'a str) -> impl Fn(&'a [u8]) -> HWResult<'a, ()> { |
|
87 |
move |i| tag(tag_str)(i).map(|(i, _)| (i, ())) |
|
88 |
} |
|
89 |
||
90 |
fn hw_tag_no_case<'a>(tag_str: &'a str) -> impl Fn(&'a [u8]) -> HWResult<'a, ()> { |
|
91 |
move |i| tag_no_case(tag_str)(i).map(|(i, _)| (i, ())) |
|
92 |
} |
|
93 |
||
94 |
fn cmd_arg(input: &[u8]) -> HWResult<String> { |
|
95 |
let delimiters = b" \n"; |
|
96 |
let (i, str) = take_while(move |c| !delimiters.contains(&c))(input)?; |
|
97 |
Ok((i, convert_utf8(str)?.1.to_string())) |
|
98 |
} |
|
99 |
||
100 |
fn u8_line(input: &[u8]) -> HWResult<u8> { |
|
101 |
let (i, str) = str_line(input)?; |
|
102 |
Ok((i, convert_from_str(str)?.1)) |
|
103 |
} |
|
104 |
||
105 |
fn u16_line(input: &[u8]) -> HWResult<u16> { |
|
106 |
let (i, str) = str_line(input)?; |
|
107 |
Ok((i, convert_from_str(str)?.1)) |
|
108 |
} |
|
109 |
||
110 |
fn u32_line(input: &[u8]) -> HWResult<u32> { |
|
111 |
let (i, str) = str_line(input)?; |
|
112 |
Ok((i, convert_from_str(str)?.1)) |
|
113 |
} |
|
114 |
||
115 |
fn yes_no_line(input: &[u8]) -> HWResult<bool> { |
|
116 |
alt(( |
|
117 |
|i| tag_no_case(b"YES")(i).map(|(i, _)| (i, true)), |
|
118 |
|i| tag_no_case(b"NO")(i).map(|(i, _)| (i, false)), |
|
119 |
))(input) |
|
120 |
} |
|
121 |
||
122 |
fn opt_arg<'a>(input: &'a [u8]) -> HWResult<'a, Option<String>> { |
|
123 |
alt(( |
|
124 |
|i: &'a [u8]| peek!(i, end_of_message).map(|(i, _)| (i, None)), |
|
125 |
|i| precededc(i, hw_tag("\n"), a_line).map(|(i, v)| (i, Some(v))), |
|
126 |
))(input) |
|
127 |
} |
|
128 |
||
129 |
fn spaces(input: &[u8]) -> HWResult<&[u8]> { |
|
130 |
precededc(input, hw_tag(" "), |i| take_while(|c| c == b' ')(i)) |
|
131 |
} |
|
132 |
||
133 |
fn opt_space_arg<'a>(input: &'a [u8]) -> HWResult<'a, Option<String>> { |
|
134 |
alt(( |
|
135 |
|i: &'a [u8]| peek!(i, end_of_message).map(|(i, _)| (i, None)), |
|
136 |
|i| precededc(i, spaces, a_line).map(|(i, v)| (i, Some(v))), |
|
137 |
))(input) |
|
138 |
} |
|
12134 | 139 |
|
14796 | 140 |
fn hedgehog_array(input: &[u8]) -> HWResult<[HedgehogInfo; 8]> { |
141 |
fn hedgehog_line(input: &[u8]) -> HWResult<HedgehogInfo> { |
|
142 |
let (i, name) = terminatedc(input, a_line, eol)?; |
|
143 |
let (i, hat) = a_line(i)?; |
|
144 |
Ok((i, HedgehogInfo { name, hat })) |
|
145 |
} |
|
146 |
||
147 |
let (i, h1) = terminatedc(input, hedgehog_line, eol)?; |
|
148 |
let (i, h2) = terminatedc(i, hedgehog_line, eol)?; |
|
149 |
let (i, h3) = terminatedc(i, hedgehog_line, eol)?; |
|
150 |
let (i, h4) = terminatedc(i, hedgehog_line, eol)?; |
|
151 |
let (i, h5) = terminatedc(i, hedgehog_line, eol)?; |
|
152 |
let (i, h6) = terminatedc(i, hedgehog_line, eol)?; |
|
153 |
let (i, h7) = terminatedc(i, hedgehog_line, eol)?; |
|
154 |
let (i, h8) = hedgehog_line(i)?; |
|
155 |
||
156 |
Ok((i, [h1, h2, h3, h4, h5, h6, h7, h8])) |
|
157 |
} |
|
12133 | 158 |
|
14796 | 159 |
fn voting(input: &[u8]) -> HWResult<VoteType> { |
160 |
alt(( |
|
161 |
|i| tag_no_case("PAUSE")(i).map(|(i, _)| (i, VoteType::Pause)), |
|
162 |
|i| tag_no_case("NEWSEED")(i).map(|(i, _)| (i, VoteType::NewSeed)), |
|
163 |
|i| { |
|
164 |
precededc(i, |i| precededc(i, hw_tag_no_case("KICK"), spaces), a_line) |
|
165 |
.map(|(i, s)| (i, VoteType::Kick(s))) |
|
166 |
}, |
|
167 |
|i| { |
|
168 |
precededc( |
|
169 |
i, |
|
170 |
|i| precededc(i, hw_tag_no_case("HEDGEHOGS"), spaces), |
|
171 |
u8_line, |
|
172 |
) |
|
173 |
.map(|(i, n)| (i, VoteType::HedgehogsPerTeam(n))) |
|
174 |
}, |
|
175 |
|i| precededc(i, hw_tag_no_case("MAP"), opt_space_arg).map(|(i, v)| (i, VoteType::Map(v))), |
|
176 |
))(input) |
|
177 |
} |
|
12135 | 178 |
|
14796 | 179 |
fn no_arg_message(input: &[u8]) -> HWResult<HWProtocolMessage> { |
180 |
fn messagec<'a>( |
|
181 |
input: &'a [u8], |
|
182 |
name: &'a str, |
|
183 |
msg: HWProtocolMessage, |
|
184 |
) -> HWResult<'a, HWProtocolMessage> { |
|
185 |
tag(name)(input).map(|(i, _)| (i, msg.clone())) |
|
186 |
} |
|
187 |
||
188 |
alt(( |
|
189 |
|i| messagec(i, "PING", Ping), |
|
190 |
|i| messagec(i, "PONG", Pong), |
|
191 |
|i| messagec(i, "LIST", List), |
|
192 |
|i| messagec(i, "BANLIST", BanList), |
|
193 |
|i| messagec(i, "GET_SERVER_VAR", GetServerVar), |
|
194 |
|i| messagec(i, "TOGGLE_READY", ToggleReady), |
|
195 |
|i| messagec(i, "START_GAME", StartGame), |
|
196 |
|i| messagec(i, "TOGGLE_RESTRICT_JOINS", ToggleRestrictJoin), |
|
197 |
|i| messagec(i, "TOGGLE_RESTRICT_TEAMS", ToggleRestrictTeams), |
|
198 |
|i| messagec(i, "TOGGLE_REGISTERED_ONLY", ToggleRegisteredOnly), |
|
199 |
))(input) |
|
200 |
} |
|
12135 | 201 |
|
14796 | 202 |
fn single_arg_message(input: &[u8]) -> HWResult<HWProtocolMessage> { |
203 |
fn messagec<'a, T, F, G>( |
|
204 |
input: &'a [u8], |
|
205 |
name: &'a str, |
|
206 |
parser: F, |
|
207 |
constructor: G, |
|
208 |
) -> HWResult<'a, HWProtocolMessage> |
|
209 |
where |
|
210 |
F: Fn(&[u8]) -> HWResult<T>, |
|
211 |
G: Fn(T) -> HWProtocolMessage, |
|
212 |
{ |
|
213 |
precededc(input, hw_tag(name), parser).map(|(i, v)| (i, constructor(v))) |
|
214 |
} |
|
215 |
||
216 |
alt(( |
|
217 |
|i| messagec(i, "NICK\n", a_line, Nick), |
|
218 |
|i| messagec(i, "INFO\n", a_line, Info), |
|
219 |
|i| messagec(i, "CHAT\n", a_line, Chat), |
|
220 |
|i| messagec(i, "PART", opt_arg, Part), |
|
221 |
|i| messagec(i, "FOLLOW\n", a_line, Follow), |
|
222 |
|i| messagec(i, "KICK\n", a_line, Kick), |
|
223 |
|i| messagec(i, "UNBAN\n", a_line, Unban), |
|
224 |
|i| messagec(i, "EM\n", a_line, EngineMessage), |
|
225 |
|i| messagec(i, "TEAMCHAT\n", a_line, TeamChat), |
|
226 |
|i| messagec(i, "ROOM_NAME\n", a_line, RoomName), |
|
227 |
|i| messagec(i, "REMOVE_TEAM\n", a_line, RemoveTeam), |
|
228 |
|i| messagec(i, "ROUNDFINISHED", opt_arg, |_| RoundFinished), |
|
229 |
|i| messagec(i, "PROTO\n", u16_line, Proto), |
|
230 |
|i| messagec(i, "QUIT", opt_arg, Quit), |
|
231 |
))(input) |
|
232 |
} |
|
12135 | 233 |
|
14796 | 234 |
fn cmd_message<'a>(input: &'a [u8]) -> HWResult<'a, HWProtocolMessage> { |
235 |
fn cmdc_no_arg<'a>( |
|
236 |
input: &'a [u8], |
|
237 |
name: &'a str, |
|
238 |
msg: HWProtocolMessage, |
|
239 |
) -> HWResult<'a, HWProtocolMessage> { |
|
240 |
tag_no_case(name)(input).map(|(i, _)| (i, msg.clone())) |
|
241 |
} |
|
242 |
||
243 |
fn cmdc_single_arg<'a, T, F, G>( |
|
244 |
input: &'a [u8], |
|
245 |
name: &'a str, |
|
246 |
parser: F, |
|
247 |
constructor: G, |
|
248 |
) -> HWResult<'a, HWProtocolMessage> |
|
249 |
where |
|
250 |
F: Fn(&'a [u8]) -> HWResult<'a, T>, |
|
251 |
G: Fn(T) -> HWProtocolMessage, |
|
252 |
{ |
|
253 |
precededc(input, |i| pairc(i, hw_tag_no_case(name), spaces), parser) |
|
254 |
.map(|(i, v)| (i, constructor(v))) |
|
255 |
} |
|
256 |
||
257 |
fn cmd_no_arg_message(input: &[u8]) -> HWResult<HWProtocolMessage> { |
|
258 |
alt(( |
|
259 |
|i| cmdc_no_arg(i, "STATS", Stats), |
|
260 |
|i| cmdc_no_arg(i, "FIX", Fix), |
|
261 |
|i| cmdc_no_arg(i, "UNFIX", Unfix), |
|
262 |
|i| cmdc_no_arg(i, "REGISTERED_ONLY", ToggleServerRegisteredOnly), |
|
263 |
|i| cmdc_no_arg(i, "SUPER_POWER", SuperPower), |
|
264 |
))(input) |
|
265 |
} |
|
12135 | 266 |
|
14796 | 267 |
fn cmd_single_arg_message(input: &[u8]) -> HWResult<HWProtocolMessage> { |
268 |
alt(( |
|
269 |
|i| cmdc_single_arg(i, "RESTART_SERVER", |i| tag("YES")(i), |_| RestartServer), |
|
270 |
|i| cmdc_single_arg(i, "DELEGATE", a_line, Delegate), |
|
271 |
|i| cmdc_single_arg(i, "DELETE", a_line, Delete), |
|
272 |
|i| cmdc_single_arg(i, "SAVEROOM", a_line, SaveRoom), |
|
273 |
|i| cmdc_single_arg(i, "LOADROOM", a_line, LoadRoom), |
|
274 |
|i| cmdc_single_arg(i, "GLOBAL", a_line, Global), |
|
14806
a1077e8d26f4
implement watch message apart from replay deserializing
alfadur
parents:
14804
diff
changeset
|
275 |
|i| cmdc_single_arg(i, "WATCH", u32_line, Watch), |
14796 | 276 |
|i| cmdc_single_arg(i, "GREETING", a_line, Greeting), |
277 |
|i| cmdc_single_arg(i, "VOTE", yes_no_line, Vote), |
|
278 |
|i| cmdc_single_arg(i, "FORCE", yes_no_line, ForceVote), |
|
279 |
|i| cmdc_single_arg(i, "INFO", a_line, Info), |
|
280 |
|i| cmdc_single_arg(i, "MAXTEAMS", u8_line, MaxTeams), |
|
281 |
|i| cmdc_single_arg(i, "CALLVOTE", |i| opt!(i, voting), CallVote), |
|
282 |
))(input) |
|
283 |
} |
|
284 |
||
285 |
precededc( |
|
286 |
input, |
|
287 |
hw_tag("CMD\n"), |
|
288 |
alt(( |
|
289 |
cmd_no_arg_message, |
|
290 |
cmd_single_arg_message, |
|
291 |
|i| precededc(i, hw_tag_no_case("PART"), opt_space_arg).map(|(i, s)| (i, Part(s))), |
|
292 |
|i| precededc(i, hw_tag_no_case("QUIT"), opt_space_arg).map(|(i, s)| (i, Quit(s))), |
|
293 |
|i| { |
|
294 |
precededc(i, hw_tag_no_case("SAVE"), |i| { |
|
295 |
pairc( |
|
296 |
i, |
|
297 |
|i| precededc(i, spaces, cmd_arg), |
|
298 |
|i| precededc(i, spaces, cmd_arg), |
|
299 |
) |
|
300 |
}) |
|
301 |
.map(|(i, (n, l))| (i, Save(n, l))) |
|
302 |
}, |
|
303 |
|i| { |
|
304 |
let (i, _) = tag_no_case("RND")(i)?; |
|
305 |
let (i, _) = alt((spaces, |i: &'a [u8]| peek!(i, end_of_message)))(i)?; |
|
306 |
let (i, v) = str_line(i)?; |
|
307 |
Ok((i, Rnd(v.split_whitespace().map(String::from).collect()))) |
|
308 |
}, |
|
309 |
)), |
|
310 |
) |
|
311 |
} |
|
312 |
||
313 |
fn config_message<'a>(input: &'a [u8]) -> HWResult<'a, HWProtocolMessage> { |
|
314 |
fn cfgc_single_arg<'a, T, F, G>( |
|
315 |
input: &'a [u8], |
|
316 |
name: &'a str, |
|
317 |
parser: F, |
|
318 |
constructor: G, |
|
319 |
) -> HWResult<'a, GameCfg> |
|
320 |
where |
|
321 |
F: Fn(&[u8]) -> HWResult<T>, |
|
322 |
G: Fn(T) -> GameCfg, |
|
323 |
{ |
|
324 |
precededc(input, |i| terminatedc(i, hw_tag(name), eol), parser) |
|
325 |
.map(|(i, v)| (i, constructor(v))) |
|
326 |
} |
|
327 |
||
328 |
let (i, cfg) = precededc( |
|
329 |
input, |
|
330 |
hw_tag("CFG\n"), |
|
331 |
alt(( |
|
332 |
|i| cfgc_single_arg(i, "THEME", a_line, GameCfg::Theme), |
|
333 |
|i| cfgc_single_arg(i, "SCRIPT", a_line, GameCfg::Script), |
|
334 |
|i| cfgc_single_arg(i, "MAP", a_line, GameCfg::MapType), |
|
335 |
|i| cfgc_single_arg(i, "MAPGEN", u32_line, GameCfg::MapGenerator), |
|
336 |
|i| cfgc_single_arg(i, "MAZE_SIZE", u32_line, GameCfg::MazeSize), |
|
337 |
|i| cfgc_single_arg(i, "TEMPLATE", u32_line, GameCfg::Template), |
|
338 |
|i| cfgc_single_arg(i, "FEATURE_SIZE", u32_line, GameCfg::FeatureSize), |
|
339 |
|i| cfgc_single_arg(i, "SEED", a_line, GameCfg::Seed), |
|
340 |
|i| cfgc_single_arg(i, "DRAWNMAP", a_line, GameCfg::DrawnMap), |
|
341 |
|i| { |
|
342 |
precededc( |
|
343 |
i, |
|
344 |
|i| terminatedc(i, hw_tag("AMMO"), eol), |
|
345 |
|i| { |
|
346 |
let (i, name) = a_line(i)?; |
|
347 |
let (i, value) = opt_arg(i)?; |
|
348 |
Ok((i, GameCfg::Ammo(name, value))) |
|
349 |
}, |
|
350 |
) |
|
351 |
}, |
|
352 |
|i| { |
|
353 |
precededc( |
|
354 |
i, |
|
355 |
|i| terminatedc(i, hw_tag("SCHEME"), eol), |
|
356 |
|i| { |
|
357 |
let (i, name) = a_line(i)?; |
|
358 |
let (i, values) = alt(( |
|
359 |
|i: &'a [u8]| peek!(i, end_of_message).map(|(i, _)| (i, None)), |
|
360 |
|i| { |
|
361 |
precededc(i, eol, |i| separated_list(eol, a_line)(i)) |
|
362 |
.map(|(i, v)| (i, Some(v))) |
|
363 |
}, |
|
364 |
))(i)?; |
|
365 |
Ok((i, GameCfg::Scheme(name, values.unwrap_or_default()))) |
|
366 |
}, |
|
367 |
) |
|
368 |
}, |
|
369 |
)), |
|
370 |
)?; |
|
371 |
Ok((i, Cfg(cfg))) |
|
372 |
} |
|
12133 | 373 |
|
14804 | 374 |
fn server_var_message(input: &[u8]) -> HWResult<HWProtocolMessage> { |
375 |
precededc( |
|
376 |
input, |
|
377 |
hw_tag("SET_SERVER_VAR\n"), |
|
378 |
alt(( |
|
379 |
|i| { |
|
380 |
precededc(i, hw_tag("MOTD_NEW\n"), a_line) |
|
381 |
.map(|(i, s)| (i, SetServerVar(ServerVar::MOTDNew(s)))) |
|
382 |
}, |
|
383 |
|i| { |
|
384 |
precededc(i, hw_tag("MOTD_OLD\n"), a_line) |
|
385 |
.map(|(i, s)| (i, SetServerVar(ServerVar::MOTDOld(s)))) |
|
386 |
}, |
|
387 |
|i| { |
|
388 |
precededc(i, hw_tag("LATEST_PROTO\n"), u16_line) |
|
389 |
.map(|(i, n)| (i, SetServerVar(ServerVar::LatestProto(n)))) |
|
390 |
}, |
|
391 |
)), |
|
392 |
) |
|
393 |
} |
|
394 |
||
14796 | 395 |
fn complex_message(input: &[u8]) -> HWResult<HWProtocolMessage> { |
396 |
alt(( |
|
397 |
|i| { |
|
398 |
precededc( |
|
399 |
i, |
|
400 |
|i| terminatedc(i, hw_tag("PASSWORD"), eol), |
|
401 |
|i| { |
|
402 |
let (i, pass) = terminatedc(i, a_line, eol)?; |
|
403 |
let (i, salt) = a_line(i)?; |
|
404 |
Ok((i, Password(pass, salt))) |
|
405 |
}, |
|
406 |
) |
|
407 |
}, |
|
408 |
|i| { |
|
409 |
precededc( |
|
410 |
i, |
|
411 |
|i| terminatedc(i, hw_tag("CHECKER"), eol), |
|
412 |
|i| { |
|
413 |
let (i, protocol) = terminatedc(i, u16_line, eol)?; |
|
414 |
let (i, name) = terminatedc(i, a_line, eol)?; |
|
415 |
let (i, pass) = a_line(i)?; |
|
416 |
Ok((i, Checker(protocol, name, pass))) |
|
417 |
}, |
|
418 |
) |
|
419 |
}, |
|
420 |
|i| { |
|
421 |
precededc( |
|
422 |
i, |
|
423 |
|i| terminatedc(i, hw_tag("CREATE_ROOM"), eol), |
|
424 |
|i| { |
|
425 |
let (i, name) = a_line(i)?; |
|
426 |
let (i, pass) = opt_arg(i)?; |
|
427 |
Ok((i, CreateRoom(name, pass))) |
|
428 |
}, |
|
429 |
) |
|
430 |
}, |
|
431 |
|i| { |
|
432 |
precededc( |
|
433 |
i, |
|
434 |
|i| terminatedc(i, hw_tag("JOIN_ROOM"), eol), |
|
435 |
|i| { |
|
436 |
let (i, name) = a_line(i)?; |
|
437 |
let (i, pass) = opt_arg(i)?; |
|
438 |
Ok((i, JoinRoom(name, pass))) |
|
439 |
}, |
|
440 |
) |
|
441 |
}, |
|
442 |
|i| { |
|
443 |
precededc( |
|
444 |
i, |
|
445 |
|i| terminatedc(i, hw_tag("ADD_TEAM"), eol), |
|
446 |
|i| { |
|
447 |
let (i, name) = terminatedc(i, a_line, eol)?; |
|
448 |
let (i, color) = terminatedc(i, u8_line, eol)?; |
|
449 |
let (i, grave) = terminatedc(i, a_line, eol)?; |
|
450 |
let (i, fort) = terminatedc(i, a_line, eol)?; |
|
451 |
let (i, voice_pack) = terminatedc(i, a_line, eol)?; |
|
452 |
let (i, flag) = terminatedc(i, a_line, eol)?; |
|
453 |
let (i, difficulty) = terminatedc(i, u8_line, eol)?; |
|
454 |
let (i, hedgehogs) = hedgehog_array(i)?; |
|
455 |
Ok(( |
|
456 |
i, |
|
457 |
AddTeam(Box::new(TeamInfo { |
|
14806
a1077e8d26f4
implement watch message apart from replay deserializing
alfadur
parents:
14804
diff
changeset
|
458 |
owner: String::new(), |
14796 | 459 |
name, |
460 |
color, |
|
461 |
grave, |
|
462 |
fort, |
|
463 |
voice_pack, |
|
464 |
flag, |
|
465 |
difficulty, |
|
466 |
hedgehogs, |
|
467 |
hedgehogs_number: 0, |
|
468 |
})), |
|
469 |
)) |
|
470 |
}, |
|
471 |
) |
|
472 |
}, |
|
473 |
|i| { |
|
474 |
precededc( |
|
475 |
i, |
|
476 |
|i| terminatedc(i, hw_tag("HH_NUM"), eol), |
|
477 |
|i| { |
|
478 |
let (i, name) = terminatedc(i, a_line, eol)?; |
|
479 |
let (i, count) = u8_line(i)?; |
|
480 |
Ok((i, SetHedgehogsNumber(name, count))) |
|
481 |
}, |
|
482 |
) |
|
483 |
}, |
|
484 |
|i| { |
|
485 |
precededc( |
|
486 |
i, |
|
487 |
|i| terminatedc(i, hw_tag("TEAM_COLOR"), eol), |
|
488 |
|i| { |
|
489 |
let (i, name) = terminatedc(i, a_line, eol)?; |
|
490 |
let (i, color) = u8_line(i)?; |
|
491 |
Ok((i, SetTeamColor(name, color))) |
|
492 |
}, |
|
493 |
) |
|
494 |
}, |
|
495 |
|i| { |
|
496 |
precededc( |
|
497 |
i, |
|
498 |
|i| terminatedc(i, hw_tag("BAN"), eol), |
|
499 |
|i| { |
|
500 |
let (i, n) = terminatedc(i, a_line, eol)?; |
|
501 |
let (i, r) = terminatedc(i, a_line, eol)?; |
|
502 |
let (i, t) = u32_line(i)?; |
|
503 |
Ok((i, Ban(n, r, t))) |
|
504 |
}, |
|
505 |
) |
|
506 |
}, |
|
507 |
|i| { |
|
508 |
precededc( |
|
509 |
i, |
|
510 |
|i| terminatedc(i, hw_tag("BAN_IP"), eol), |
|
511 |
|i| { |
|
512 |
let (i, n) = terminatedc(i, a_line, eol)?; |
|
513 |
let (i, r) = terminatedc(i, a_line, eol)?; |
|
514 |
let (i, t) = u32_line(i)?; |
|
515 |
Ok((i, BanIP(n, r, t))) |
|
516 |
}, |
|
517 |
) |
|
518 |
}, |
|
519 |
|i| { |
|
520 |
precededc( |
|
521 |
i, |
|
522 |
|i| terminatedc(i, hw_tag("BAN_NICK"), eol), |
|
523 |
|i| { |
|
524 |
let (i, n) = terminatedc(i, a_line, eol)?; |
|
525 |
let (i, r) = terminatedc(i, a_line, eol)?; |
|
526 |
let (i, t) = u32_line(i)?; |
|
527 |
Ok((i, BanNick(n, r, t))) |
|
528 |
}, |
|
529 |
) |
|
530 |
}, |
|
531 |
))(input) |
|
532 |
} |
|
13422 | 533 |
|
14796 | 534 |
fn empty_message(input: &[u8]) -> HWResult<HWProtocolMessage> { |
535 |
let (i, _) = alt((end_of_message, eol))(input)?; |
|
536 |
Ok((i, Empty)) |
|
537 |
} |
|
12137
193dfdcb0620
- Use logging facilities instead of plain println!
unc0rr
parents:
12136
diff
changeset
|
538 |
|
14796 | 539 |
fn malformed_message(input: &[u8]) -> HWResult<HWProtocolMessage> { |
540 |
let (i, _) = separated_listc(input, eol, a_line)?; |
|
541 |
Ok((i, Malformed)) |
|
542 |
} |
|
12137
193dfdcb0620
- Use logging facilities instead of plain println!
unc0rr
parents:
12136
diff
changeset
|
543 |
|
14796 | 544 |
fn message(input: &[u8]) -> HWResult<HWProtocolMessage> { |
545 |
alt(( |
|
546 |
|i| { |
|
547 |
terminatedc( |
|
548 |
i, |
|
549 |
alt(( |
|
550 |
no_arg_message, |
|
551 |
single_arg_message, |
|
552 |
cmd_message, |
|
553 |
config_message, |
|
14804 | 554 |
server_var_message, |
14796 | 555 |
complex_message, |
556 |
)), |
|
557 |
end_of_message, |
|
558 |
) |
|
559 |
}, |
|
560 |
|i| terminatedc(i, malformed_message, end_of_message), |
|
561 |
empty_message, |
|
562 |
))(input) |
|
563 |
} |
|
12133 | 564 |
|
14796 | 565 |
pub fn extract_messages<'a>(input: &'a [u8]) -> HWResult<Vec<HWProtocolMessage>> { |
566 |
many0(|i: &'a [u8]| complete!(i, message))(input) |
|
567 |
} |
|
12133 | 568 |
|
13713 | 569 |
#[cfg(test)] |
14798 | 570 |
mod test { |
571 |
use super::{extract_messages, message}; |
|
572 |
use crate::protocol::{messages::HWProtocolMessage::*, test::gen_proto_msg}; |
|
573 |
use proptest::{proptest, proptest_helper}; |
|
574 |
||
575 |
#[cfg(test)] |
|
576 |
proptest! { |
|
577 |
#[test] |
|
578 |
fn is_parser_composition_idempotent(ref msg in gen_proto_msg()) { |
|
579 |
println!("!! Msg: {:?}, Bytes: {:?} !!", msg, msg.to_raw_protocol().as_bytes()); |
|
580 |
assert_eq!(message(msg.to_raw_protocol().as_bytes()), Ok((&b""[..], msg.clone()))) |
|
581 |
} |
|
582 |
} |
|
583 |
||
13119
1e39b8749072
separated the server logic from all the async io mess.
alfadur
parents:
12142
diff
changeset
|
584 |
#[test] |
14798 | 585 |
fn parse_test() { |
586 |
assert_eq!(message(b"PING\n\n"), Ok((&b""[..], Ping))); |
|
587 |
assert_eq!(message(b"START_GAME\n\n"), Ok((&b""[..], StartGame))); |
|
588 |
assert_eq!( |
|
589 |
message(b"NICK\nit's me\n\n"), |
|
590 |
Ok((&b""[..], Nick("it's me".to_string()))) |
|
591 |
); |
|
592 |
assert_eq!(message(b"PROTO\n51\n\n"), Ok((&b""[..], Proto(51)))); |
|
593 |
assert_eq!( |
|
594 |
message(b"QUIT\nbye-bye\n\n"), |
|
595 |
Ok((&b""[..], Quit(Some("bye-bye".to_string())))) |
|
596 |
); |
|
597 |
assert_eq!(message(b"QUIT\n\n"), Ok((&b""[..], Quit(None)))); |
|
598 |
assert_eq!( |
|
599 |
message(b"CMD\nwatch demo\n\n"), |
|
600 |
Ok((&b""[..], Watch("demo".to_string()))) |
|
601 |
); |
|
602 |
assert_eq!( |
|
603 |
message(b"BAN\nme\nbad\n77\n\n"), |
|
604 |
Ok((&b""[..], Ban("me".to_string(), "bad".to_string(), 77))) |
|
605 |
); |
|
606 |
||
607 |
assert_eq!(message(b"CMD\nPART\n\n"), Ok((&b""[..], Part(None)))); |
|
608 |
assert_eq!( |
|
609 |
message(b"CMD\nPART _msg_\n\n"), |
|
610 |
Ok((&b""[..], Part(Some("_msg_".to_string())))) |
|
611 |
); |
|
612 |
||
613 |
assert_eq!(message(b"CMD\nRND\n\n"), Ok((&b""[..], Rnd(vec![])))); |
|
614 |
assert_eq!( |
|
615 |
message(b"CMD\nRND A B\n\n"), |
|
616 |
Ok((&b""[..], Rnd(vec![String::from("A"), String::from("B")]))) |
|
617 |
); |
|
618 |
||
619 |
assert_eq!( |
|
620 |
extract_messages(b"QUIT\n1\n2\n\n"), |
|
621 |
Ok((&b""[..], vec![Malformed])) |
|
622 |
); |
|
623 |
||
624 |
assert_eq!( |
|
625 |
extract_messages(b"PING\n\nPING\n\nP"), |
|
626 |
Ok((&b"P"[..], vec![Ping, Ping])) |
|
627 |
); |
|
628 |
assert_eq!( |
|
629 |
extract_messages(b"SING\n\nPING\n\n"), |
|
630 |
Ok((&b""[..], vec![Malformed, Ping])) |
|
631 |
); |
|
632 |
assert_eq!( |
|
633 |
extract_messages(b"\n\n\n\nPING\n\n"), |
|
634 |
Ok((&b""[..], vec![Empty, Empty, Ping])) |
|
635 |
); |
|
636 |
assert_eq!( |
|
637 |
extract_messages(b"\n\n\nPING\n\n"), |
|
638 |
Ok((&b""[..], vec![Empty, Empty, Ping])) |
|
639 |
); |
|
13119
1e39b8749072
separated the server logic from all the async io mess.
alfadur
parents:
12142
diff
changeset
|
640 |
} |
1e39b8749072
separated the server logic from all the async io mess.
alfadur
parents:
12142
diff
changeset
|
641 |
} |