rust/hedgewars-network-protocol/src/parser.rs
changeset 15804 747278149393
child 15810 ee84e417d8d0
equal deleted inserted replaced
15803:b06b33cf0a89 15804:747278149393
       
     1 /** The parsers for the chat and multiplayer protocol. The main parser is `message`.
       
     2  * # Protocol
       
     3  * All messages consist of `\n`-separated strings. The end of a message is
       
     4  * indicated by a double newline - `\n\n`.
       
     5  *
       
     6  * For example, a nullary command like PING will be actually sent as `PING\n\n`.
       
     7  * A unary command, such as `START_GAME nick` will be actually sent as `START_GAME\nnick\n\n`.
       
     8  */
       
     9 use nom::{
       
    10     branch::alt,
       
    11     bytes::complete::{tag, tag_no_case, take_until, take_while},
       
    12     character::complete::{newline, not_line_ending},
       
    13     combinator::{map, peek},
       
    14     error::{ErrorKind, ParseError},
       
    15     multi::separated_list0,
       
    16     sequence::{delimited, pair, preceded, terminated, tuple},
       
    17     Err, IResult,
       
    18 };
       
    19 
       
    20 use std::{
       
    21     num::ParseIntError,
       
    22     str,
       
    23     str::{FromStr, Utf8Error},
       
    24 };
       
    25 
       
    26 use crate::messages::{HwProtocolMessage, HwProtocolMessage::*};
       
    27 use crate::types::{GameCfg, HedgehogInfo, ServerVar, TeamInfo, VoteType};
       
    28 
       
    29 #[derive(Debug, PartialEq)]
       
    30 pub struct HwProtocolError {}
       
    31 
       
    32 impl HwProtocolError {
       
    33     pub fn new() -> Self {
       
    34         HwProtocolError {}
       
    35     }
       
    36 }
       
    37 
       
    38 impl<I> ParseError<I> for HwProtocolError {
       
    39     fn from_error_kind(_input: I, _kind: ErrorKind) -> Self {
       
    40         HwProtocolError::new()
       
    41     }
       
    42 
       
    43     fn append(_input: I, _kind: ErrorKind, _other: Self) -> Self {
       
    44         HwProtocolError::new()
       
    45     }
       
    46 }
       
    47 
       
    48 impl From<Utf8Error> for HwProtocolError {
       
    49     fn from(_: Utf8Error) -> Self {
       
    50         HwProtocolError::new()
       
    51     }
       
    52 }
       
    53 
       
    54 impl From<ParseIntError> for HwProtocolError {
       
    55     fn from(_: ParseIntError) -> Self {
       
    56         HwProtocolError::new()
       
    57     }
       
    58 }
       
    59 
       
    60 pub type HwResult<'a, O> = IResult<&'a [u8], O, HwProtocolError>;
       
    61 
       
    62 fn end_of_message(input: &[u8]) -> HwResult<&[u8]> {
       
    63     tag("\n\n")(input)
       
    64 }
       
    65 
       
    66 fn convert_utf8(input: &[u8]) -> HwResult<&str> {
       
    67     match str::from_utf8(input) {
       
    68         Ok(str) => Ok((b"", str)),
       
    69         Err(utf_err) => Result::Err(Err::Failure(utf_err.into())),
       
    70     }
       
    71 }
       
    72 
       
    73 fn convert_from_str<T>(str: &str) -> HwResult<T>
       
    74 where
       
    75     T: FromStr<Err = ParseIntError>,
       
    76 {
       
    77     match T::from_str(str) {
       
    78         Ok(x) => Ok((b"", x)),
       
    79         Err(format_err) => Result::Err(Err::Failure(format_err.into())),
       
    80     }
       
    81 }
       
    82 
       
    83 fn str_line(input: &[u8]) -> HwResult<&str> {
       
    84     let (i, text) = not_line_ending(<&[u8]>::clone(&input))?;
       
    85     if i != input {
       
    86         Ok((i, convert_utf8(text)?.1))
       
    87     } else {
       
    88         Err(Err::Error(HwProtocolError::new()))
       
    89     }
       
    90 }
       
    91 
       
    92 fn a_line(input: &[u8]) -> HwResult<String> {
       
    93     map(str_line, String::from)(input)
       
    94 }
       
    95 
       
    96 fn cmd_arg(input: &[u8]) -> HwResult<String> {
       
    97     let delimiters = b" \n";
       
    98     let (i, str) = take_while(move |c| !delimiters.contains(&c))(<&[u8]>::clone(&input))?;
       
    99     if i != input {
       
   100         Ok((i, convert_utf8(str)?.1.to_string()))
       
   101     } else {
       
   102         Err(Err::Error(HwProtocolError::new()))
       
   103     }
       
   104 }
       
   105 
       
   106 fn u8_line(input: &[u8]) -> HwResult<u8> {
       
   107     let (i, str) = str_line(input)?;
       
   108     Ok((i, convert_from_str(str)?.1))
       
   109 }
       
   110 
       
   111 fn u16_line(input: &[u8]) -> HwResult<u16> {
       
   112     let (i, str) = str_line(input)?;
       
   113     Ok((i, convert_from_str(str)?.1))
       
   114 }
       
   115 
       
   116 fn u32_line(input: &[u8]) -> HwResult<u32> {
       
   117     let (i, str) = str_line(input)?;
       
   118     Ok((i, convert_from_str(str)?.1))
       
   119 }
       
   120 
       
   121 fn yes_no_line(input: &[u8]) -> HwResult<bool> {
       
   122     alt((
       
   123         map(tag_no_case(b"YES"), |_| true),
       
   124         map(tag_no_case(b"NO"), |_| false),
       
   125     ))(input)
       
   126 }
       
   127 
       
   128 fn opt_arg<'a>(input: &'a [u8]) -> HwResult<'a, Option<String>> {
       
   129     alt((
       
   130         map(peek(end_of_message), |_| None),
       
   131         map(preceded(tag("\n"), a_line), Some),
       
   132     ))(input)
       
   133 }
       
   134 
       
   135 fn spaces(input: &[u8]) -> HwResult<&[u8]> {
       
   136     preceded(tag(" "), take_while(|c| c == b' '))(input)
       
   137 }
       
   138 
       
   139 fn opt_space_arg<'a>(input: &'a [u8]) -> HwResult<'a, Option<String>> {
       
   140     alt((
       
   141         map(peek(end_of_message), |_| None),
       
   142         map(preceded(spaces, a_line), Some),
       
   143     ))(input)
       
   144 }
       
   145 
       
   146 fn hedgehog_array(input: &[u8]) -> HwResult<[HedgehogInfo; 8]> {
       
   147     fn hedgehog_line(input: &[u8]) -> HwResult<HedgehogInfo> {
       
   148         map(
       
   149             tuple((terminated(a_line, newline), a_line)),
       
   150             |(name, hat)| HedgehogInfo { name, hat },
       
   151         )(input)
       
   152     }
       
   153 
       
   154     let (i, (h1, h2, h3, h4, h5, h6, h7, h8)) = tuple((
       
   155         terminated(hedgehog_line, newline),
       
   156         terminated(hedgehog_line, newline),
       
   157         terminated(hedgehog_line, newline),
       
   158         terminated(hedgehog_line, newline),
       
   159         terminated(hedgehog_line, newline),
       
   160         terminated(hedgehog_line, newline),
       
   161         terminated(hedgehog_line, newline),
       
   162         hedgehog_line,
       
   163     ))(input)?;
       
   164 
       
   165     Ok((i, [h1, h2, h3, h4, h5, h6, h7, h8]))
       
   166 }
       
   167 
       
   168 fn voting(input: &[u8]) -> HwResult<VoteType> {
       
   169     alt((
       
   170         map(tag_no_case("PAUSE"), |_| VoteType::Pause),
       
   171         map(tag_no_case("NEWSEED"), |_| VoteType::NewSeed),
       
   172         map(
       
   173             preceded(pair(tag_no_case("KICK"), spaces), a_line),
       
   174             VoteType::Kick,
       
   175         ),
       
   176         map(
       
   177             preceded(pair(tag_no_case("HEDGEHOGS"), spaces), u8_line),
       
   178             VoteType::HedgehogsPerTeam,
       
   179         ),
       
   180         map(preceded(tag_no_case("MAP"), opt_space_arg), VoteType::Map),
       
   181     ))(input)
       
   182 }
       
   183 
       
   184 fn no_arg_message(input: &[u8]) -> HwResult<HwProtocolMessage> {
       
   185     fn message<'a>(
       
   186         name: &'a str,
       
   187         msg: HwProtocolMessage,
       
   188     ) -> impl Fn(&'a [u8]) -> HwResult<'a, HwProtocolMessage> {
       
   189         move |i| map(tag(name), |_| msg.clone())(i)
       
   190     }
       
   191 
       
   192     alt((
       
   193         message("PING", Ping),
       
   194         message("PONG", Pong),
       
   195         message("LIST", List),
       
   196         message("BANLIST", BanList),
       
   197         message("GET_SERVER_VAR", GetServerVar),
       
   198         message("TOGGLE_READY", ToggleReady),
       
   199         message("START_GAME", StartGame),
       
   200         message("TOGGLE_RESTRICT_JOINS", ToggleRestrictJoin),
       
   201         message("TOGGLE_RESTRICT_TEAMS", ToggleRestrictTeams),
       
   202         message("TOGGLE_REGISTERED_ONLY", ToggleRegisteredOnly),
       
   203     ))(input)
       
   204 }
       
   205 
       
   206 fn single_arg_message(input: &[u8]) -> HwResult<HwProtocolMessage> {
       
   207     fn message<'a, T, F, G>(
       
   208         name: &'a str,
       
   209         parser: F,
       
   210         constructor: G,
       
   211     ) -> impl FnMut(&'a [u8]) -> HwResult<'a, HwProtocolMessage>
       
   212     where
       
   213         F: Fn(&[u8]) -> HwResult<T>,
       
   214         G: Fn(T) -> HwProtocolMessage,
       
   215     {
       
   216         map(preceded(tag(name), parser), constructor)
       
   217     }
       
   218 
       
   219     alt((
       
   220         message("NICK\n", a_line, Nick),
       
   221         message("INFO\n", a_line, Info),
       
   222         message("CHAT\n", a_line, Chat),
       
   223         message("PART", opt_arg, Part),
       
   224         message("FOLLOW\n", a_line, Follow),
       
   225         message("KICK\n", a_line, Kick),
       
   226         message("UNBAN\n", a_line, Unban),
       
   227         message("EM\n", a_line, EngineMessage),
       
   228         message("TEAMCHAT\n", a_line, TeamChat),
       
   229         message("ROOM_NAME\n", a_line, RoomName),
       
   230         message("REMOVE_TEAM\n", a_line, RemoveTeam),
       
   231         message("ROUNDFINISHED", opt_arg, |_| RoundFinished),
       
   232         message("PROTO\n", u16_line, Proto),
       
   233         message("QUIT", opt_arg, Quit),
       
   234     ))(input)
       
   235 }
       
   236 
       
   237 fn cmd_message<'a>(input: &'a [u8]) -> HwResult<'a, HwProtocolMessage> {
       
   238     fn cmd_no_arg<'a>(
       
   239         name: &'a str,
       
   240         msg: HwProtocolMessage,
       
   241     ) -> impl Fn(&'a [u8]) -> HwResult<'a, HwProtocolMessage> {
       
   242         move |i| map(tag_no_case(name), |_| msg.clone())(i)
       
   243     }
       
   244 
       
   245     fn cmd_single_arg<'a, T, F, G>(
       
   246         name: &'a str,
       
   247         parser: F,
       
   248         constructor: G,
       
   249     ) -> impl FnMut(&'a [u8]) -> HwResult<'a, HwProtocolMessage>
       
   250     where
       
   251         F: Fn(&'a [u8]) -> HwResult<'a, T>,
       
   252         G: Fn(T) -> HwProtocolMessage,
       
   253     {
       
   254         map(
       
   255             preceded(pair(tag_no_case(name), spaces), parser),
       
   256             constructor,
       
   257         )
       
   258     }
       
   259 
       
   260     fn cmd_no_arg_message(input: &[u8]) -> HwResult<HwProtocolMessage> {
       
   261         alt((
       
   262             cmd_no_arg("STATS", Stats),
       
   263             cmd_no_arg("FIX", Fix),
       
   264             cmd_no_arg("UNFIX", Unfix),
       
   265             cmd_no_arg("REGISTERED_ONLY", ToggleServerRegisteredOnly),
       
   266             cmd_no_arg("SUPER_POWER", SuperPower),
       
   267         ))(input)
       
   268     }
       
   269 
       
   270     fn cmd_single_arg_message(input: &[u8]) -> HwResult<HwProtocolMessage> {
       
   271         alt((
       
   272             cmd_single_arg("RESTART_SERVER", |i| tag("YES")(i), |_| RestartServer),
       
   273             cmd_single_arg("DELEGATE", a_line, Delegate),
       
   274             cmd_single_arg("DELETE", a_line, Delete),
       
   275             cmd_single_arg("SAVEROOM", a_line, SaveRoom),
       
   276             cmd_single_arg("LOADROOM", a_line, LoadRoom),
       
   277             cmd_single_arg("GLOBAL", a_line, Global),
       
   278             cmd_single_arg("WATCH", u32_line, Watch),
       
   279             cmd_single_arg("VOTE", yes_no_line, Vote),
       
   280             cmd_single_arg("FORCE", yes_no_line, ForceVote),
       
   281             cmd_single_arg("INFO", a_line, Info),
       
   282             cmd_single_arg("MAXTEAMS", u8_line, MaxTeams),
       
   283             cmd_single_arg("CALLVOTE", voting, |v| CallVote(Some(v))),
       
   284         ))(input)
       
   285     }
       
   286 
       
   287     preceded(
       
   288         tag("CMD\n"),
       
   289         alt((
       
   290             cmd_no_arg_message,
       
   291             cmd_single_arg_message,
       
   292             map(tag_no_case("CALLVOTE"), |_| CallVote(None)),
       
   293             map(preceded(tag_no_case("GREETING"), opt_space_arg), Greeting),
       
   294             map(preceded(tag_no_case("PART"), opt_space_arg), Part),
       
   295             map(preceded(tag_no_case("QUIT"), opt_space_arg), Quit),
       
   296             map(
       
   297                 preceded(
       
   298                     tag_no_case("SAVE"),
       
   299                     pair(preceded(spaces, cmd_arg), preceded(spaces, cmd_arg)),
       
   300                 ),
       
   301                 |(n, l)| Save(n, l),
       
   302             ),
       
   303             map(
       
   304                 preceded(
       
   305                     tag_no_case("RND"),
       
   306                     alt((
       
   307                         map(peek(end_of_message), |_| vec![]),
       
   308                         preceded(spaces, separated_list0(spaces, cmd_arg)),
       
   309                     )),
       
   310                 ),
       
   311                 Rnd,
       
   312             ),
       
   313         )),
       
   314     )(input)
       
   315 }
       
   316 
       
   317 fn config_message<'a>(input: &'a [u8]) -> HwResult<'a, HwProtocolMessage> {
       
   318     fn cfg_single_arg<'a, T, F, G>(
       
   319         name: &'a str,
       
   320         parser: F,
       
   321         constructor: G,
       
   322     ) -> impl FnMut(&'a [u8]) -> HwResult<'a, GameCfg>
       
   323     where
       
   324         F: Fn(&[u8]) -> HwResult<T>,
       
   325         G: Fn(T) -> GameCfg,
       
   326     {
       
   327         map(preceded(pair(tag(name), newline), parser), constructor)
       
   328     }
       
   329 
       
   330     let (i, cfg) = preceded(
       
   331         tag("CFG\n"),
       
   332         alt((
       
   333             cfg_single_arg("THEME", a_line, GameCfg::Theme),
       
   334             cfg_single_arg("SCRIPT", a_line, GameCfg::Script),
       
   335             cfg_single_arg("MAP", a_line, GameCfg::MapType),
       
   336             cfg_single_arg("MAPGEN", u32_line, GameCfg::MapGenerator),
       
   337             cfg_single_arg("MAZE_SIZE", u32_line, GameCfg::MazeSize),
       
   338             cfg_single_arg("TEMPLATE", u32_line, GameCfg::Template),
       
   339             cfg_single_arg("FEATURE_SIZE", u32_line, GameCfg::FeatureSize),
       
   340             cfg_single_arg("SEED", a_line, GameCfg::Seed),
       
   341             cfg_single_arg("DRAWNMAP", a_line, GameCfg::DrawnMap),
       
   342             preceded(pair(tag("AMMO"), newline), |i| {
       
   343                 let (i, name) = a_line(i)?;
       
   344                 let (i, value) = opt_arg(i)?;
       
   345                 Ok((i, GameCfg::Ammo(name, value)))
       
   346             }),
       
   347             preceded(
       
   348                 pair(tag("SCHEME"), newline),
       
   349                 map(
       
   350                     pair(
       
   351                         a_line,
       
   352                         alt((
       
   353                             map(peek(end_of_message), |_| None),
       
   354                             map(preceded(newline, separated_list0(newline, a_line)), Some),
       
   355                         )),
       
   356                     ),
       
   357                     |(name, values)| GameCfg::Scheme(name, values.unwrap_or_default()),
       
   358                 ),
       
   359             ),
       
   360         )),
       
   361     )(input)?;
       
   362     Ok((i, Cfg(cfg)))
       
   363 }
       
   364 
       
   365 fn server_var_message(input: &[u8]) -> HwResult<HwProtocolMessage> {
       
   366     map(
       
   367         preceded(
       
   368             tag("SET_SERVER_VAR\n"),
       
   369             alt((
       
   370                 map(preceded(tag("MOTD_NEW\n"), a_line), ServerVar::MOTDNew),
       
   371                 map(preceded(tag("MOTD_OLD\n"), a_line), ServerVar::MOTDOld),
       
   372                 map(
       
   373                     preceded(tag("LATEST_PROTO\n"), u16_line),
       
   374                     ServerVar::LatestProto,
       
   375                 ),
       
   376             )),
       
   377         ),
       
   378         SetServerVar,
       
   379     )(input)
       
   380 }
       
   381 
       
   382 fn complex_message(input: &[u8]) -> HwResult<HwProtocolMessage> {
       
   383     alt((
       
   384         preceded(
       
   385             pair(tag("PASSWORD"), newline),
       
   386             map(pair(terminated(a_line, newline), a_line), |(pass, salt)| {
       
   387                 Password(pass, salt)
       
   388             }),
       
   389         ),
       
   390         preceded(
       
   391             pair(tag("CHECKER"), newline),
       
   392             map(
       
   393                 tuple((
       
   394                     terminated(u16_line, newline),
       
   395                     terminated(a_line, newline),
       
   396                     a_line,
       
   397                 )),
       
   398                 |(protocol, name, pass)| Checker(protocol, name, pass),
       
   399             ),
       
   400         ),
       
   401         preceded(
       
   402             pair(tag("CREATE_ROOM"), newline),
       
   403             map(pair(a_line, opt_arg), |(name, pass)| CreateRoom(name, pass)),
       
   404         ),
       
   405         preceded(
       
   406             pair(tag("JOIN_ROOM"), newline),
       
   407             map(pair(a_line, opt_arg), |(name, pass)| JoinRoom(name, pass)),
       
   408         ),
       
   409         preceded(
       
   410             pair(tag("ADD_TEAM"), newline),
       
   411             map(
       
   412                 tuple((
       
   413                     terminated(a_line, newline),
       
   414                     terminated(u8_line, newline),
       
   415                     terminated(a_line, newline),
       
   416                     terminated(a_line, newline),
       
   417                     terminated(a_line, newline),
       
   418                     terminated(a_line, newline),
       
   419                     terminated(u8_line, newline),
       
   420                     hedgehog_array,
       
   421                 )),
       
   422                 |(name, color, grave, fort, voice_pack, flag, difficulty, hedgehogs)| {
       
   423                     AddTeam(Box::new(TeamInfo {
       
   424                         owner: String::new(),
       
   425                         name,
       
   426                         color,
       
   427                         grave,
       
   428                         fort,
       
   429                         voice_pack,
       
   430                         flag,
       
   431                         difficulty,
       
   432                         hedgehogs,
       
   433                         hedgehogs_number: 0,
       
   434                     }))
       
   435                 },
       
   436             ),
       
   437         ),
       
   438         preceded(
       
   439             pair(tag("HH_NUM"), newline),
       
   440             map(
       
   441                 pair(terminated(a_line, newline), u8_line),
       
   442                 |(name, count)| SetHedgehogsNumber(name, count),
       
   443             ),
       
   444         ),
       
   445         preceded(
       
   446             pair(tag("TEAM_COLOR"), newline),
       
   447             map(
       
   448                 pair(terminated(a_line, newline), u8_line),
       
   449                 |(name, color)| SetTeamColor(name, color),
       
   450             ),
       
   451         ),
       
   452         preceded(
       
   453             pair(tag("BAN"), newline),
       
   454             map(
       
   455                 tuple((
       
   456                     terminated(a_line, newline),
       
   457                     terminated(a_line, newline),
       
   458                     u32_line,
       
   459                 )),
       
   460                 |(name, reason, time)| Ban(name, reason, time),
       
   461             ),
       
   462         ),
       
   463         preceded(
       
   464             pair(tag("BAN_IP"), newline),
       
   465             map(
       
   466                 tuple((
       
   467                     terminated(a_line, newline),
       
   468                     terminated(a_line, newline),
       
   469                     u32_line,
       
   470                 )),
       
   471                 |(ip, reason, time)| BanIp(ip, reason, time),
       
   472             ),
       
   473         ),
       
   474         preceded(
       
   475             pair(tag("BAN_NICK"), newline),
       
   476             map(
       
   477                 tuple((
       
   478                     terminated(a_line, newline),
       
   479                     terminated(a_line, newline),
       
   480                     u32_line,
       
   481                 )),
       
   482                 |(nick, reason, time)| BanNick(nick, reason, time),
       
   483             ),
       
   484         ),
       
   485     ))(input)
       
   486 }
       
   487 
       
   488 pub fn malformed_message(input: &[u8]) -> HwResult<()> {
       
   489     map(terminated(take_until(&b"\n\n"[..]), end_of_message), |_| ())(input)
       
   490 }
       
   491 
       
   492 pub fn message(input: &[u8]) -> HwResult<HwProtocolMessage> {
       
   493     delimited(
       
   494         take_while(|c| c == b'\n'),
       
   495         alt((
       
   496             no_arg_message,
       
   497             single_arg_message,
       
   498             cmd_message,
       
   499             config_message,
       
   500             server_var_message,
       
   501             complex_message,
       
   502         )),
       
   503         end_of_message,
       
   504     )(input)
       
   505 }