rust/hedgewars-server/src/handlers/inroom.rs
changeset 15074 c5a6e8566425
parent 15026 a479916799ea
child 15075 e935b1ad23f3
equal deleted inserted replaced
15073:7732013ce64c 15074:c5a6e8566425
       
     1 use mio;
       
     2 
       
     3 use super::common::rnd_reply;
       
     4 use crate::utils::to_engine_msg;
       
     5 use crate::{
       
     6     protocol::messages::{
       
     7         add_flags, remove_flags, server_chat, HWProtocolMessage, HWServerMessage::*,
       
     8         ProtocolFlags as Flags,
       
     9     },
       
    10     core::{
       
    11         server::HWServer,
       
    12         types,
       
    13         types::{ClientId, GameCfg, RoomId, VoteType, Voting, MAX_HEDGEHOGS_PER_TEAM},
       
    14         room::{HWRoom, RoomFlags, MAX_TEAMS_IN_ROOM},
       
    15     },
       
    16     utils::is_name_illegal,
       
    17 };
       
    18 use base64::{decode, encode};
       
    19 use log::*;
       
    20 use std::{cmp::min, iter::once, mem::swap};
       
    21 
       
    22 #[derive(Clone)]
       
    23 struct ByMsg<'a> {
       
    24     messages: &'a [u8],
       
    25 }
       
    26 
       
    27 impl<'a> Iterator for ByMsg<'a> {
       
    28     type Item = &'a [u8];
       
    29 
       
    30     fn next(&mut self) -> Option<<Self as Iterator>::Item> {
       
    31         if let Some(size) = self.messages.get(0) {
       
    32             let (msg, next) = self.messages.split_at(*size as usize + 1);
       
    33             self.messages = next;
       
    34             Some(msg)
       
    35         } else {
       
    36             None
       
    37         }
       
    38     }
       
    39 }
       
    40 
       
    41 fn by_msg(source: &[u8]) -> ByMsg {
       
    42     ByMsg { messages: source }
       
    43 }
       
    44 
       
    45 const VALID_MESSAGES: &[u8] =
       
    46     b"M#+LlRrUuDdZzAaSjJ,NpPwtgfhbc12345\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8A";
       
    47 const NON_TIMED_MESSAGES: &[u8] = b"M#hb";
       
    48 
       
    49 #[cfg(canhazslicepatterns)]
       
    50 fn is_msg_valid(msg: &[u8], team_indices: &[u8]) -> bool {
       
    51     match msg {
       
    52         [size, typ, body..] => {
       
    53             VALID_MESSAGES.contains(typ)
       
    54                 && match body {
       
    55                     [1...MAX_HEDGEHOGS_PER_TEAM, team, ..] if *typ == b'h' => {
       
    56                         team_indices.contains(team)
       
    57                     }
       
    58                     _ => *typ != b'h',
       
    59                 }
       
    60         }
       
    61         _ => false,
       
    62     }
       
    63 }
       
    64 
       
    65 fn is_msg_valid(msg: &[u8], _team_indices: &[u8]) -> bool {
       
    66     if let Some(typ) = msg.get(1) {
       
    67         VALID_MESSAGES.contains(typ)
       
    68     } else {
       
    69         false
       
    70     }
       
    71 }
       
    72 
       
    73 fn is_msg_empty(msg: &[u8]) -> bool {
       
    74     msg.get(1).filter(|t| **t == b'+').is_some()
       
    75 }
       
    76 
       
    77 fn is_msg_timed(msg: &[u8]) -> bool {
       
    78     msg.get(1)
       
    79         .filter(|t| !NON_TIMED_MESSAGES.contains(t))
       
    80         .is_some()
       
    81 }
       
    82 
       
    83 fn voting_description(kind: &VoteType) -> String {
       
    84     format!(
       
    85         "New voting started: {}",
       
    86         match kind {
       
    87             VoteType::Kick(nick) => format!("kick {}", nick),
       
    88             VoteType::Map(name) => format!("map {}", name.as_ref().unwrap()),
       
    89             VoteType::Pause => "pause".to_string(),
       
    90             VoteType::NewSeed => "new seed".to_string(),
       
    91             VoteType::HedgehogsPerTeam(number) => format!("hedgehogs per team: {}", number),
       
    92         }
       
    93     )
       
    94 }
       
    95 
       
    96 fn room_message_flag(msg: &HWProtocolMessage) -> RoomFlags {
       
    97     use crate::protocol::messages::HWProtocolMessage::*;
       
    98     match msg {
       
    99         ToggleRestrictJoin => RoomFlags::RESTRICTED_JOIN,
       
   100         ToggleRestrictTeams => RoomFlags::RESTRICTED_TEAM_ADD,
       
   101         ToggleRegisteredOnly => RoomFlags::RESTRICTED_UNREGISTERED_PLAYERS,
       
   102         _ => RoomFlags::empty(),
       
   103     }
       
   104 }
       
   105 
       
   106 pub fn handle(
       
   107     server: &mut HWServer,
       
   108     client_id: ClientId,
       
   109     response: &mut super::Response,
       
   110     room_id: RoomId,
       
   111     message: HWProtocolMessage,
       
   112 ) {
       
   113     let client = &mut server.clients[client_id];
       
   114     let room = &mut server.rooms[room_id];
       
   115 
       
   116     use crate::protocol::messages::HWProtocolMessage::*;
       
   117     match message {
       
   118         Part(msg) => {
       
   119             let msg = match msg {
       
   120                 Some(s) => format!("part: {}", s),
       
   121                 None => "part".to_string(),
       
   122             };
       
   123             super::common::exit_room(server, client_id, response, &msg);
       
   124         }
       
   125         Chat(msg) => {
       
   126             response.add(
       
   127                 ChatMsg {
       
   128                     nick: client.nick.clone(),
       
   129                     msg,
       
   130                 }
       
   131                 .send_all()
       
   132                 .in_room(room_id),
       
   133             );
       
   134         }
       
   135         TeamChat(msg) => {
       
   136             let room = &server.rooms[room_id];
       
   137             if let Some(ref info) = room.game_info {
       
   138                 if let Some(clan_color) = room.find_team_color(client_id) {
       
   139                     let client = &server.clients[client_id];
       
   140                     let engine_msg =
       
   141                         to_engine_msg(format!("b{}]{}\x20\x20", client.nick, msg).bytes());
       
   142                     let team = room.clan_team_owners(clan_color).collect();
       
   143                     response.add(ForwardEngineMessage(vec![engine_msg]).send_many(team))
       
   144                 }
       
   145             }
       
   146         }
       
   147         Fix => {
       
   148             if client.is_admin() {
       
   149                 room.set_is_fixed(true);
       
   150                 room.set_join_restriction(false);
       
   151                 room.set_team_add_restriction(false);
       
   152                 room.set_unregistered_players_restriction(true);
       
   153             }
       
   154         }
       
   155         Unfix => {
       
   156             if client.is_admin() {
       
   157                 room.set_is_fixed(false);
       
   158             }
       
   159         }
       
   160         Greeting(text) => {
       
   161             if client.is_admin() || client.is_master() && !room.is_fixed() {
       
   162                 room.greeting = text;
       
   163             }
       
   164         }
       
   165         MaxTeams(count) => {
       
   166             if !client.is_master() {
       
   167                 response.add(Warning("You're not the room master!".to_string()).send_self());
       
   168             } else if !(2..=MAX_TEAMS_IN_ROOM).contains(&count) {
       
   169                 response
       
   170                     .add(Warning("/maxteams: specify number from 2 to 8".to_string()).send_self());
       
   171             } else {
       
   172                 server.rooms[room_id].max_teams = count;
       
   173             }
       
   174         }
       
   175         RoomName(new_name) => {
       
   176             if is_name_illegal(&new_name) {
       
   177                 response.add(Warning("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()).send_self());
       
   178             } else if server.has_room(&new_name) {
       
   179                 response.add(
       
   180                     Warning("A room with the same name already exists.".to_string()).send_self(),
       
   181                 );
       
   182             } else {
       
   183                 let room = &mut server.rooms[room_id];
       
   184                 if room.is_fixed() || room.master_id != Some(client_id) {
       
   185                     response.add(Warning("Access denied.".to_string()).send_self());
       
   186                 } else {
       
   187                     let mut old_name = new_name.clone();
       
   188                     let client = &server.clients[client_id];
       
   189                     swap(&mut room.name, &mut old_name);
       
   190                     super::common::get_room_update(Some(old_name), room, Some(&client), response);
       
   191                 }
       
   192             }
       
   193         }
       
   194         ToggleReady => {
       
   195             let flags = if client.is_ready() {
       
   196                 room.ready_players_number -= 1;
       
   197                 remove_flags(&[Flags::Ready])
       
   198             } else {
       
   199                 room.ready_players_number += 1;
       
   200                 add_flags(&[Flags::Ready])
       
   201             };
       
   202 
       
   203             let msg = if client.protocol_number < 38 {
       
   204                 LegacyReady(client.is_ready(), vec![client.nick.clone()])
       
   205             } else {
       
   206                 ClientFlags(flags, vec![client.nick.clone()])
       
   207             };
       
   208             response.add(msg.send_all().in_room(room.id));
       
   209             client.set_is_ready(!client.is_ready());
       
   210 
       
   211             if room.is_fixed() && room.ready_players_number == room.players_number {
       
   212                 super::common::start_game(server, room_id, response);
       
   213             }
       
   214         }
       
   215         AddTeam(mut info) => {
       
   216             if room.teams.len() >= room.max_teams as usize {
       
   217                 response.add(Warning("Too many teams!".to_string()).send_self());
       
   218             } else if room.addable_hedgehogs() == 0 {
       
   219                 response.add(Warning("Too many hedgehogs!".to_string()).send_self());
       
   220             } else if room.find_team(|t| t.name == info.name) != None {
       
   221                 response.add(
       
   222                     Warning("There's already a team with same name in the list.".to_string())
       
   223                         .send_self(),
       
   224                 );
       
   225             } else if room.game_info.is_some() {
       
   226                 response.add(
       
   227                     Warning("Joining not possible: Round is in progress.".to_string()).send_self(),
       
   228                 );
       
   229             } else if room.is_team_add_restricted() {
       
   230                 response.add(
       
   231                     Warning("This room currently does not allow adding new teams.".to_string())
       
   232                         .send_self(),
       
   233                 );
       
   234             } else {
       
   235                 info.owner = client.nick.clone();
       
   236                 let team = room.add_team(client.id, *info, client.protocol_number < 42);
       
   237                 client.teams_in_game += 1;
       
   238                 client.clan = Some(team.color);
       
   239                 response.add(TeamAccepted(team.name.clone()).send_self());
       
   240                 response.add(
       
   241                     TeamAdd(team.to_protocol())
       
   242                         .send_all()
       
   243                         .in_room(room_id)
       
   244                         .but_self(),
       
   245                 );
       
   246                 response.add(
       
   247                     TeamColor(team.name.clone(), team.color)
       
   248                         .send_all()
       
   249                         .in_room(room_id),
       
   250                 );
       
   251                 response.add(
       
   252                     HedgehogsNumber(team.name.clone(), team.hedgehogs_number)
       
   253                         .send_all()
       
   254                         .in_room(room_id),
       
   255                 );
       
   256 
       
   257                 let room_master = if let Some(id) = room.master_id {
       
   258                     Some(&server.clients[id])
       
   259                 } else {
       
   260                     None
       
   261                 };
       
   262                 super::common::get_room_update(None, room, room_master, response);
       
   263             }
       
   264         }
       
   265         RemoveTeam(name) => match room.find_team_owner(&name) {
       
   266             None => response.add(
       
   267                 Warning("Error: The team you tried to remove does not exist.".to_string())
       
   268                     .send_self(),
       
   269             ),
       
   270             Some((id, _)) if id != client_id => response
       
   271                 .add(Warning("You can't remove a team you don't own.".to_string()).send_self()),
       
   272             Some((_, name)) => {
       
   273                 client.teams_in_game -= 1;
       
   274                 client.clan = room.find_team_color(client.id);
       
   275                 super::common::remove_teams(
       
   276                     room,
       
   277                     vec![name.to_string()],
       
   278                     client.is_in_game(),
       
   279                     response,
       
   280                 );
       
   281 
       
   282                 match room.game_info {
       
   283                     Some(ref info) if info.teams_in_game == 0 => {
       
   284                         super::common::end_game(server, room_id, response)
       
   285                     }
       
   286                     _ => (),
       
   287                 }
       
   288             }
       
   289         },
       
   290         SetHedgehogsNumber(team_name, number) => {
       
   291             let addable_hedgehogs = room.addable_hedgehogs();
       
   292             if let Some((_, team)) = room.find_team_and_owner_mut(|t| t.name == team_name) {
       
   293                 let max_hedgehogs = min(
       
   294                     MAX_HEDGEHOGS_PER_TEAM,
       
   295                     addable_hedgehogs + team.hedgehogs_number,
       
   296                 );
       
   297                 if !client.is_master() {
       
   298                     response.add(Error("You're not the room master!".to_string()).send_self());
       
   299                 } else if !(1..=max_hedgehogs).contains(&number) {
       
   300                     response
       
   301                         .add(HedgehogsNumber(team.name.clone(), team.hedgehogs_number).send_self());
       
   302                 } else {
       
   303                     team.hedgehogs_number = number;
       
   304                     response.add(
       
   305                         HedgehogsNumber(team.name.clone(), number)
       
   306                             .send_all()
       
   307                             .in_room(room_id)
       
   308                             .but_self(),
       
   309                     );
       
   310                 }
       
   311             } else {
       
   312                 response.add(Warning("No such team.".to_string()).send_self());
       
   313             }
       
   314         }
       
   315         SetTeamColor(team_name, color) => {
       
   316             if let Some((owner, team)) = room.find_team_and_owner_mut(|t| t.name == team_name) {
       
   317                 if !client.is_master() {
       
   318                     response.add(Error("You're not the room master!".to_string()).send_self());
       
   319                 } else {
       
   320                     team.color = color;
       
   321                     response.add(
       
   322                         TeamColor(team.name.clone(), color)
       
   323                             .send_all()
       
   324                             .in_room(room_id)
       
   325                             .but_self(),
       
   326                     );
       
   327                     server.clients[owner].clan = Some(color);
       
   328                 }
       
   329             } else {
       
   330                 response.add(Warning("No such team.".to_string()).send_self());
       
   331             }
       
   332         }
       
   333         Cfg(cfg) => {
       
   334             if room.is_fixed() {
       
   335                 response.add(Warning("Access denied.".to_string()).send_self());
       
   336             } else if !client.is_master() {
       
   337                 response.add(Error("You're not the room master!".to_string()).send_self());
       
   338             } else {
       
   339                 let cfg = match cfg {
       
   340                     GameCfg::Scheme(name, mut values) => {
       
   341                         if client.protocol_number == 49 && values.len() >= 2 {
       
   342                             let mut s = "X".repeat(50);
       
   343                             s.push_str(&values.pop().unwrap());
       
   344                             values.push(s);
       
   345                         }
       
   346                         GameCfg::Scheme(name, values)
       
   347                     }
       
   348                     cfg => cfg,
       
   349                 };
       
   350 
       
   351                 response.add(cfg.to_server_msg().send_all().in_room(room.id).but_self());
       
   352                 room.set_config(cfg);
       
   353             }
       
   354         }
       
   355         Save(name, location) => {
       
   356             response.add(
       
   357                 server_chat(format!("Room config saved as {}", name))
       
   358                     .send_all()
       
   359                     .in_room(room_id),
       
   360             );
       
   361             room.save_config(name, location);
       
   362         }
       
   363         #[cfg(feature = "official-server")]
       
   364         SaveRoom(filename) => {
       
   365             if client.is_admin() {
       
   366                 match room.get_saves() {
       
   367                     Ok(contents) => response.request_io(super::IoTask::SaveRoom {
       
   368                         room_id,
       
   369                         filename,
       
   370                         contents,
       
   371                     }),
       
   372                     Err(e) => {
       
   373                         warn!("Error while serializing the room configs: {}", e);
       
   374                         response.add(
       
   375                             Warning("Unable to serialize the room configs.".to_string())
       
   376                                 .send_self(),
       
   377                         )
       
   378                     }
       
   379                 }
       
   380             }
       
   381         }
       
   382         #[cfg(feature = "official-server")]
       
   383         LoadRoom(filename) => {
       
   384             if client.is_admin() {
       
   385                 response.request_io(super::IoTask::LoadRoom { room_id, filename });
       
   386             }
       
   387         }
       
   388         Delete(name) => {
       
   389             if !room.delete_config(&name) {
       
   390                 response.add(Warning(format!("Save doesn't exist: {}", name)).send_self());
       
   391             } else {
       
   392                 response.add(
       
   393                     server_chat(format!("Room config {} has been deleted", name))
       
   394                         .send_all()
       
   395                         .in_room(room_id),
       
   396                 );
       
   397             }
       
   398         }
       
   399         CallVote(None) => {
       
   400             response.add(server_chat("Available callvote commands: kick <nickname>, map <name>, pause, newseed, hedgehogs <number>".to_string())
       
   401                 .send_self());
       
   402         }
       
   403         CallVote(Some(kind)) => {
       
   404             let is_in_game = room.game_info.is_some();
       
   405             let error = match &kind {
       
   406                 VoteType::Kick(nick) => {
       
   407                     if server
       
   408                         .find_client(&nick)
       
   409                         .filter(|c| c.room_id == Some(room_id))
       
   410                         .is_some()
       
   411                     {
       
   412                         None
       
   413                     } else {
       
   414                         Some("/callvote kick: No such user!".to_string())
       
   415                     }
       
   416                 }
       
   417                 VoteType::Map(None) => {
       
   418                     let names: Vec<_> = server.rooms[room_id].saves.keys().cloned().collect();
       
   419                     if names.is_empty() {
       
   420                         Some("/callvote map: No maps saved in this room!".to_string())
       
   421                     } else {
       
   422                         Some(format!("Available maps: {}", names.join(", ")))
       
   423                     }
       
   424                 }
       
   425                 VoteType::Map(Some(name)) => {
       
   426                     if room.saves.get(&name[..]).is_some() {
       
   427                         None
       
   428                     } else {
       
   429                         Some("/callvote map: No such map!".to_string())
       
   430                     }
       
   431                 }
       
   432                 VoteType::Pause => {
       
   433                     if is_in_game {
       
   434                         None
       
   435                     } else {
       
   436                         Some("/callvote pause: No game in progress!".to_string())
       
   437                     }
       
   438                 }
       
   439                 VoteType::NewSeed => None,
       
   440                 VoteType::HedgehogsPerTeam(number) => match number {
       
   441                     1...MAX_HEDGEHOGS_PER_TEAM => None,
       
   442                     _ => Some("/callvote hedgehogs: Specify number from 1 to 8.".to_string()),
       
   443                 },
       
   444             };
       
   445 
       
   446             match error {
       
   447                 None => {
       
   448                     let msg = voting_description(&kind);
       
   449                     let voting = Voting::new(kind, server.room_clients(client_id).collect());
       
   450                     let room = &mut server.rooms[room_id];
       
   451                     room.voting = Some(voting);
       
   452                     response.add(server_chat(msg).send_all().in_room(room_id));
       
   453                     super::common::submit_vote(
       
   454                         server,
       
   455                         types::Vote {
       
   456                             is_pro: true,
       
   457                             is_forced: false,
       
   458                         },
       
   459                         response,
       
   460                     );
       
   461                 }
       
   462                 Some(msg) => {
       
   463                     response.add(server_chat(msg).send_self());
       
   464                 }
       
   465             }
       
   466         }
       
   467         Vote(vote) => {
       
   468             super::common::submit_vote(
       
   469                 server,
       
   470                 types::Vote {
       
   471                     is_pro: vote,
       
   472                     is_forced: false,
       
   473                 },
       
   474                 response,
       
   475             );
       
   476         }
       
   477         ForceVote(vote) => {
       
   478             let is_forced = client.is_admin();
       
   479             super::common::submit_vote(
       
   480                 server,
       
   481                 types::Vote {
       
   482                     is_pro: vote,
       
   483                     is_forced,
       
   484                 },
       
   485                 response,
       
   486             );
       
   487         }
       
   488         ToggleRestrictJoin | ToggleRestrictTeams | ToggleRegisteredOnly => {
       
   489             if client.is_master() {
       
   490                 room.flags.toggle(room_message_flag(&message));
       
   491                 super::common::get_room_update(None, room, Some(&client), response);
       
   492             }
       
   493         }
       
   494         StartGame => {
       
   495             super::common::start_game(server, room_id, response);
       
   496         }
       
   497         EngineMessage(em) => {
       
   498             if client.teams_in_game > 0 {
       
   499                 let decoding = decode(&em[..]).unwrap();
       
   500                 let messages = by_msg(&decoding);
       
   501                 let valid = messages.filter(|m| is_msg_valid(m, &client.team_indices));
       
   502                 let non_empty = valid.clone().filter(|m| !is_msg_empty(m));
       
   503                 let sync_msg = valid.clone().filter(|m| is_msg_timed(m)).last().map(|m| {
       
   504                     if is_msg_empty(m) {
       
   505                         Some(encode(m))
       
   506                     } else {
       
   507                         None
       
   508                     }
       
   509                 });
       
   510 
       
   511                 let em_response = encode(&valid.flat_map(|msg| msg).cloned().collect::<Vec<_>>());
       
   512                 if !em_response.is_empty() {
       
   513                     response.add(
       
   514                         ForwardEngineMessage(vec![em_response])
       
   515                             .send_all()
       
   516                             .in_room(room.id)
       
   517                             .but_self(),
       
   518                     );
       
   519                 }
       
   520                 let em_log = encode(&non_empty.flat_map(|msg| msg).cloned().collect::<Vec<_>>());
       
   521                 if let Some(ref mut info) = room.game_info {
       
   522                     if !em_log.is_empty() {
       
   523                         info.msg_log.push(em_log);
       
   524                     }
       
   525                     if let Some(msg) = sync_msg {
       
   526                         info.sync_msg = msg;
       
   527                     }
       
   528                 }
       
   529             }
       
   530         }
       
   531         RoundFinished => {
       
   532             let mut game_ended = false;
       
   533             if client.is_in_game() {
       
   534                 client.set_is_in_game(false);
       
   535                 response.add(
       
   536                     ClientFlags(remove_flags(&[Flags::InGame]), vec![client.nick.clone()])
       
   537                         .send_all()
       
   538                         .in_room(room.id),
       
   539                 );
       
   540                 let team_names: Vec<_> = room
       
   541                     .client_teams(client_id)
       
   542                     .map(|t| t.name.clone())
       
   543                     .collect();
       
   544 
       
   545                 if let Some(ref mut info) = room.game_info {
       
   546                     info.teams_in_game -= team_names.len() as u8;
       
   547                     if info.teams_in_game == 0 {
       
   548                         game_ended = true;
       
   549                     }
       
   550 
       
   551                     for team_name in team_names {
       
   552                         let msg = once(b'F').chain(team_name.bytes());
       
   553                         response.add(
       
   554                             ForwardEngineMessage(vec![to_engine_msg(msg)])
       
   555                                 .send_all()
       
   556                                 .in_room(room_id)
       
   557                                 .but_self(),
       
   558                         );
       
   559 
       
   560                         let remove_msg = to_engine_msg(once(b'F').chain(team_name.bytes()));
       
   561                         if let Some(m) = &info.sync_msg {
       
   562                             info.msg_log.push(m.clone());
       
   563                         }
       
   564                         if info.sync_msg.is_some() {
       
   565                             info.sync_msg = None
       
   566                         }
       
   567                         info.msg_log.push(remove_msg.clone());
       
   568                         response.add(
       
   569                             ForwardEngineMessage(vec![remove_msg])
       
   570                                 .send_all()
       
   571                                 .in_room(room_id)
       
   572                                 .but_self(),
       
   573                         );
       
   574                     }
       
   575                 }
       
   576             }
       
   577             if game_ended {
       
   578                 super::common::end_game(server, room_id, response)
       
   579             }
       
   580         }
       
   581         Rnd(v) => {
       
   582             let result = rnd_reply(&v);
       
   583             let mut echo = vec!["/rnd".to_string()];
       
   584             echo.extend(v.into_iter());
       
   585             let chat_msg = ChatMsg {
       
   586                 nick: server.clients[client_id].nick.clone(),
       
   587                 msg: echo.join(" "),
       
   588             };
       
   589             response.add(chat_msg.send_all().in_room(room_id));
       
   590             response.add(result.send_all().in_room(room_id));
       
   591         }
       
   592         Delegate(nick) => {
       
   593             let delegate_id = server.find_client(&nick).map(|c| (c.id, c.room_id));
       
   594             let client = &server.clients[client_id];
       
   595             if !(client.is_admin() || client.is_master()) {
       
   596                 response.add(
       
   597                     Warning("You're not the room master or a server admin!".to_string())
       
   598                         .send_self(),
       
   599                 )
       
   600             } else {
       
   601                 match delegate_id {
       
   602                     None => response.add(Warning("Player is not online.".to_string()).send_self()),
       
   603                     Some((id, _)) if id == client_id => response
       
   604                         .add(Warning("You're already the room master.".to_string()).send_self()),
       
   605                     Some((_, id)) if id != Some(room_id) => response
       
   606                         .add(Warning("The player is not in your room.".to_string()).send_self()),
       
   607                     Some((id, _)) => {
       
   608                         super::common::change_master(server, room_id, id, response);
       
   609                     }
       
   610                 }
       
   611             }
       
   612         }
       
   613         _ => warn!("Unimplemented!"),
       
   614     }
       
   615 }