add server anteroom
authoralfadur <mail@none>
Thu, 07 Feb 2019 17:02:24 +0300
changeset 14698 6a2e13e36b7f
parent 14697 e5415faa117b
child 14699 25c564f77b7d
add server anteroom
rust/hedgewars-server/src/server/client.rs
rust/hedgewars-server/src/server/core.rs
rust/hedgewars-server/src/server/handlers.rs
rust/hedgewars-server/src/server/handlers/loggingin.rs
rust/hedgewars-server/src/server/indexslab.rs
rust/hedgewars-server/src/server/network.rs
--- a/rust/hedgewars-server/src/server/client.rs	Thu Feb 07 14:49:51 2019 +0300
+++ b/rust/hedgewars-server/src/server/client.rs	Thu Feb 07 17:02:24 2019 +0300
@@ -19,8 +19,6 @@
     pub id: ClientId,
     pub room_id: Option<usize>,
     pub nick: String,
-    pub web_password: String,
-    pub server_salt: String,
     pub protocol_number: u16,
     pub flags: ClientFlags,
     pub teams_in_game: u8,
@@ -29,14 +27,12 @@
 }
 
 impl HWClient {
-    pub fn new(id: ClientId, salt: String) -> HWClient {
+    pub fn new(id: ClientId, protocol_number: u16, nick: String) -> HWClient {
         HWClient {
             id,
+            nick,
+            protocol_number,
             room_id: None,
-            nick: String::new(),
-            web_password: String::new(),
-            server_salt: salt,
-            protocol_number: 0,
             flags: ClientFlags::DEFAULT,
             teams_in_game: 0,
             team_indices: Vec::new(),
--- a/rust/hedgewars-server/src/server/core.rs	Thu Feb 07 14:49:51 2019 +0300
+++ b/rust/hedgewars-server/src/server/core.rs	Thu Feb 07 17:02:24 2019 +0300
@@ -4,61 +4,88 @@
     client::HWClient,
     coretypes::{ClientId, RoomId},
     handlers,
+    indexslab::IndexSlab,
     io::HWServerIO,
     room::HWRoom,
 };
-use crate::protocol::messages::*;
-use crate::utils;
-use base64::encode;
+use crate::{protocol::messages::*, utils};
+
 use log::*;
-use rand::{thread_rng, RngCore};
 use slab;
-use std::borrow::BorrowMut;
+use std::{borrow::BorrowMut, iter, num::NonZeroU16};
 
 type Slab<T> = slab::Slab<T>;
 
+pub struct HWAnteClient {
+    pub nick: Option<String>,
+    pub protocol_number: Option<NonZeroU16>,
+    pub server_salt: String,
+    pub web_password: String,
+}
+
+pub struct HWAnteroom {
+    pub clients: IndexSlab<HWAnteClient>,
+}
+
+impl HWAnteroom {
+    pub fn new(clients_limit: usize) -> Self {
+        let clients = IndexSlab::with_capacity(clients_limit);
+        HWAnteroom { clients }
+    }
+
+    pub fn add_client(&mut self, client_id: ClientId, salt: String) {
+        let client = HWAnteClient {
+            nick: None,
+            protocol_number: None,
+            server_salt: salt,
+            web_password: "".to_string(),
+        };
+        self.clients.insert(client_id, client);
+    }
+
+    pub fn remove_client(&mut self, client_id: ClientId) -> Option<HWAnteClient> {
+        let mut client = self.clients.remove(client_id);
+        if let Some(ref mut client) = client {
+            client
+                .web_password
+                .replace_range(.., "🦔🦔🦔🦔🦔🦔🦔🦔");
+        }
+        client
+    }
+}
+
 pub struct HWServer {
-    pub clients: Slab<HWClient>,
+    pub clients: IndexSlab<HWClient>,
     pub rooms: Slab<HWRoom>,
     pub lobby_id: RoomId,
     pub output: Vec<(Vec<ClientId>, HWServerMessage)>,
     pub removed_clients: Vec<ClientId>,
     pub io: Box<dyn HWServerIO>,
+    pub anteroom: HWAnteroom,
 }
 
 impl HWServer {
-    pub fn new(clients_limit: usize, rooms_limit: usize, io: Box<dyn HWServerIO>) -> HWServer {
+    pub fn new(clients_limit: usize, rooms_limit: usize, io: Box<dyn HWServerIO>) -> Self {
         let rooms = Slab::with_capacity(rooms_limit);
-        let clients = Slab::with_capacity(clients_limit);
-        let mut server = HWServer {
+        let clients = IndexSlab::with_capacity(clients_limit);
+        let mut server = Self {
             clients,
             rooms,
             lobby_id: 0,
             output: vec![],
             removed_clients: vec![],
             io,
+            anteroom: HWAnteroom::new(clients_limit),
         };
         server.lobby_id = server.add_room().id;
         server
     }
 
-    pub fn add_client(&mut self) -> ClientId {
-        let key: ClientId;
-        {
-            let entry = self.clients.vacant_entry();
-            key = entry.key();
-            let mut salt = [0u8; 18];
-            thread_rng().fill_bytes(&mut salt);
-
-            let client = HWClient::new(entry.key(), encode(&salt));
-            entry.insert(client);
+    pub fn add_client(&mut self, client_id: ClientId, data: HWAnteClient) {
+        if let (Some(protocol), Some(nick)) = (data.protocol_number, data.nick) {
+            let client = HWClient::new(client_id, protocol.get(), nick);
+            self.clients.insert(client_id, client);
         }
-        self.send(
-            key,
-            &Destination::ToSelf,
-            HWServerMessage::Connected(utils::PROTOCOL_VERSION),
-        );
-        key
     }
 
     pub fn remove_client(&mut self, client_id: ClientId) {
@@ -92,19 +119,6 @@
         move_to_room(&mut self.clients[client_id], &mut self.rooms[room_id])
     }
 
-    pub fn send(
-        &mut self,
-        client_id: ClientId,
-        destination: &Destination,
-        message: HWServerMessage,
-    ) {
-
-    }
-
-    pub fn send_msg(&mut self, client_id: ClientId, message: PendingMessage) {
-        self.send(client_id, &message.destination, message.message)
-    }
-
     pub fn lobby(&self) -> &HWRoom {
         &self.rooms[self.lobby_id]
     }
--- a/rust/hedgewars-server/src/server/handlers.rs	Thu Feb 07 14:49:51 2019 +0300
+++ b/rust/hedgewars-server/src/server/handlers.rs	Thu Feb 07 17:02:24 2019 +0300
@@ -5,8 +5,11 @@
 use crate::{
     protocol::messages::{HWProtocolMessage, HWServerMessage, HWServerMessage::*},
     server::actions::PendingMessage,
+    utils,
 };
+use base64::encode;
 use log::*;
+use rand::{thread_rng, RngCore};
 
 mod checker;
 mod common;
@@ -14,6 +17,8 @@
 mod lobby;
 mod loggingin;
 
+use self::loggingin::LoginResult;
+
 pub struct Response {
     client_id: ClientId,
     messages: Vec<PendingMessage>,
@@ -102,27 +107,51 @@
     message: HWProtocolMessage,
 ) {
     match message {
-        HWProtocolMessage::Ping => {
-            response.add(Pong.send_self());
-        }
-        HWProtocolMessage::Quit(Some(msg)) => {
-            common::remove_client(server, response, "User quit: ".to_string() + &msg);
-        }
-        HWProtocolMessage::Quit(None) => {
-            common::remove_client(server, response, "User quit".to_string());
-        }
+        HWProtocolMessage::Ping => response.add(Pong.send_self()),
         HWProtocolMessage::Malformed => warn!("Malformed/unknown message"),
         HWProtocolMessage::Empty => warn!("Empty message"),
-        _ => match server.clients[client_id].room_id {
-            None => loggingin::handle(server, client_id, response, message),
-            Some(id) if id == server.lobby_id => {
-                lobby::handle(server, client_id, response, message)
+        _ => {
+            if server.anteroom.clients.contains(client_id) {
+                match loggingin::handle(&mut server.anteroom, client_id, response, message) {
+                    LoginResult::Unchanged => (),
+                    LoginResult::Complete => {
+                        if let Some(client) = server.anteroom.remove_client(client_id) {
+                            server.add_client(client_id, client);
+                        }
+                    }
+                    LoginResult::Exit => {
+                        server.anteroom.remove_client(client_id);
+                    }
+                }
+            } else {
+                match message {
+                    HWProtocolMessage::Quit(Some(msg)) => {
+                        common::remove_client(server, response, "User quit: ".to_string() + &msg);
+                    }
+                    HWProtocolMessage::Quit(None) => {
+                        common::remove_client(server, response, "User quit".to_string());
+                    }
+                    _ => match server.clients[client_id].room_id {
+                        None => lobby::handle(server, client_id, response, message),
+                        Some(room_id) => {
+                            inroom::handle(server, client_id, response, room_id, message)
+                        }
+                    },
+                }
             }
-            Some(id) => inroom::handle(server, client_id, response, id, message),
-        },
+        }
     }
 }
 
+pub fn handle_client_accept(server: &mut HWServer, client_id: ClientId, response: &mut Response) {
+    let mut salt = [0u8; 18];
+    thread_rng().fill_bytes(&mut salt);
+
+    server.anteroom.add_client(client_id, encode(&salt));
+
+    response.add(HWServerMessage::Connected(utils::PROTOCOL_VERSION).send_self());
+}
+
 pub fn handle_client_loss(server: &mut HWServer, client_id: ClientId, response: &mut Response) {
     common::remove_client(server, response, "Connection reset".to_string());
 }
--- a/rust/hedgewars-server/src/server/handlers/loggingin.rs	Thu Feb 07 14:49:51 2019 +0300
+++ b/rust/hedgewars-server/src/server/handlers/loggingin.rs	Thu Feb 07 17:02:24 2019 +0300
@@ -2,13 +2,19 @@
 
 use crate::{
     protocol::messages::{HWProtocolMessage, HWServerMessage::*},
-    server::{client::HWClient, core::HWServer, coretypes::ClientId},
+    server::{
+        core::{HWAnteClient, HWAnteroom},
+        coretypes::ClientId,
+    },
     utils::is_name_illegal,
 };
 use log::*;
 #[cfg(feature = "official-server")]
 use openssl::sha::sha1;
-use std::fmt::{Formatter, LowerHex};
+use std::{
+    fmt::{Formatter, LowerHex},
+    num::NonZeroU16,
+};
 
 #[derive(PartialEq)]
 struct Sha1Digest([u8; 20]);
@@ -23,7 +29,7 @@
 }
 
 #[cfg(feature = "official-server")]
-fn get_hash(client: &HWClient, salt1: &str, salt2: &str) -> Sha1Digest {
+fn get_hash(client: &HWAnteClient, salt1: &str, salt2: &str) -> Sha1Digest {
     let s = format!(
         "{}{}{}{}{}",
         salt1, salt2, client.web_password, client.protocol_number, "!hedgewars"
@@ -31,66 +37,88 @@
     Sha1Digest(sha1(s.as_bytes()))
 }
 
+pub enum LoginResult {
+    Unchanged,
+    Complete,
+    Exit,
+}
+
 pub fn handle(
-    server: &mut HWServer,
+    anteroom: &mut HWAnteroom,
     client_id: ClientId,
     response: &mut super::Response,
     message: HWProtocolMessage,
-) {
+) -> LoginResult {
     match message {
+        HWProtocolMessage::Quit(_) => {
+            response.add(Bye("User quit".to_string()).send_self());
+            LoginResult::Exit
+        }
         HWProtocolMessage::Nick(nick) => {
-            let client = &mut server.clients[client_id];
+            let client = &mut anteroom.clients[client_id];
             debug!("{} {}", nick, is_name_illegal(&nick));
-            if client.room_id != None {
-                unreachable!()
-            } else if !client.nick.is_empty() {
+            if !client.nick.is_some() {
                 response.add(Error("Nickname already provided.".to_string()).send_self());
+                LoginResult::Unchanged
             } else if is_name_illegal(&nick) {
-                super::common::remove_client(server, response, "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())
+                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());
+                LoginResult::Exit
             } else {
-                client.nick = nick.clone();
+                client.nick = Some(nick.clone());
                 response.add(Nick(nick).send_self());
 
-                if client.protocol_number > 0 {
-                    super::common::process_login(server, response);
+                if client.protocol_number.is_some() {
+                    LoginResult::Complete
+                } else {
+                    LoginResult::Unchanged
                 }
             }
         }
         HWProtocolMessage::Proto(proto) => {
-            let client = &mut server.clients[client_id];
-            if client.protocol_number != 0 {
+            let client = &mut anteroom.clients[client_id];
+            if client.protocol_number.is_some() {
                 response.add(Error("Protocol already known.".to_string()).send_self());
+                LoginResult::Unchanged
             } else if proto == 0 {
                 response.add(Error("Bad number.".to_string()).send_self());
+                LoginResult::Unchanged
             } else {
-                client.protocol_number = proto;
+                client.protocol_number = NonZeroU16::new(proto);
                 response.add(Proto(proto).send_self());
 
-                if client.nick != "" {
-                    super::common::process_login(server, response);
+                if client.nick.is_some() {
+                    LoginResult::Complete
+                } else {
+                    LoginResult::Unchanged
                 }
             }
         }
         #[cfg(feature = "official-server")]
         HWProtocolMessage::Password(hash, salt) => {
-            let c = &server.clients[client_id];
+            let client = &anteroom.clients[client_id];
 
-            let client_hash = get_hash(c, &salt, &c.server_salt);
-            let server_hash = get_hash(c, &c.server_salt, &salt);
+            let client_hash = get_hash(client, &salt, &client.server_salt);
+            let server_hash = get_hash(client, &client.server_salt, &salt);
             if client_hash == server_hash {
                 response.add(ServerAuth(format!("{:x}", server_hash)).send_self());
-            //TODO enter lobby
+                LoginResult::Complete
             } else {
-                super::common::remove_client(server, response, "Authentication failed".to_string())
+                response.add(Bye("Authentication failed".to_string()).send_self());
+                LoginResult::Exit
             }
         }
         #[cfg(feature = "official-server")]
         HWProtocolMessage::Checker(protocol, nick, password) => {
-            let c = &mut server.clients[client_id];
-            c.nick = nick;
-            c.web_password = password;
-            c.set_is_checker(true);
+            let client = &mut anteroom.clients[client_id];
+            client.protocol_number = Some(protocol);
+            client.nick = Some(nick);
+            client.web_password = password;
+            //client.set_is_checker(true);
+            LoginResult::Complete
         }
-        _ => warn!("Incorrect command in logging-in state"),
+        _ => {
+            warn!("Incorrect command in logging-in state");
+            LoginResult::Unchanged
+        }
     }
 }
--- a/rust/hedgewars-server/src/server/indexslab.rs	Thu Feb 07 14:49:51 2019 +0300
+++ b/rust/hedgewars-server/src/server/indexslab.rs	Thu Feb 07 17:02:24 2019 +0300
@@ -1,5 +1,6 @@
 use std::{
     iter,
+    mem::replace,
     ops::{Index, IndexMut},
 };
 
@@ -32,9 +33,11 @@
         self.data.get(index).and_then(|x| x.as_ref()).is_some()
     }
 
-    pub fn remove(&mut self, index: usize) {
+    pub fn remove(&mut self, index: usize) -> Option<T> {
         if let Some(x) = self.data.get_mut(index) {
-            *x = None
+            replace(x, None)
+        } else {
+            None
         }
     }
 
--- a/rust/hedgewars-server/src/server/network.rs	Thu Feb 07 14:49:51 2019 +0300
+++ b/rust/hedgewars-server/src/server/network.rs	Thu Feb 07 17:02:24 2019 +0300
@@ -309,22 +309,25 @@
     fn register_client(
         &mut self,
         poll: &Poll,
-        id: ClientId,
         client_socket: ClientSocket,
         addr: SocketAddr,
-    ) {
+    ) -> ClientId {
+        let entry = self.clients.vacant_entry();
+        let client_id = entry.key();
+
         poll.register(
             client_socket.inner(),
-            Token(id),
+            Token(client_id),
             Ready::readable() | Ready::writable(),
             PollOpt::edge(),
         )
         .expect("could not register socket with event loop");
 
-        let entry = self.clients.vacant_entry();
-        let client = NetworkClient::new(id, client_socket, addr);
+        let client = NetworkClient::new(client_id, client_socket, addr);
         info!("client {} ({}) added", client.id, client.peer_addr);
         entry.insert(client);
+
+        client_id
     }
 
     fn flush_server_messages(&mut self, mut response: handlers::Response) {
@@ -371,14 +374,15 @@
         let (client_socket, addr) = self.listener.accept()?;
         info!("Connected: {}", addr);
 
-        let client_id = self.server.add_client();
-        self.register_client(
-            poll,
-            client_id,
-            self.create_client_socket(client_socket)?,
-            addr,
-        );
-        //TODO: create response for initial messages
+        let client_id = self.register_client(poll, self.create_client_socket(client_socket)?, addr);
+
+        let mut response = handlers::Response::new(client_id);
+
+        handlers::handle_client_accept(&mut self.server, client_id, &mut response);
+
+        if !response.is_empty() {
+            self.flush_server_messages(response);
+        }
 
         Ok(())
     }