gameServer2/src/server/handlers/inroom.rs
changeset 14420 06672690d71b
parent 14419 6843c4551cde
child 14421 96624a6cdb93
equal deleted inserted replaced
14419:6843c4551cde 14420:06672690d71b
     1 use mio;
       
     2 
       
     3 use crate::{
       
     4     server::{
       
     5         coretypes::{
       
     6             ClientId, RoomId, Voting, VoteType, GameCfg,
       
     7             MAX_HEDGEHOGS_PER_TEAM
       
     8         },
       
     9         core::HWServer,
       
    10         room::{HWRoom, RoomFlags},
       
    11         actions::{Action, Action::*}
       
    12     },
       
    13     protocol::messages::{
       
    14         HWProtocolMessage,
       
    15         HWServerMessage::*,
       
    16         server_chat
       
    17     },
       
    18     utils::is_name_illegal
       
    19 };
       
    20 use std::{
       
    21     mem::swap
       
    22 };
       
    23 use base64::{encode, decode};
       
    24 use super::common::rnd_reply;
       
    25 use log::*;
       
    26 
       
    27 #[derive(Clone)]
       
    28 struct ByMsg<'a> {
       
    29     messages: &'a[u8]
       
    30 }
       
    31 
       
    32 impl <'a> Iterator for ByMsg<'a> {
       
    33     type Item = &'a[u8];
       
    34 
       
    35     fn next(&mut self) -> Option<<Self as Iterator>::Item> {
       
    36         if let Some(size) = self.messages.get(0) {
       
    37             let (msg, next) = self.messages.split_at(*size as usize + 1);
       
    38             self.messages = next;
       
    39             Some(msg)
       
    40         } else {
       
    41             None
       
    42         }
       
    43     }
       
    44 }
       
    45 
       
    46 fn by_msg(source: &[u8]) -> ByMsg {
       
    47     ByMsg {messages: source}
       
    48 }
       
    49 
       
    50 const VALID_MESSAGES: &[u8] =
       
    51     b"M#+LlRrUuDdZzAaSjJ,NpPwtgfhbc12345\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8A";
       
    52 const NON_TIMED_MESSAGES: &[u8] = b"M#hb";
       
    53 
       
    54 #[cfg(canhazslicepatterns)]
       
    55 fn is_msg_valid(msg: &[u8], team_indices: &[u8]) -> bool {
       
    56     match msg {
       
    57         [size, typ, body..] => VALID_MESSAGES.contains(typ)
       
    58             && match body {
       
    59                 [1...MAX_HEDGEHOGS_PER_TEAM, team, ..] if *typ == b'h' =>
       
    60                     team_indices.contains(team),
       
    61                 _ => *typ != b'h'
       
    62             },
       
    63         _ => false
       
    64     }
       
    65 }
       
    66 
       
    67 fn is_msg_valid(msg: &[u8], _team_indices: &[u8]) -> bool {
       
    68     if let Some(typ) = msg.get(1) {
       
    69         VALID_MESSAGES.contains(typ)
       
    70     } else {
       
    71         false
       
    72     }
       
    73 }
       
    74 
       
    75 fn is_msg_empty(msg: &[u8]) -> bool {
       
    76     msg.get(1).filter(|t| **t == b'+').is_some()
       
    77 }
       
    78 
       
    79 fn is_msg_timed(msg: &[u8]) -> bool {
       
    80     msg.get(1).filter(|t| !NON_TIMED_MESSAGES.contains(t)).is_some()
       
    81 }
       
    82 
       
    83 fn voting_description(kind: &VoteType) -> String {
       
    84     format!("New voting started: {}", match kind {
       
    85         VoteType::Kick(nick) => format!("kick {}", nick),
       
    86         VoteType::Map(name) => format!("map {}", name.as_ref().unwrap()),
       
    87         VoteType::Pause => "pause".to_string(),
       
    88         VoteType::NewSeed => "new seed".to_string(),
       
    89         VoteType::HedgehogsPerTeam(number) => format!("hedgehogs per team: {}", number)
       
    90     })
       
    91 }
       
    92 
       
    93 fn room_message_flag(msg: &HWProtocolMessage) -> RoomFlags {
       
    94     use crate::protocol::messages::HWProtocolMessage::*;
       
    95     match msg {
       
    96         ToggleRestrictJoin => RoomFlags::RESTRICTED_JOIN,
       
    97         ToggleRestrictTeams => RoomFlags::RESTRICTED_TEAM_ADD,
       
    98         ToggleRegisteredOnly => RoomFlags::RESTRICTED_UNREGISTERED_PLAYERS,
       
    99         _ => RoomFlags::empty()
       
   100     }
       
   101 }
       
   102 
       
   103 pub fn handle(server: &mut HWServer, client_id: ClientId, room_id: RoomId, message: HWProtocolMessage) {
       
   104     use crate::protocol::messages::HWProtocolMessage::*;
       
   105     match message {
       
   106         Part(None) => server.react(client_id, vec![
       
   107             MoveToLobby("part".to_string())]),
       
   108         Part(Some(msg)) => server.react(client_id, vec![
       
   109             MoveToLobby(format!("part: {}", msg))]),
       
   110         Chat(msg) => {
       
   111             let actions = {
       
   112                 let c = &mut server.clients[client_id];
       
   113                 let chat_msg = ChatMsg {nick: c.nick.clone(), msg};
       
   114                 vec![chat_msg.send_all().in_room(room_id).but_self().action()]
       
   115             };
       
   116             server.react(client_id, actions);
       
   117         },
       
   118         Fix => {
       
   119             if let (c, Some(r)) = server.client_and_room(client_id) {
       
   120                 if c.is_admin() { r.set_is_fixed(true) }
       
   121             }
       
   122         }
       
   123         Unfix => {
       
   124             if let (c, Some(r)) = server.client_and_room(client_id) {
       
   125                 if c.is_admin() { r.set_is_fixed(false) }
       
   126             }
       
   127         }
       
   128         Greeting(text) => {
       
   129             if let (c, Some(r)) = server.client_and_room(client_id) {
       
   130                 if c.is_admin() || c.is_master() && !r.is_fixed() {
       
   131                     r.greeting = text
       
   132                 }
       
   133             }
       
   134         }
       
   135         RoomName(new_name) => {
       
   136             let actions =
       
   137                 if is_name_illegal(&new_name) {
       
   138                     vec![Warn("Illegal room name! A room name must be between 1-40 characters long, must not have a trailing or leading space and must not have any of these characters: $()*+?[]^{|}".to_string())]
       
   139                 } else if server.rooms[room_id].is_fixed() {
       
   140                     vec![Warn("Access denied.".to_string())]
       
   141                 } else if server.has_room(&new_name) {
       
   142                     vec![Warn("A room with the same name already exists.".to_string())]
       
   143                 } else {
       
   144                     let mut old_name = new_name.clone();
       
   145                     swap(&mut server.rooms[room_id].name, &mut old_name);
       
   146                     vec![SendRoomUpdate(Some(old_name))]
       
   147                 };
       
   148             server.react(client_id, actions);
       
   149         },
       
   150         ToggleReady => {
       
   151             if let (c, Some(r)) = server.client_and_room(client_id) {
       
   152                 let flags = if c.is_ready() {
       
   153                     r.ready_players_number -= 1;
       
   154                     "-r"
       
   155                 } else {
       
   156                     r.ready_players_number += 1;
       
   157                     "+r"
       
   158                 };
       
   159 
       
   160                 let msg = if c.protocol_number < 38 {
       
   161                     LegacyReady(c.is_ready(), vec![c.nick.clone()])
       
   162                 } else {
       
   163                     ClientFlags(flags.to_string(), vec![c.nick.clone()])
       
   164                 };
       
   165 
       
   166                 let mut v = vec![msg.send_all().in_room(r.id).action()];
       
   167 
       
   168                 if r.is_fixed() && r.ready_players_number == r.players_number {
       
   169                     v.push(StartRoomGame(r.id))
       
   170                 }
       
   171 
       
   172                 c.set_is_ready(!c.is_ready());
       
   173                 server.react(client_id, v);
       
   174             }
       
   175         }
       
   176         AddTeam(info) => {
       
   177             let mut actions = Vec::new();
       
   178             if let (c, Some(r)) = server.client_and_room(client_id) {
       
   179                 if r.teams.len() >= r.team_limit as usize {
       
   180                     actions.push(Warn("Too many teams!".to_string()))
       
   181                 } else if r.addable_hedgehogs() == 0 {
       
   182                     actions.push(Warn("Too many hedgehogs!".to_string()))
       
   183                 } else if r.find_team(|t| t.name == info.name) != None {
       
   184                     actions.push(Warn("There's already a team with same name in the list.".to_string()))
       
   185                 } else if r.game_info.is_some() {
       
   186                     actions.push(Warn("Joining not possible: Round is in progress.".to_string()))
       
   187                 } else if r.is_team_add_restricted() {
       
   188                     actions.push(Warn("This room currently does not allow adding new teams.".to_string()));
       
   189                 } else {
       
   190                     let team = r.add_team(c.id, *info, c.protocol_number < 42);
       
   191                     c.teams_in_game += 1;
       
   192                     c.clan = Some(team.color);
       
   193                     actions.push(TeamAccepted(team.name.clone())
       
   194                         .send_self().action());
       
   195                     actions.push(TeamAdd(HWRoom::team_info(&c, team))
       
   196                         .send_all().in_room(room_id).but_self().action());
       
   197                     actions.push(TeamColor(team.name.clone(), team.color)
       
   198                         .send_all().in_room(room_id).action());
       
   199                     actions.push(HedgehogsNumber(team.name.clone(), team.hedgehogs_number)
       
   200                         .send_all().in_room(room_id).action());
       
   201                     actions.push(SendRoomUpdate(None));
       
   202                 }
       
   203             }
       
   204             server.react(client_id, actions);
       
   205         },
       
   206         RemoveTeam(name) => {
       
   207             let mut actions = Vec::new();
       
   208             if let (c, Some(r)) = server.client_and_room(client_id) {
       
   209                 match r.find_team_owner(&name) {
       
   210                     None =>
       
   211                         actions.push(Warn("Error: The team you tried to remove does not exist.".to_string())),
       
   212                     Some((id, _)) if id != client_id =>
       
   213                         actions.push(Warn("You can't remove a team you don't own.".to_string())),
       
   214                     Some((_, name)) => {
       
   215                         c.teams_in_game -= 1;
       
   216                         c.clan = r.find_team_color(c.id);
       
   217                         actions.push(Action::RemoveTeam(name.to_string()));
       
   218                     }
       
   219                 }
       
   220             };
       
   221             server.react(client_id, actions);
       
   222         },
       
   223         SetHedgehogsNumber(team_name, number) => {
       
   224             if let (c, Some(r)) = server.client_and_room(client_id) {
       
   225                 let addable_hedgehogs = r.addable_hedgehogs();
       
   226                 let actions = if let Some((_, team)) = r.find_team_and_owner_mut(|t| t.name == team_name) {
       
   227                     if !c.is_master() {
       
   228                         vec![ProtocolError("You're not the room master!".to_string())]
       
   229                     } else if number < 1 || number > MAX_HEDGEHOGS_PER_TEAM
       
   230                            || number > addable_hedgehogs + team.hedgehogs_number {
       
   231                         vec![HedgehogsNumber(team.name.clone(), team.hedgehogs_number)
       
   232                             .send_self().action()]
       
   233                     } else {
       
   234                         team.hedgehogs_number = number;
       
   235                         vec![HedgehogsNumber(team.name.clone(), number)
       
   236                             .send_all().in_room(room_id).but_self().action()]
       
   237                     }
       
   238                 } else {
       
   239                     vec![(Warn("No such team.".to_string()))]
       
   240                 };
       
   241                 server.react(client_id, actions);
       
   242             }
       
   243         },
       
   244         SetTeamColor(team_name, color) => {
       
   245             if let (c, Some(r)) = server.client_and_room(client_id) {
       
   246                 let mut owner_id = None;
       
   247                 let actions = if let Some((owner, team)) = r.find_team_and_owner_mut(|t| t.name == team_name) {
       
   248                     if !c.is_master() {
       
   249                         vec![ProtocolError("You're not the room master!".to_string())]
       
   250                     } else if false  {
       
   251                         Vec::new()
       
   252                     } else {
       
   253                         owner_id = Some(owner);
       
   254                         team.color = color;
       
   255                         vec![TeamColor(team.name.clone(), color)
       
   256                             .send_all().in_room(room_id).but_self().action()]
       
   257                     }
       
   258                 } else {
       
   259                     vec![(Warn("No such team.".to_string()))]
       
   260                 };
       
   261 
       
   262                 if let Some(id) = owner_id {
       
   263                     server.clients[id].clan = Some(color);
       
   264                 }
       
   265 
       
   266                 server.react(client_id, actions);
       
   267             };
       
   268         },
       
   269         Cfg(cfg) => {
       
   270             if let (c, Some(r)) = server.client_and_room(client_id) {
       
   271                 let actions = if r.is_fixed() {
       
   272                     vec![Warn("Access denied.".to_string())]
       
   273                 } else if !c.is_master() {
       
   274                     vec![ProtocolError("You're not the room master!".to_string())]
       
   275                 } else {
       
   276                     let cfg = match cfg {
       
   277                         GameCfg::Scheme(name, mut values) => {
       
   278                             if c.protocol_number == 49 && values.len() >= 2 {
       
   279                                 let mut s = "X".repeat(50);
       
   280                                 s.push_str(&values.pop().unwrap());
       
   281                                 values.push(s);
       
   282                             }
       
   283                             GameCfg::Scheme(name, values)
       
   284                         }
       
   285                         cfg => cfg
       
   286                     };
       
   287 
       
   288                     let v = vec![cfg.to_server_msg()
       
   289                         .send_all().in_room(r.id).but_self().action()];
       
   290                     r.set_config(cfg);
       
   291                     v
       
   292                 };
       
   293                 server.react(client_id, actions);
       
   294             }
       
   295         }
       
   296         Save(name, location) => {
       
   297             let actions = vec![server_chat(format!("Room config saved as {}", name))
       
   298                 .send_all().in_room(room_id).action()];
       
   299             server.rooms[room_id].save_config(name, location);
       
   300             server.react(client_id, actions);
       
   301         }
       
   302         SaveRoom(filename) => {
       
   303             if server.clients[client_id].is_admin() {
       
   304                 let actions = match server.rooms[room_id].get_saves() {
       
   305                     Ok(text) => match server.io.write_file(&filename, &text) {
       
   306                         Ok(_) => vec![server_chat("Room configs saved successfully.".to_string())
       
   307                             .send_self().action()],
       
   308                         Err(e) => {
       
   309                             warn!("Error while writing the config file \"{}\": {}", filename, e);
       
   310                             vec![Warn("Unable to save the room configs.".to_string())]
       
   311                         }
       
   312                     }
       
   313                     Err(e) => {
       
   314                         warn!("Error while serializing the room configs: {}", e);
       
   315                         vec![Warn("Unable to serialize the room configs.".to_string())]
       
   316                     }
       
   317                 };
       
   318                 server.react(client_id, actions);
       
   319             }
       
   320         }
       
   321         LoadRoom(filename) => {
       
   322             if server.clients[client_id].is_admin() {
       
   323                 let actions = match server.io.read_file(&filename) {
       
   324                     Ok(text) => match server.rooms[room_id].set_saves(&text) {
       
   325                         Ok(_) => vec![server_chat("Room configs loaded successfully.".to_string())
       
   326                             .send_self().action()],
       
   327                         Err(e) => {
       
   328                             warn!("Error while deserializing the room configs: {}", e);
       
   329                             vec![Warn("Unable to deserialize the room configs.".to_string())]
       
   330                         }
       
   331                     }
       
   332                     Err(e) => {
       
   333                         warn!("Error while reading the config file \"{}\": {}", filename, e);
       
   334                         vec![Warn("Unable to load the room configs.".to_string())]
       
   335                     }
       
   336                 };
       
   337                 server.react(client_id, actions);
       
   338             }
       
   339         }
       
   340         Delete(name) => {
       
   341             let actions = if !server.rooms[room_id].delete_config(&name) {
       
   342                 vec![Warn(format!("Save doesn't exist: {}", name))]
       
   343             } else {
       
   344                 vec![server_chat(format!("Room config {} has been deleted", name))
       
   345                     .send_all().in_room(room_id).action()]
       
   346             };
       
   347             server.react(client_id, actions);
       
   348         }
       
   349         CallVote(None) => {
       
   350             server.react(client_id, vec![
       
   351                 server_chat("Available callvote commands: kick <nickname>, map <name>, pause, newseed, hedgehogs <number>".to_string())
       
   352                     .send_self().action()])
       
   353         }
       
   354         CallVote(Some(kind)) => {
       
   355             let is_in_game = server.rooms[room_id].game_info.is_some();
       
   356             let error = match &kind {
       
   357                 VoteType::Kick(nick) => {
       
   358                     if server.find_client(&nick).filter(|c| c.room_id == Some(room_id)).is_some() {
       
   359                         None
       
   360                     } else {
       
   361                         Some("/callvote kick: No such user!".to_string())
       
   362                     }
       
   363                 },
       
   364                 VoteType::Map(None) => {
       
   365                     let names: Vec<_> = server.rooms[room_id].saves.keys().cloned().collect();
       
   366                     if names.is_empty() {
       
   367                         Some("/callvote map: No maps saved in this room!".to_string())
       
   368                     } else {
       
   369                         Some(format!("Available maps: {}", names.join(", ")))
       
   370                     }
       
   371                 },
       
   372                 VoteType::Map(Some(name)) => {
       
   373                     if server.rooms[room_id].saves.get(&name[..]).is_some() {
       
   374                         None
       
   375                     } else {
       
   376                         Some("/callvote map: No such map!".to_string())
       
   377                     }
       
   378                 },
       
   379                 VoteType::Pause => {
       
   380                     if is_in_game {
       
   381                         None
       
   382                     } else {
       
   383                         Some("/callvote pause: No game in progress!".to_string())
       
   384                     }
       
   385                 },
       
   386                 VoteType::NewSeed => {
       
   387                     None
       
   388                 },
       
   389                 VoteType::HedgehogsPerTeam(number) => {
       
   390                     match number {
       
   391                         1...MAX_HEDGEHOGS_PER_TEAM => None,
       
   392                         _ => Some("/callvote hedgehogs: Specify number from 1 to 8.".to_string())
       
   393                     }
       
   394                 },
       
   395             };
       
   396             match error {
       
   397                 None => {
       
   398                     let msg = voting_description(&kind);
       
   399                     let voting = Voting::new(kind, server.room_clients(client_id));
       
   400                     server.rooms[room_id].voting = Some(voting);
       
   401                     server.react(client_id, vec![
       
   402                         server_chat(msg).send_all().in_room(room_id).action(),
       
   403                         AddVote{ vote: true, is_forced: false}]);
       
   404                 }
       
   405                 Some(msg) => {
       
   406                     server.react(client_id, vec![
       
   407                         server_chat(msg).send_self().action()])
       
   408                 }
       
   409             }
       
   410         }
       
   411         Vote(vote) => {
       
   412             server.react(client_id, vec![AddVote{ vote, is_forced: false }]);
       
   413         }
       
   414         ForceVote(vote) => {
       
   415             let is_forced = server.clients[client_id].is_admin();
       
   416             server.react(client_id, vec![AddVote{ vote, is_forced }]);
       
   417         }
       
   418         ToggleRestrictJoin | ToggleRestrictTeams | ToggleRegisteredOnly  => {
       
   419             if server.clients[client_id].is_master() {
       
   420                 server.rooms[room_id].flags.toggle(room_message_flag(&message));
       
   421             }
       
   422             server.react(client_id, vec![SendRoomUpdate(None)]);
       
   423         }
       
   424         StartGame => {
       
   425             server.react(client_id, vec![StartRoomGame(room_id)]);
       
   426         }
       
   427         EngineMessage(em) => {
       
   428             let mut actions = Vec::new();
       
   429             if let (c, Some(r)) = server.client_and_room(client_id) {
       
   430                 if c.teams_in_game > 0 {
       
   431                     let decoding = decode(&em[..]).unwrap();
       
   432                     let messages = by_msg(&decoding);
       
   433                     let valid = messages.filter(|m| is_msg_valid(m, &c.team_indices));
       
   434                     let non_empty = valid.clone().filter(|m| !is_msg_empty(m));
       
   435                     let sync_msg = valid.clone().filter(|m| is_msg_timed(m))
       
   436                         .last().map(|m| if is_msg_empty(m) {Some(encode(m))} else {None});
       
   437 
       
   438                     let em_response = encode(&valid.flat_map(|msg| msg).cloned().collect::<Vec<_>>());
       
   439                     if !em_response.is_empty() {
       
   440                         actions.push(ForwardEngineMessage(vec![em_response])
       
   441                             .send_all().in_room(r.id).but_self().action());
       
   442                     }
       
   443                     let em_log = encode(&non_empty.flat_map(|msg| msg).cloned().collect::<Vec<_>>());
       
   444                     if let Some(ref mut info) = r.game_info {
       
   445                         if !em_log.is_empty() {
       
   446                             info.msg_log.push(em_log);
       
   447                         }
       
   448                         if let Some(msg) = sync_msg {
       
   449                             info.sync_msg = msg;
       
   450                         }
       
   451                     }
       
   452                 }
       
   453             }
       
   454             server.react(client_id, actions)
       
   455         }
       
   456         RoundFinished => {
       
   457             let mut actions = Vec::new();
       
   458             if let (c, Some(r)) = server.client_and_room(client_id) {
       
   459                 if c.is_in_game() {
       
   460                     c.set_is_in_game(false);
       
   461                     actions.push(ClientFlags("-g".to_string(), vec![c.nick.clone()]).
       
   462                         send_all().in_room(r.id).action());
       
   463                     if r.game_info.is_some() {
       
   464                         for team in r.client_teams(c.id) {
       
   465                             actions.push(SendTeamRemovalMessage(team.name.clone()));
       
   466                         }
       
   467                     }
       
   468                 }
       
   469             }
       
   470             server.react(client_id, actions)
       
   471         },
       
   472         Rnd(v) => {
       
   473             let result = rnd_reply(&v);
       
   474             let mut echo = vec!["/rnd".to_string()];
       
   475             echo.extend(v.into_iter());
       
   476             let chat_msg = ChatMsg {
       
   477                 nick: server.clients[client_id].nick.clone(),
       
   478                 msg: echo.join(" ")
       
   479             };
       
   480             server.react(client_id, vec![
       
   481                 chat_msg.send_all().in_room(room_id).action(),
       
   482                 result.send_all().in_room(room_id).action()])
       
   483         },
       
   484         _ => warn!("Unimplemented!")
       
   485     }
       
   486 }