complete checker login handling
authoralfadur <mail@none>
Fri, 27 Dec 2019 22:36:19 +0300
changeset 15532 f1205f33bf5b
parent 15531 ede5f4ec48f3
child 15533 0606f89698e7
complete checker login handling
rust/hedgewars-server/src/core/client.rs
rust/hedgewars-server/src/core/server.rs
rust/hedgewars-server/src/handlers.rs
rust/hedgewars-server/src/handlers/inanteroom.rs
rust/hedgewars-server/src/handlers/strings.rs
rust/hedgewars-server/src/protocol/messages.rs
rust/hedgewars-server/src/server/database.rs
rust/hedgewars-server/src/server/io.rs
--- a/rust/hedgewars-server/src/core/client.rs	Thu Dec 26 21:55:51 2019 +0300
+++ b/rust/hedgewars-server/src/core/client.rs	Fri Dec 27 22:36:19 2019 +0300
@@ -2,16 +2,15 @@
 use bitflags::*;
 
 bitflags! {
-    pub struct ClientFlags: u16 {
+    pub struct ClientFlags: u8 {
         const IS_ADMIN = 0b0000_0001;
         const IS_MASTER = 0b0000_0010;
         const IS_READY = 0b0000_0100;
         const IS_IN_GAME = 0b0000_1000;
         const IS_JOINED_MID_GAME = 0b0001_0000;
-        const IS_CHECKER = 0b0010_0000;
-        const IS_CONTRIBUTOR = 0b0100_0000;
-        const HAS_SUPER_POWER = 0b1000_0000;
-        const IS_REGISTERED = 0b0001_0000_0000;
+        const IS_CONTRIBUTOR = 0b0010_0000;
+        const HAS_SUPER_POWER = 0b0100_0000;
+        const IS_REGISTERED = 0b1000_0000;
 
         const NONE = 0b0000_0000;
         const DEFAULT = Self::NONE.bits;
@@ -66,9 +65,6 @@
     pub fn is_joined_mid_game(&self) -> bool {
         self.contains(ClientFlags::IS_JOINED_MID_GAME)
     }
-    pub fn is_checker(&self) -> bool {
-        self.contains(ClientFlags::IS_CHECKER)
-    }
     pub fn is_contributor(&self) -> bool {
         self.contains(ClientFlags::IS_CONTRIBUTOR)
     }
@@ -94,9 +90,6 @@
     pub fn set_is_joined_mid_game(&mut self, value: bool) {
         self.set(ClientFlags::IS_JOINED_MID_GAME, value)
     }
-    pub fn set_is_checker(&mut self, value: bool) {
-        self.set(ClientFlags::IS_CHECKER, value)
-    }
     pub fn set_is_contributor(&mut self, value: bool) {
         self.set(ClientFlags::IS_CONTRIBUTOR, value)
     }
--- a/rust/hedgewars-server/src/core/server.rs	Thu Dec 26 21:55:51 2019 +0300
+++ b/rust/hedgewars-server/src/core/server.rs	Fri Dec 27 22:36:19 2019 +0300
@@ -157,9 +157,24 @@
     }
 }
 
+struct HwChecker {
+    pub id: ClientId,
+    pub is_ready: bool,
+}
+
+impl HwChecker {
+    pub fn new(id: ClientId) -> Self {
+        Self {
+            id,
+            is_ready: false,
+        }
+    }
+}
+
 pub struct HwServer {
     clients: IndexSlab<HwClient>,
     rooms: Slab<HwRoom>,
+    checkers: IndexSlab<HwChecker>,
     latest_protocol: u16,
     flags: ServerFlags,
     greetings: ServerGreetings,
@@ -169,9 +184,11 @@
     pub fn new(clients_limit: usize, rooms_limit: usize) -> Self {
         let rooms = Slab::with_capacity(rooms_limit);
         let clients = IndexSlab::with_capacity(clients_limit);
+        let checkers = IndexSlab::new();
         Self {
             clients,
             rooms,
+            checkers,
             greetings: ServerGreetings::new(),
             latest_protocol: 58,
             flags: ServerFlags::empty(),
@@ -242,9 +259,10 @@
     }
 
     pub fn add_client(&mut self, client_id: ClientId, data: HwAnteroomClient) {
-        if let (Some(protocol), Some(nick)) = (data.protocol_number, data.nick) {
+        if data.is_checker {
+            self.checkers.insert(client_id, HwChecker::new(client_id));
+        } else if let (Some(protocol), Some(nick)) = (data.protocol_number, data.nick) {
             let mut client = HwClient::new(client_id, protocol.get(), nick);
-            client.set_is_checker(data.is_checker);
             #[cfg(not(feature = "official-server"))]
             client.set_is_admin(data.is_local_admin);
 
--- a/rust/hedgewars-server/src/handlers.rs	Thu Dec 26 21:55:51 2019 +0300
+++ b/rust/hedgewars-server/src/handlers.rs	Fri Dec 27 22:36:19 2019 +0300
@@ -115,6 +115,10 @@
         client_salt: String,
         server_salt: String,
     },
+    GetCheckerAccount {
+        nick: String,
+        password: String,
+    },
     GetReplay {
         id: u32,
     },
@@ -133,6 +137,7 @@
 pub enum IoResult {
     AccountRegistered(bool),
     Account(Option<AccountInfo>),
+    CheckerAccount { is_registered: bool },
     Replay(Option<Replay>),
     SaveRoom(RoomId, bool),
     LoadRoom(RoomId, Option<String>),
@@ -410,13 +415,17 @@
                 response.add(Bye(REGISTRATION_REQUIRED.to_string()).send_self());
                 response.remove_client(client_id);
             } else if is_registered {
-                let salt = state.anteroom.clients[client_id].server_salt.clone();
-                response.add(AskPassword(salt).send_self());
+                let client = &state.anteroom.clients[client_id];
+                response.add(AskPassword(client.server_salt.clone()).send_self());
             } else if let Some(client) = state.anteroom.remove_client(client_id) {
                 state.server.add_client(client_id, client);
                 common::get_lobby_join_data(&state.server, response);
             }
         }
+        IoResult::Account(None) => {
+            response.add(Bye(AUTHENTICATION_FAILED.to_string()).send_self());
+            response.remove_client(client_id);
+        }
         IoResult::Account(Some(info)) => {
             response.add(ServerAuth(format!("{:x}", info.server_hash)).send_self());
             if let Some(mut client) = state.anteroom.remove_client(client_id) {
@@ -427,9 +436,16 @@
                 common::get_lobby_join_data(&state.server, response);
             }
         }
-        IoResult::Account(None) => {
-            response.error(AUTHENTICATION_FAILED);
-            response.remove_client(client_id);
+        IoResult::CheckerAccount { is_registered } => {
+            if is_registered {
+                if let Some(client) = state.anteroom.remove_client(client_id) {
+                    state.server.add_client(client_id, client);
+                    response.add(LogonPassed.send_self());
+                }
+            } else {
+                response.add(Bye(NO_CHECKER_RIGHTS.to_string()).send_self());
+                response.remove_client(client_id);
+            }
         }
         IoResult::Replay(Some(replay)) => {
             let client = state.server.client(client_id);
--- a/rust/hedgewars-server/src/handlers/inanteroom.rs	Thu Dec 26 21:55:51 2019 +0300
+++ b/rust/hedgewars-server/src/handlers/inanteroom.rs	Fri Dec 27 22:36:19 2019 +0300
@@ -1,5 +1,6 @@
 use mio;
 
+use super::strings::*;
 use crate::{
     core::{
         anteroom::{HwAnteroom, HwAnteroomClient},
@@ -33,18 +34,12 @@
 where
     I: Iterator<Item = &'a HwClient>,
 {
-    let has_nick_clash =
-        other_clients.any(|c| !c.is_checker() && c.nick == *client.nick.as_ref().unwrap());
+    let has_nick_clash = other_clients.any(|c| c.nick == *client.nick.as_ref().unwrap());
 
     if has_nick_clash {
-        if client.protocol_number.unwrap().get() < 38 {
-            response.add(Bye("User quit: Nickname is already in use".to_string()).send_self());
-            LoginResult::Exit
-        } else {
-            client.nick = None;
-            response.add(Notice("NickAlreadyInUse".to_string()).send_self());
-            LoginResult::Unchanged
-        }
+        client.nick = None;
+        response.add(Notice("NickAlreadyInUse".to_string()).send_self());
+        LoginResult::Unchanged
     } else {
         #[cfg(feature = "official-server")]
         {
@@ -76,10 +71,10 @@
             let client = &mut server_state.anteroom.clients[client_id];
 
             if client.nick.is_some() {
-                response.add(Error("Nickname already provided.".to_string()).send_self());
+                response.error(NICKNAME_PROVIDED);
                 LoginResult::Unchanged
             } else if is_name_illegal(&nick) {
-                response.add(Bye("Illegal nickname! Nicknames 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());
+                response.add(Bye(ILLEGAL_CLIENT_NAME.to_string()).send_self());
                 LoginResult::Exit
             } else {
                 client.nick = Some(nick.clone());
@@ -95,11 +90,11 @@
         HwProtocolMessage::Proto(proto) => {
             let client = &mut server_state.anteroom.clients[client_id];
             if client.protocol_number.is_some() {
-                response.add(Error("Protocol already known.".to_string()).send_self());
+                response.error(PROTOCOL_PROVIDED);
                 LoginResult::Unchanged
-            } else if proto == 0 {
-                response.add(Error("Bad number.".to_string()).send_self());
-                LoginResult::Unchanged
+            } else if proto < 48 {
+                response.add(Bye(PROTOCOL_TOO_OLD.to_string()).send_self());
+                LoginResult::Exit
             } else {
                 client.protocol_number = NonZeroU16::new(proto);
                 response.add(Proto(proto).send_self());
@@ -131,17 +126,29 @@
         HwProtocolMessage::Checker(protocol, nick, password) => {
             let client = &mut server_state.anteroom.clients[client_id];
             if protocol == 0 {
-                response.add(Error("Bad number.".to_string()).send_self());
+                response.error("Bad number.");
                 LoginResult::Unchanged
             } else {
                 client.protocol_number = NonZeroU16::new(protocol);
-                client.nick = Some(nick);
                 client.is_checker = true;
-                LoginResult::Complete
+                #[cfg(not(feature = "official-server"))]
+                {
+                    response.request_io(super::IoTask::GetCheckerAccount {
+                        nick: nick,
+                        password: password,
+                    });
+                    LoginResult::Unchanged
+                }
+
+                #[cfg(feature = "official-server")]
+                {
+                    response.add(LogonPassed.send_self());
+                    LoginResult::Complete
+                }
             }
         }
         _ => {
-            warn!("Incorrect command in logging-in state");
+            warn!("Incorrect command in anteroom");
             LoginResult::Unchanged
         }
     }
--- a/rust/hedgewars-server/src/handlers/strings.rs	Thu Dec 26 21:55:51 2019 +0300
+++ b/rust/hedgewars-server/src/handlers/strings.rs	Fri Dec 27 22:36:19 2019 +0300
@@ -1,11 +1,17 @@
 pub const ACCESS_DENIED: &str = "Access denied.";
-pub const AUTHENTICATION_FAILED: &str = "Authentication failed.";
+pub const AUTHENTICATION_FAILED: &str = "Authentication failed";
+pub const BAD_NUMBER: &str = "Bad number.";
+pub const ILLEGAL_CLIENT_NAME: &str = "Illegal nickname! Nicknames must be between 1-40 characters long, must not have a trailing or leading space and must not have any of these characters: $()*+?[]^{|}";
 pub const ILLEGAL_ROOM_NAME: &str = "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: $()*+?[]^{|}";
+pub const NICKNAME_PROVIDED: &str = "Nickname already provided.";
+pub const NO_CHECKER_RIGHTS: &str = "No checker rights";
 pub const NO_ROOM: &str = "No such room.";
 pub const NO_TEAM: &str = "No such team.";
 pub const NO_TEAM_TO_REMOVE: &str = "Error: The team you tried to remove does not exist.";
 pub const NO_USER: &str = "No such user.";
 pub const NOT_MASTER: &str = "You're not the room master!";
+pub const PROTOCOL_PROVIDED: &str = "Protocol already known.";
+pub const PROTOCOL_TOO_OLD: &str = "Protocol version is too old";
 pub const REPLAY_LOAD_FAILED: &str = "Could't load the replay";
 pub const REPLAY_NOT_SUPPORTED: &str = "This server does not support replays!";
 pub const REGISTRATION_REQUIRED: &str = "This server only allows registered users to join.";
--- a/rust/hedgewars-server/src/protocol/messages.rs	Thu Dec 26 21:55:51 2019 +0300
+++ b/rust/hedgewars-server/src/protocol/messages.rs	Fri Dec 27 22:36:19 2019 +0300
@@ -120,6 +120,7 @@
     Proto(u16),
     AskPassword(String),
     ServerAuth(String),
+    LogonPassed,
 
     LobbyLeft(String, String),
     LobbyJoined(Vec<String>),
@@ -390,6 +391,7 @@
             Proto(proto) => msg!["PROTO", proto],
             AskPassword(salt) => msg!["ASKPASSWORD", salt],
             ServerAuth(hash) => msg!["SERVER_AUTH", hash],
+            LogonPassed => msg!["LOGONPASSED"],
             LobbyLeft(nick, msg) => msg!["LOBBY:LEFT", nick, msg],
             LobbyJoined(nicks) => construct_message(&["LOBBY:JOINED"], &nicks),
             ClientFlags(flags, nicks) => construct_message(&["CLIENT_FLAGS", flags], &nicks),
--- a/rust/hedgewars-server/src/server/database.rs	Thu Dec 26 21:55:51 2019 +0300
+++ b/rust/hedgewars-server/src/server/database.rs	Fri Dec 27 22:36:19 2019 +0300
@@ -86,6 +86,23 @@
         }
     }
 
+    pub fn get_checker_account(
+        &mut self,
+        nick: &str,
+        checker_password: &str,
+    ) -> Result<bool, Error> {
+        if let Some(pool) = &self.pool {
+            if let Some(row) = pool.first_exec(GET_ACCOUNT_QUERY, params! { "username" => nick })? {
+                let (mut password, _, _) = from_row_opt::<(String, i32, i32)>(row)?;
+                Ok(checker_password == password)
+            } else {
+                Ok(false)
+            }
+        } else {
+            Err(DriverError::SetupError.into())
+        }
+    }
+
     pub fn store_stats(&mut self, stats: &ServerStatistics) -> Result<(), Error> {
         if let Some(pool) = &self.pool {
             for mut stmt in pool.prepare(STORE_STATS_QUERY).into_iter() {
--- a/rust/hedgewars-server/src/server/io.rs	Thu Dec 26 21:55:51 2019 +0300
+++ b/rust/hedgewars-server/src/server/io.rs	Fri Dec 27 22:36:19 2019 +0300
@@ -61,6 +61,18 @@
                         }
                     }
 
+                    IoTask::GetCheckerAccount { nick, password } => {
+                        match db.get_checker_account(&nick, &password) {
+                            Ok(is_registered) => IoResult::CheckerAccount { is_registered },
+                            Err(e) => {
+                                warn!("Unable to get checker account data: {}", e);
+                                IoResult::CheckerAccount {
+                                    is_registered: false,
+                                }
+                            }
+                        }
+                    }
+
                     IoTask::GetReplay { id } => {
                         let result = match db.get_replay_name(id) {
                             Ok(Some(filename)) => {