rust/hedgewars-server/src/handlers.rs
changeset 15074 c5a6e8566425
parent 14907 c156273b57de
child 15075 e935b1ad23f3
equal deleted inserted replaced
15073:7732013ce64c 15074:c5a6e8566425
       
     1 use mio;
       
     2 use std::{collections::HashMap, io, io::Write};
       
     3 
       
     4 use self::{
       
     5     actions::{Destination, DestinationGroup, PendingMessage},
       
     6     inanteroom::LoginResult
       
     7 };
       
     8 use crate::{
       
     9     core::{
       
    10         server::HWServer,
       
    11         types::{ClientId, Replay, RoomId, GameCfg, TeamInfo},
       
    12         room::RoomSave
       
    13     },
       
    14     protocol::messages::{
       
    15         server_chat,
       
    16         HWProtocolMessage,
       
    17         HWServerMessage,
       
    18         HWServerMessage::*,
       
    19         global_chat,
       
    20         HWProtocolMessage::EngineMessage
       
    21     },
       
    22     utils,
       
    23 };
       
    24 use base64::encode;
       
    25 use log::*;
       
    26 use rand::{thread_rng, RngCore};
       
    27 
       
    28 mod actions;
       
    29 mod checker;
       
    30 mod common;
       
    31 mod inroom;
       
    32 mod inlobby;
       
    33 mod inanteroom;
       
    34 
       
    35 use std::fmt::{Formatter, LowerHex};
       
    36 
       
    37 #[derive(PartialEq)]
       
    38 pub struct Sha1Digest([u8; 20]);
       
    39 
       
    40 impl Sha1Digest {
       
    41     pub fn new(digest: [u8; 20]) -> Self {
       
    42         Self(digest)
       
    43     }
       
    44 }
       
    45 
       
    46 impl LowerHex for Sha1Digest {
       
    47     fn fmt(&self, f: &mut Formatter) -> Result<(), std::fmt::Error> {
       
    48         for byte in &self.0 {
       
    49             write!(f, "{:02x}", byte)?;
       
    50         }
       
    51         Ok(())
       
    52     }
       
    53 }
       
    54 
       
    55 pub struct AccountInfo {
       
    56     pub is_registered: bool,
       
    57     pub is_admin: bool,
       
    58     pub is_contributor: bool,
       
    59     pub server_hash: Sha1Digest,
       
    60 }
       
    61 
       
    62 pub enum IoTask {
       
    63     GetAccount {
       
    64         nick: String,
       
    65         protocol: u16,
       
    66         password_hash: String,
       
    67         client_salt: String,
       
    68         server_salt: String,
       
    69     },
       
    70     GetReplay {
       
    71         id: u32,
       
    72     },
       
    73     SaveRoom {
       
    74         room_id: RoomId,
       
    75         filename: String,
       
    76         contents: String,
       
    77     },
       
    78     LoadRoom {
       
    79         room_id: RoomId,
       
    80         filename: String,
       
    81     },
       
    82 }
       
    83 
       
    84 pub enum IoResult {
       
    85     Account(Option<AccountInfo>),
       
    86     Replay(Option<Replay>),
       
    87     SaveRoom(RoomId, bool),
       
    88     LoadRoom(RoomId, Option<String>),
       
    89 }
       
    90 
       
    91 pub struct Response {
       
    92     client_id: ClientId,
       
    93     messages: Vec<PendingMessage>,
       
    94     io_tasks: Vec<IoTask>,
       
    95     removed_clients: Vec<ClientId>,
       
    96 }
       
    97 
       
    98 impl Response {
       
    99     pub fn new(client_id: ClientId) -> Self {
       
   100         Self {
       
   101             client_id,
       
   102             messages: vec![],
       
   103             io_tasks: vec![],
       
   104             removed_clients: vec![],
       
   105         }
       
   106     }
       
   107 
       
   108     #[inline]
       
   109     pub fn is_empty(&self) -> bool {
       
   110         self.messages.is_empty() && self.removed_clients.is_empty() && self.io_tasks.is_empty()
       
   111     }
       
   112 
       
   113     #[inline]
       
   114     pub fn len(&self) -> usize {
       
   115         self.messages.len()
       
   116     }
       
   117 
       
   118     #[inline]
       
   119     pub fn client_id(&self) -> ClientId {
       
   120         self.client_id
       
   121     }
       
   122 
       
   123     #[inline]
       
   124     pub fn add(&mut self, message: PendingMessage) {
       
   125         self.messages.push(message)
       
   126     }
       
   127 
       
   128     #[inline]
       
   129     pub fn request_io(&mut self, task: IoTask) {
       
   130         self.io_tasks.push(task)
       
   131     }
       
   132 
       
   133     pub fn extract_messages<'a, 'b: 'a>(
       
   134         &'b mut self,
       
   135         server: &'a HWServer,
       
   136     ) -> impl Iterator<Item = (Vec<ClientId>, HWServerMessage)> + 'a {
       
   137         let client_id = self.client_id;
       
   138         self.messages.drain(..).map(move |m| {
       
   139             let ids = get_recipients(server, client_id, m.destination);
       
   140             (ids, m.message)
       
   141         })
       
   142     }
       
   143 
       
   144     pub fn remove_client(&mut self, client_id: ClientId) {
       
   145         self.removed_clients.push(client_id);
       
   146     }
       
   147 
       
   148     pub fn extract_removed_clients(&mut self) -> impl Iterator<Item = ClientId> + '_ {
       
   149         self.removed_clients.drain(..)
       
   150     }
       
   151 
       
   152     pub fn extract_io_tasks(&mut self) -> impl Iterator<Item = IoTask> + '_ {
       
   153         self.io_tasks.drain(..)
       
   154     }
       
   155 }
       
   156 
       
   157 impl Extend<PendingMessage> for Response {
       
   158     fn extend<T: IntoIterator<Item = PendingMessage>>(&mut self, iter: T) {
       
   159         for msg in iter {
       
   160             self.add(msg)
       
   161         }
       
   162     }
       
   163 }
       
   164 
       
   165 fn get_recipients(
       
   166     server: &HWServer,
       
   167     client_id: ClientId,
       
   168     destination: Destination,
       
   169 ) -> Vec<ClientId> {
       
   170     match destination {
       
   171         Destination::ToSelf => vec![client_id],
       
   172         Destination::ToId(id) => vec![id],
       
   173         Destination::ToIds(ids) => ids,
       
   174         Destination::ToAll { group, skip_self } => {
       
   175             let mut ids: Vec<_> = match group {
       
   176                 DestinationGroup::All => server.all_clients().collect(),
       
   177                 DestinationGroup::Lobby => server.lobby_clients().collect(),
       
   178                 DestinationGroup::Protocol(proto) => server.protocol_clients(proto).collect(),
       
   179                 DestinationGroup::Room(id) => server.room_clients(id).collect(),
       
   180             };
       
   181 
       
   182             if skip_self {
       
   183                 if let Some(index) = ids.iter().position(|id| *id == client_id) {
       
   184                     ids.remove(index);
       
   185                 }
       
   186             }
       
   187 
       
   188             ids
       
   189         }
       
   190     }
       
   191 }
       
   192 
       
   193 pub fn handle(
       
   194     server: &mut HWServer,
       
   195     client_id: ClientId,
       
   196     response: &mut Response,
       
   197     message: HWProtocolMessage,
       
   198 ) {
       
   199     match message {
       
   200         HWProtocolMessage::Ping => response.add(Pong.send_self()),
       
   201         _ => {
       
   202             if server.anteroom.clients.contains(client_id) {
       
   203                 match inanteroom::handle(server, client_id, response, message) {
       
   204                     LoginResult::Unchanged => (),
       
   205                     LoginResult::Complete => {
       
   206                         if let Some(client) = server.anteroom.remove_client(client_id) {
       
   207                             server.add_client(client_id, client);
       
   208                             common::join_lobby(server, response);
       
   209                         }
       
   210                     }
       
   211                     LoginResult::Exit => {
       
   212                         server.anteroom.remove_client(client_id);
       
   213                         response.remove_client(client_id);
       
   214                     }
       
   215                 }
       
   216             } else if server.clients.contains(client_id) {
       
   217                 match message {
       
   218                     HWProtocolMessage::Quit(Some(msg)) => {
       
   219                         common::remove_client(server, response, "User quit: ".to_string() + &msg);
       
   220                     }
       
   221                     HWProtocolMessage::Quit(None) => {
       
   222                         common::remove_client(server, response, "User quit".to_string());
       
   223                     }
       
   224                     HWProtocolMessage::Info(nick) => {
       
   225                         if let Some(client) = server.find_client(&nick) {
       
   226                             let admin_sign = if client.is_admin() { "@" } else { "" };
       
   227                             let master_sign = if client.is_master() { "+" } else { "" };
       
   228                             let room_info = match client.room_id {
       
   229                                 Some(room_id) => {
       
   230                                     let room = &server.rooms[room_id];
       
   231                                     let status = match room.game_info {
       
   232                                         Some(_) if client.teams_in_game == 0 => "(spectating)",
       
   233                                         Some(_) => "(playing)",
       
   234                                         None => "",
       
   235                                     };
       
   236                                     format!(
       
   237                                         "[{}{}room {}]{}",
       
   238                                         admin_sign, master_sign, room.name, status
       
   239                                     )
       
   240                                 }
       
   241                                 None => format!("[{}lobby]", admin_sign),
       
   242                             };
       
   243 
       
   244                             let info = vec![
       
   245                                 client.nick.clone(),
       
   246                                 "[]".to_string(),
       
   247                                 utils::protocol_version_string(client.protocol_number).to_string(),
       
   248                                 room_info,
       
   249                             ];
       
   250                             response.add(Info(info).send_self())
       
   251                         } else {
       
   252                             response
       
   253                                 .add(server_chat("Player is not online.".to_string()).send_self())
       
   254                         }
       
   255                     }
       
   256                     HWProtocolMessage::ToggleServerRegisteredOnly => {
       
   257                         if !server.clients[client_id].is_admin() {
       
   258                             response.add(Warning("Access denied.".to_string()).send_self());
       
   259                         } else {
       
   260                             server.set_is_registered_only(server.is_registered_only());
       
   261                             let msg = if server.is_registered_only() {
       
   262                                 "This server no longer allows unregistered players to join."
       
   263                             } else {
       
   264                                 "This server now allows unregistered players to join."
       
   265                             };
       
   266                             response.add(server_chat(msg.to_string()).send_all());
       
   267                         }
       
   268                     }
       
   269                     HWProtocolMessage::Global(msg) => {
       
   270                         if !server.clients[client_id].is_admin() {
       
   271                             response.add(Warning("Access denied.".to_string()).send_self());
       
   272                         } else {
       
   273                             response.add(global_chat(msg).send_all())
       
   274                         }
       
   275                     }
       
   276                     HWProtocolMessage::SuperPower => {
       
   277                         if !server.clients[client_id].is_admin() {
       
   278                             response.add(Warning("Access denied.".to_string()).send_self());
       
   279                         } else {
       
   280                             server.clients[client_id].set_has_super_power(true);
       
   281                             response
       
   282                                 .add(server_chat("Super power activated.".to_string()).send_self())
       
   283                         }
       
   284                     }
       
   285                     HWProtocolMessage::Watch(id) => {
       
   286                         #[cfg(feature = "official-server")]
       
   287                         {
       
   288                             response.request_io(IoTask::GetReplay { id })
       
   289                         }
       
   290 
       
   291                         #[cfg(not(feature = "official-server"))]
       
   292                         {
       
   293                             response.add(
       
   294                                 Warning("This server does not support replays!".to_string())
       
   295                                     .send_self(),
       
   296                             );
       
   297                         }
       
   298                     }
       
   299                     _ => match server.clients[client_id].room_id {
       
   300                         None => inlobby::handle(server, client_id, response, message),
       
   301                         Some(room_id) => {
       
   302                             inroom::handle(server, client_id, response, room_id, message)
       
   303                         }
       
   304                     },
       
   305                 }
       
   306             }
       
   307         }
       
   308     }
       
   309 }
       
   310 
       
   311 pub fn handle_client_accept(server: &mut HWServer, client_id: ClientId, response: &mut Response) {
       
   312     let mut salt = [0u8; 18];
       
   313     thread_rng().fill_bytes(&mut salt);
       
   314 
       
   315     server.anteroom.add_client(client_id, encode(&salt));
       
   316 
       
   317     response.add(HWServerMessage::Connected(utils::SERVER_VERSION).send_self());
       
   318 }
       
   319 
       
   320 pub fn handle_client_loss(server: &mut HWServer, client_id: ClientId, response: &mut Response) {
       
   321     if server.anteroom.remove_client(client_id).is_none() {
       
   322         common::remove_client(server, response, "Connection reset".to_string());
       
   323     }
       
   324 }
       
   325 
       
   326 pub fn handle_io_result(
       
   327     server: &mut HWServer,
       
   328     client_id: ClientId,
       
   329     response: &mut Response,
       
   330     io_result: IoResult,
       
   331 ) {
       
   332     match io_result {
       
   333         IoResult::Account(Some(info)) => {
       
   334             if !info.is_registered && server.is_registered_only() {
       
   335                 response.add(
       
   336                     Bye("This server only allows registered users to join.".to_string())
       
   337                         .send_self(),
       
   338                 );
       
   339                 response.remove_client(client_id);
       
   340             } else {
       
   341                 response.add(ServerAuth(format!("{:x}", info.server_hash)).send_self());
       
   342                 if let Some(client) = server.anteroom.remove_client(client_id) {
       
   343                     server.add_client(client_id, client);
       
   344                     let client = &mut server.clients[client_id];
       
   345                     client.set_is_registered(info.is_registered);
       
   346                     client.set_is_admin(info.is_admin);
       
   347                     client.set_is_contributor(info.is_admin)
       
   348                 }
       
   349             }
       
   350         }
       
   351         IoResult::Account(None) => {
       
   352             response.add(Error("Authentication failed.".to_string()).send_self());
       
   353             response.remove_client(client_id);
       
   354         }
       
   355         IoResult::Replay(Some(replay)) => {
       
   356             let protocol = server.clients[client_id].protocol_number;
       
   357             let start_msg = if protocol < 58 {
       
   358                 RoomJoined(vec![server.clients[client_id].nick.clone()])
       
   359             } else {
       
   360                 ReplayStart
       
   361             };
       
   362             response.add(start_msg.send_self());
       
   363 
       
   364             common::get_room_config_impl(&replay.config, client_id, response);
       
   365             common::get_teams(replay.teams.iter(), client_id, response);
       
   366             response.add(RunGame.send_self());
       
   367             response.add(ForwardEngineMessage(replay.message_log).send_self());
       
   368 
       
   369             if protocol < 58 {
       
   370                 response.add(Kicked.send_self());
       
   371             }
       
   372         }
       
   373         IoResult::Replay(None) => {
       
   374             response.add(Warning("Could't load the replay".to_string()).send_self())
       
   375         }
       
   376         IoResult::SaveRoom(_, true) => {
       
   377             response.add(server_chat("Room configs saved successfully.".to_string()).send_self());
       
   378         }
       
   379         IoResult::SaveRoom(_, false) => {
       
   380             response.add(Warning("Unable to save the room configs.".to_string()).send_self());
       
   381         }
       
   382         IoResult::LoadRoom(room_id, Some(contents)) => {
       
   383             if let Some(ref mut room) = server.rooms.get_mut(room_id) {
       
   384                 match room.set_saves(&contents) {
       
   385                     Ok(_) => response.add(
       
   386                         server_chat("Room configs loaded successfully.".to_string()).send_self(),
       
   387                     ),
       
   388                     Err(e) => {
       
   389                         warn!("Error while deserializing the room configs: {}", e);
       
   390                         response.add(
       
   391                             Warning("Unable to deserialize the room configs.".to_string())
       
   392                                 .send_self(),
       
   393                         );
       
   394                     }
       
   395                 }
       
   396             }
       
   397         }
       
   398         IoResult::LoadRoom(_, None) => {
       
   399             response.add(Warning("Unable to load the room configs.".to_string()).send_self());
       
   400         }
       
   401     }
       
   402 }