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