--- a/rust/hedgewars-server/Cargo.toml Tue Apr 09 00:45:14 2019 +0200
+++ b/rust/hedgewars-server/Cargo.toml Tue Apr 09 21:08:35 2019 +0300
@@ -12,6 +12,7 @@
[dependencies]
rand = "0.6"
mio = "0.6"
+mio-extras = "2.0.5"
slab = "0.4"
netbuf = "0.4"
nom = { git = "https://github.com/Geal/nom", branch = "5.0" }
--- a/rust/hedgewars-server/src/main.rs Tue Apr 09 00:45:14 2019 +0200
+++ b/rust/hedgewars-server/src/main.rs Tue Apr 09 21:08:35 2019 +0300
@@ -40,13 +40,16 @@
for event in events.iter() {
if event.readiness() & Ready::readable() == Ready::readable() {
match event.token() {
- utils::SERVER => hw_network.accept_client(&poll).unwrap(),
+ utils::SERVER_TOKEN => hw_network.accept_client(&poll).unwrap(),
+ #[cfg(feature = "official-server")]
+ utils::IO_TOKEN => hw_network.handle_io_result(),
Token(tok) => hw_network.client_readable(&poll, tok).unwrap(),
}
}
if event.readiness() & Ready::writable() == Ready::writable() {
match event.token() {
- utils::SERVER => unreachable!(),
+ utils::SERVER_TOKEN => unreachable!(),
+ utils::IO_TOKEN => unreachable!(),
Token(tok) => hw_network.client_writable(&poll, tok).unwrap(),
}
}
--- a/rust/hedgewars-server/src/protocol/messages.rs Tue Apr 09 00:45:14 2019 +0200
+++ b/rust/hedgewars-server/src/protocol/messages.rs Tue Apr 09 21:08:35 2019 +0300
@@ -71,8 +71,11 @@
Ping,
Pong,
Bye(String),
+
Nick(String),
Proto(u16),
+ AskPassword(String),
+
ServerAuth(String),
LobbyLeft(String, String),
LobbyJoined(Vec<String>),
@@ -282,6 +285,7 @@
Bye(msg) => msg!["BYE", msg],
Nick(nick) => msg!["NICK", nick],
Proto(proto) => msg!["PROTO", proto],
+ AskPassword(salt) => msg!["ASKPASSWORD", salt],
ServerAuth(hash) => msg!["SERVER_AUTH", hash],
LobbyLeft(nick, msg) => msg!["LOBBY:LEFT", nick, msg],
LobbyJoined(nicks) => construct_message(&["LOBBY:JOINED"], &nicks),
--- a/rust/hedgewars-server/src/server.rs Tue Apr 09 00:45:14 2019 +0200
+++ b/rust/hedgewars-server/src/server.rs Tue Apr 09 21:08:35 2019 +0300
@@ -6,6 +6,7 @@
mod database;
mod handlers;
pub mod indexslab;
+#[cfg(feature = "official-server")]
pub mod io;
pub mod network;
pub mod room;
--- a/rust/hedgewars-server/src/server/client.rs Tue Apr 09 00:45:14 2019 +0200
+++ b/rust/hedgewars-server/src/server/client.rs Tue Apr 09 21:08:35 2019 +0300
@@ -9,6 +9,7 @@
const IS_IN_GAME = 0b0000_1000;
const IS_JOINED_MID_GAME = 0b0001_0000;
const IS_CHECKER = 0b0010_0000;
+ const IS_CONTRIBUTOR = 0b0100_0000;
const NONE = 0b0000_0000;
const DEFAULT = Self::NONE.bits;
@@ -66,6 +67,9 @@
pub fn is_checker(&self) -> bool {
self.contains(ClientFlags::IS_CHECKER)
}
+ pub fn is_contributor(&self) -> bool {
+ self.contains(ClientFlags::IS_CONTRIBUTOR)
+ }
pub fn set_is_admin(&mut self, value: bool) {
self.set(ClientFlags::IS_ADMIN, value)
@@ -85,4 +89,7 @@
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/server/core.rs Tue Apr 09 00:45:14 2019 +0200
+++ b/rust/hedgewars-server/src/server/core.rs Tue Apr 09 21:08:35 2019 +0300
@@ -2,13 +2,14 @@
client::HWClient,
coretypes::{ClientId, RoomId},
indexslab::IndexSlab,
- io::HWServerIO,
room::HWRoom,
};
use crate::utils;
use log::*;
use slab;
+use std::fs::{File, OpenOptions};
+use std::io::{Read, Write};
use std::{borrow::BorrowMut, iter, num::NonZeroU16};
type Slab<T> = slab::Slab<T>;
@@ -16,7 +17,6 @@
pub struct HWAnteClient {
pub nick: Option<String>,
pub protocol_number: Option<NonZeroU16>,
- pub web_password: Option<String>,
pub server_salt: String,
}
@@ -35,20 +35,12 @@
nick: None,
protocol_number: None,
server_salt: salt,
- web_password: None,
};
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(HWAnteClient {
- web_password: Some(ref mut password),
- ..
- }) = client
- {
- password.replace_range(.., "🦔🦔🦔🦔🦔🦔🦔🦔");
- }
client
}
}
@@ -213,3 +205,48 @@
}
}
}
+
+pub trait HWServerIO {
+ fn write_file(&mut self, name: &str, content: &str) -> std::io::Result<()>;
+ fn read_file(&mut self, name: &str) -> std::io::Result<String>;
+}
+
+pub struct EmptyServerIO {}
+
+impl EmptyServerIO {
+ pub fn new() -> Self {
+ Self {}
+ }
+}
+
+impl HWServerIO for EmptyServerIO {
+ fn write_file(&mut self, _name: &str, _content: &str) -> std::io::Result<()> {
+ Ok(())
+ }
+
+ fn read_file(&mut self, _name: &str) -> std::io::Result<String> {
+ Ok("".to_string())
+ }
+}
+
+pub struct FileServerIO {}
+
+impl FileServerIO {
+ pub fn new() -> Self {
+ Self {}
+ }
+}
+
+impl HWServerIO for FileServerIO {
+ fn write_file(&mut self, name: &str, content: &str) -> std::io::Result<()> {
+ let mut writer = OpenOptions::new().create(true).write(true).open(name)?;
+ writer.write_all(content.as_bytes())
+ }
+
+ fn read_file(&mut self, name: &str) -> std::io::Result<String> {
+ let mut reader = File::open(name)?;
+ let mut result = String::new();
+ reader.read_to_string(&mut result)?;
+ Ok(result)
+ }
+}
--- a/rust/hedgewars-server/src/server/database.rs Tue Apr 09 00:45:14 2019 +0200
+++ b/rust/hedgewars-server/src/server/database.rs Tue Apr 09 21:08:35 2019 +0300
@@ -1,11 +1,20 @@
use mysql;
-use mysql::{error::DriverError, error::Error, params};
+use mysql::{error::DriverError, error::Error, from_row_opt, params};
+use openssl::sha::sha1;
+
+use super::handlers::AccountInfo;
+use crate::server::handlers::Sha1Digest;
-struct AccountInfo {
- is_registered: bool,
- is_admin: bool,
- is_contributor: bool,
-}
+const GET_ACCOUNT_QUERY: &str =
+ r"SELECT CASE WHEN users.status = 1 THEN users.pass ELSE '' END,
+ (SELECT COUNT(users_roles.rid) FROM users_roles WHERE users.uid = users_roles.uid AND users_roles.rid = 3),
+ (SELECT COUNT(users_roles.rid) FROM users_roles WHERE users.uid = users_roles.uid AND users_roles.rid = 13)
+ FROM users WHERE users.name = :username";
+
+const STORE_STATS_QUERY: &str = r"INSERT INTO gameserver_stats
+ (players, rooms, last_update)
+ VALUES
+ (:players, :rooms, UNIX_TIMESTAMP())";
struct ServerStatistics {
rooms: u32,
@@ -14,47 +23,63 @@
struct Achievements {}
-trait DatabaseInterface {
- fn check_account(username: &str, password: &str) -> AccountInfo;
- fn store_stats(stats: &ServerStatistics) -> Result<(), ()>;
- fn store_achievements(achievements: &Achievements) -> Result<(), ()>;
- fn get_replay_name(replay_id: u32) -> Result<String, ()>;
-}
-
-struct Database {
+pub struct Database {
pool: Option<mysql::Pool>,
}
impl Database {
- fn new() -> Self {
+ pub fn new() -> Self {
Self { pool: None }
}
- fn connect(&mut self, url: &str) -> Result<(), Error> {
+ pub fn connect(&mut self, url: &str) -> Result<(), Error> {
self.pool = Some(mysql::Pool::new(url)?);
Ok(())
}
- fn check_account(&mut self, username: &str, password: &str) -> AccountInfo {
- AccountInfo {
- is_registered: false,
- is_admin: false,
- is_contributor: false,
+ pub fn get_account(
+ &mut self,
+ nick: &str,
+ protocol: u16,
+ password_hash: &str,
+ client_salt: &str,
+ server_salt: &str,
+ ) -> Result<Option<AccountInfo>, Error> {
+ if let Some(pool) = &self.pool {
+ if let Some(row) = pool.first_exec(GET_ACCOUNT_QUERY, params! { "username" => nick })? {
+ let (mut password, is_admin, is_contributor) =
+ from_row_opt::<(String, i32, i32)>(row)?;
+ let client_hash = get_hash(protocol, &password, &client_salt, &server_salt);
+ let server_hash = get_hash(protocol, &password, &server_salt, &client_salt);
+ password.replace_range(.., "🦔🦔🦔🦔🦔🦔🦔🦔");
+
+ if server_hash == client_hash {
+ Ok(Some(AccountInfo {
+ is_registered: true,
+ is_admin: is_admin == 1,
+ is_contributor: is_contributor == 1,
+ server_hash,
+ }))
+ } else {
+ Ok(None)
+ }
+ } else {
+ Ok(Some(AccountInfo {
+ is_registered: false,
+ is_admin: false,
+ is_contributor: false,
+ server_hash: Sha1Digest::new([0; 20]),
+ }))
+ }
+ } else {
+ Err(DriverError::SetupError.into())
}
}
- fn store_stats(&mut self, stats: &ServerStatistics) -> Result<(), Error> {
+ pub fn store_stats(&mut self, stats: &ServerStatistics) -> Result<(), Error> {
if let Some(pool) = &self.pool {
- for mut stmt in pool
- .prepare(
- r"INSERT INTO gameserver_stats
- (players, rooms, last_update)
- VALUES
- (:players, :rooms, UNIX_TIMESTAMP())",
- )
- .into_iter()
- {
+ for mut stmt in pool.prepare(STORE_STATS_QUERY).into_iter() {
stmt.execute(params! {
"players" => stats.players,
"rooms" => stats.rooms,
@@ -66,11 +91,19 @@
}
}
- fn store_achievements(&mut self, achievements: &Achievements) -> Result<(), ()> {
+ pub fn store_achievements(&mut self, achievements: &Achievements) -> Result<(), ()> {
Ok(())
}
- fn get_replay_name(&mut self, replay_id: u32) -> Result<String, ()> {
+ pub fn get_replay_name(&mut self, replay_id: u32) -> Result<String, ()> {
Err(())
}
}
+
+fn get_hash(protocol_number: u16, web_password: &str, salt1: &str, salt2: &str) -> Sha1Digest {
+ let s = format!(
+ "{}{}{}{}{}",
+ salt1, salt2, web_password, protocol_number, "!hedgewars"
+ );
+ Sha1Digest::new(sha1(s.as_bytes()))
+}
--- a/rust/hedgewars-server/src/server/handlers.rs Tue Apr 09 00:45:14 2019 +0200
+++ b/rust/hedgewars-server/src/server/handlers.rs Tue Apr 09 21:08:35 2019 +0300
@@ -1,10 +1,11 @@
use mio;
-use std::{io, io::Write};
+use std::{collections::HashMap, io, io::Write};
use super::{
actions::{Destination, DestinationRoom},
core::HWServer,
coretypes::ClientId,
+ room::RoomSave,
};
use crate::{
protocol::messages::{HWProtocolMessage, HWServerMessage, HWServerMessage::*},
@@ -22,10 +23,51 @@
mod loggingin;
use self::loggingin::LoginResult;
+use std::fmt::{Formatter, LowerHex};
+
+#[derive(PartialEq)]
+pub struct Sha1Digest([u8; 20]);
+
+impl Sha1Digest {
+ pub fn new(digest: [u8; 20]) -> Self {
+ Self(digest)
+ }
+}
+
+impl LowerHex for Sha1Digest {
+ fn fmt(&self, f: &mut Formatter) -> Result<(), std::fmt::Error> {
+ for byte in &self.0 {
+ write!(f, "{:02x}", byte)?;
+ }
+ Ok(())
+ }
+}
+
+pub struct AccountInfo {
+ pub is_registered: bool,
+ pub is_admin: bool,
+ pub is_contributor: bool,
+ pub server_hash: Sha1Digest,
+}
+
+pub enum IoTask {
+ GetAccount {
+ nick: String,
+ protocol: u16,
+ password_hash: String,
+ client_salt: String,
+ server_salt: String,
+ },
+}
+
+pub enum IoResult {
+ Account(Option<AccountInfo>),
+}
pub struct Response {
client_id: ClientId,
messages: Vec<PendingMessage>,
+ io_tasks: Vec<IoTask>,
removed_clients: Vec<ClientId>,
}
@@ -34,13 +76,14 @@
Self {
client_id,
messages: vec![],
+ io_tasks: vec![],
removed_clients: vec![],
}
}
#[inline]
pub fn is_empty(&self) -> bool {
- self.messages.is_empty() && self.removed_clients.is_empty()
+ self.messages.is_empty() && self.removed_clients.is_empty() && self.io_tasks.is_empty()
}
#[inline]
@@ -58,6 +101,11 @@
self.messages.push(message)
}
+ #[inline]
+ pub fn request_io(&mut self, task: IoTask) {
+ self.io_tasks.push(task)
+ }
+
pub fn extract_messages<'a, 'b: 'a>(
&'b mut self,
server: &'a HWServer,
@@ -76,6 +124,10 @@
pub fn extract_removed_clients(&mut self) -> impl Iterator<Item = ClientId> + '_ {
self.removed_clients.drain(..)
}
+
+ pub fn extract_io_tasks(&mut self) -> impl Iterator<Item = IoTask> + '_ {
+ self.io_tasks.drain(..)
+ }
}
impl Extend<PendingMessage> for Response {
@@ -177,3 +229,26 @@
common::remove_client(server, response, "Connection reset".to_string());
}
}
+
+pub fn handle_io_result(
+ server: &mut HWServer,
+ client_id: ClientId,
+ response: &mut Response,
+ io_result: IoResult,
+) {
+ match io_result {
+ IoResult::Account(Some(info)) => {
+ response.add(ServerAuth(format!("{:x}", info.server_hash)).send_self());
+ if let Some(client) = server.anteroom.remove_client(client_id) {
+ server.add_client(client_id, client);
+ let client = &mut server.clients[client_id];
+ client.set_is_admin(info.is_admin);
+ client.set_is_contributor(info.is_admin)
+ }
+ }
+ IoResult::Account(None) => {
+ response.add(Error("Authentication failed.".to_string()).send_self());
+ response.remove_client(client_id);
+ }
+ }
+}
--- a/rust/hedgewars-server/src/server/handlers/loggingin.rs Tue Apr 09 00:45:14 2019 +0200
+++ b/rust/hedgewars-server/src/server/handlers/loggingin.rs Tue Apr 09 21:08:35 2019 +0300
@@ -1,5 +1,6 @@
use mio;
+use crate::protocol::messages::HWProtocolMessage::LoadRoom;
use crate::{
protocol::messages::{HWProtocolMessage, HWServerMessage::*},
server::{
@@ -16,33 +17,25 @@
num::NonZeroU16,
};
-#[derive(PartialEq)]
-struct Sha1Digest([u8; 20]);
-
-impl LowerHex for Sha1Digest {
- fn fmt(&self, f: &mut Formatter) -> Result<(), std::fmt::Error> {
- for byte in &self.0 {
- write!(f, "{:02x}", byte)?;
- }
- Ok(())
- }
-}
-
-#[cfg(feature = "official-server")]
-fn get_hash(protocol_number: u16, web_password: &str, salt1: &str, salt2: &str) -> Sha1Digest {
- let s = format!(
- "{}{}{}{}{}",
- salt1, salt2, web_password, protocol_number, "!hedgewars"
- );
- Sha1Digest(sha1(s.as_bytes()))
-}
-
pub enum LoginResult {
Unchanged,
Complete,
Exit,
}
+fn completion_result(client: &HWAnteClient, response: &mut super::Response) -> LoginResult {
+ #[cfg(feature = "official-server")]
+ {
+ response.add(AskPassword(client.server_salt.clone()).send_self());
+ LoginResult::Unchanged
+ }
+
+ #[cfg(not(feature = "official-server"))]
+ {
+ LoginResult::Complete
+ }
+}
+
pub fn handle(
anteroom: &mut HWAnteroom,
client_id: ClientId,
@@ -68,7 +61,7 @@
response.add(Nick(nick).send_self());
if client.protocol_number.is_some() {
- LoginResult::Complete
+ completion_result(&client, response)
} else {
LoginResult::Unchanged
}
@@ -87,7 +80,7 @@
response.add(Proto(proto).send_self());
if client.nick.is_some() {
- LoginResult::Complete
+ completion_result(&client, response)
} else {
LoginResult::Unchanged
}
@@ -97,29 +90,23 @@
HWProtocolMessage::Password(hash, salt) => {
let client = &anteroom.clients[client_id];
- if let (Some(protocol), Some(password)) =
- (client.protocol_number, client.web_password.as_ref())
- {
- let client_hash = get_hash(protocol.get(), &password, &salt, &client.server_salt);
- let server_hash = get_hash(protocol.get(), &password, &client.server_salt, &salt);
- if client_hash == server_hash {
- response.add(ServerAuth(format!("{:x}", server_hash)).send_self());
- LoginResult::Complete
- } else {
- response.add(Bye("No protocol provided.".to_string()).send_self());
- LoginResult::Unchanged
- }
- } else {
- response.add(Bye("Authentication failed.".to_string()).send_self());
- LoginResult::Exit
- }
+ if let (Some(nick), Some(protocol)) = (client.nick.as_ref(), client.protocol_number) {
+ response.request_io(super::IoTask::GetAccount {
+ nick: nick.clone(),
+ protocol: protocol.get(),
+ server_salt: client.server_salt.clone(),
+ client_salt: salt,
+ password_hash: hash,
+ });
+ };
+
+ LoginResult::Unchanged
}
#[cfg(feature = "official-server")]
HWProtocolMessage::Checker(protocol, nick, password) => {
let client = &mut anteroom.clients[client_id];
client.protocol_number = NonZeroU16::new(protocol);
client.nick = Some(nick);
- client.web_password = Some(password);
//client.set_is_checker(true);
LoginResult::Complete
}
--- a/rust/hedgewars-server/src/server/io.rs Tue Apr 09 00:45:14 2019 +0200
+++ b/rust/hedgewars-server/src/server/io.rs Tue Apr 09 21:08:35 2019 +0300
@@ -1,49 +1,73 @@
use std::{
fs::{File, OpenOptions},
io::{Error, ErrorKind, Read, Result, Write},
+ sync::mpsc,
+ thread,
};
-pub trait HWServerIO {
- fn write_file(&mut self, name: &str, content: &str) -> Result<()>;
- fn read_file(&mut self, name: &str) -> Result<String>;
+use crate::server::{
+ database::Database,
+ handlers::{IoResult, IoTask},
+};
+use mio::{Evented, Poll, PollOpt};
+use mio_extras::channel;
+
+pub type RequestId = u32;
+
+pub struct IOThread {
+ core_tx: mpsc::Sender<(RequestId, IoTask)>,
+ core_rx: channel::Receiver<(RequestId, IoResult)>,
}
-pub struct EmptyServerIO {}
-
-impl EmptyServerIO {
+impl IOThread {
pub fn new() -> Self {
- Self {}
- }
-}
+ let (core_tx, io_rx) = mpsc::channel();
+ let (io_tx, core_rx) = channel::channel();
+
+ let mut db = Database::new();
+ db.connect("localhost");
-impl HWServerIO for EmptyServerIO {
- fn write_file(&mut self, _name: &str, _content: &str) -> Result<()> {
- Ok(())
+ thread::spawn(move || {
+ while let Ok((request_id, task)) = io_rx.try_recv() {
+ match task {
+ IoTask::GetAccount {
+ nick,
+ protocol,
+ password_hash,
+ client_salt,
+ server_salt,
+ } => {
+ if let Ok(account) = db.get_account(
+ &nick,
+ protocol,
+ &password_hash,
+ &client_salt,
+ &server_salt,
+ ) {
+ io_tx.send((request_id, IoResult::Account(account)));
+ }
+ }
+ }
+ }
+ });
+
+ Self { core_rx, core_tx }
}
- fn read_file(&mut self, _name: &str) -> Result<String> {
- Ok("".to_string())
+ pub fn send(&self, request_id: RequestId, task: IoTask) {
+ self.core_tx.send((request_id, task)).unwrap();
}
-}
-pub struct FileServerIO {}
+ pub fn try_recv(&self) -> Option<(RequestId, IoResult)> {
+ match self.core_rx.try_recv() {
+ Ok(result) => Some(result),
+ Err(mpsc::TryRecvError::Empty) => None,
+ Err(mpsc::TryRecvError::Disconnected) => unreachable!(),
+ }
+ }
-impl FileServerIO {
- pub fn new() -> Self {
- Self {}
+ pub fn register_rx(&self, poll: &mio::Poll, token: mio::Token) -> Result<()> {
+ self.core_rx
+ .register(poll, token, mio::Ready::readable(), PollOpt::edge())
}
}
-
-impl HWServerIO for FileServerIO {
- fn write_file(&mut self, name: &str, content: &str) -> Result<()> {
- let mut writer = OpenOptions::new().create(true).write(true).open(name)?;
- writer.write_all(content.as_bytes())
- }
-
- fn read_file(&mut self, name: &str) -> Result<String> {
- let mut reader = File::open(name)?;
- let mut result = String::new();
- reader.read_to_string(&mut result)?;
- Ok(result)
- }
-}
--- a/rust/hedgewars-server/src/server/network.rs Tue Apr 09 00:45:14 2019 +0200
+++ b/rust/hedgewars-server/src/server/network.rs Tue Apr 09 21:08:35 2019 +0300
@@ -16,11 +16,16 @@
use netbuf;
use slab::Slab;
-use super::{core::HWServer, coretypes::ClientId, handlers, io::FileServerIO};
+use super::{core::FileServerIO, core::HWServer, coretypes::ClientId, handlers};
use crate::{
protocol::{messages::*, ProtocolDecoder},
utils,
};
+
+#[cfg(feature = "official-server")]
+use super::io::{IOThread, RequestId};
+
+use crate::server::handlers::{IoResult, IoTask};
#[cfg(feature = "tls-connections")]
use openssl::{
error::ErrorStack,
@@ -234,6 +239,56 @@
context: SslContext,
}
+#[cfg(feature = "official-server")]
+pub struct IoLayer {
+ next_request_id: RequestId,
+ request_queue: Vec<(RequestId, ClientId)>,
+ io_thread: IOThread,
+}
+
+#[cfg(feature = "official-server")]
+impl IoLayer {
+ fn new() -> Self {
+ Self {
+ next_request_id: 0,
+ request_queue: vec![],
+ io_thread: IOThread::new(),
+ }
+ }
+
+ fn send(&mut self, client_id: ClientId, task: IoTask) {
+ let request_id = self.next_request_id;
+ self.next_request_id += 1;
+ self.request_queue.push((request_id, client_id));
+ self.io_thread.send(request_id, task);
+ }
+
+ fn try_recv(&mut self) -> Option<(ClientId, IoResult)> {
+ let (request_id, result) = self.io_thread.try_recv()?;
+ if let Some(index) = self
+ .request_queue
+ .iter()
+ .position(|(id, _)| *id == request_id)
+ {
+ let (_, client_id) = self.request_queue.swap_remove(index);
+ Some((client_id, result))
+ } else {
+ None
+ }
+ }
+
+ fn cancel(&mut self, client_id: ClientId) {
+ let mut index = 0;
+ while index < self.request_queue.len() {
+ if self.request_queue[index].1 == client_id {
+ self.request_queue.swap_remove(index);
+ } else {
+ index += 1;
+ }
+ }
+ }
+}
+
pub struct NetworkLayer {
listener: TcpListener,
server: HWServer,
@@ -242,6 +297,8 @@
pending_cache: Vec<(ClientId, NetworkClientState)>,
#[cfg(feature = "tls-connections")]
ssl: ServerSsl,
+ #[cfg(feature = "official-server")]
+ io: IoLayer,
}
impl NetworkLayer {
@@ -259,6 +316,8 @@
pending_cache,
#[cfg(feature = "tls-connections")]
ssl: NetworkLayer::create_ssl_context(),
+ #[cfg(feature = "official-server")]
+ io: IoLayer::new(),
}
}
@@ -283,10 +342,15 @@
pub fn register_server(&self, poll: &Poll) -> io::Result<()> {
poll.register(
&self.listener,
- utils::SERVER,
+ utils::SERVER_TOKEN,
Ready::readable(),
PollOpt::edge(),
- )
+ )?;
+
+ #[cfg(feature = "official-server")]
+ self.io.io_thread.register_rx(poll, utils::IO_TOKEN)?;
+
+ Ok(())
}
fn deregister_client(&mut self, poll: &Poll, id: ClientId) {
@@ -299,6 +363,8 @@
}
if client_exists {
self.clients.remove(id);
+ #[cfg(feature = "official-server")]
+ self.io.cancel(id);
}
}
@@ -326,7 +392,7 @@
client_id
}
- fn flush_server_messages(&mut self, mut response: handlers::Response, poll: &Poll) {
+ fn handle_response(&mut self, mut response: handlers::Response, poll: &Poll) {
debug!("{} pending server messages", response.len());
let output = response.extract_messages(&mut self.server);
for (clients, message) in output {
@@ -344,6 +410,22 @@
for client_id in response.extract_removed_clients() {
self.deregister_client(poll, client_id);
}
+
+ #[cfg(feature = "official-server")]
+ {
+ let client_id = response.client_id();
+ for task in response.extract_io_tasks() {
+ self.io.send(client_id, task);
+ }
+ }
+ }
+
+ #[cfg(feature = "official-server")]
+ pub fn handle_io_result(&mut self) {
+ if let Some((client_id, result)) = self.io.try_recv() {
+ let mut response = handlers::Response::new(client_id);
+ handlers::handle_io_result(&mut self.server, client_id, &mut response, result);
+ }
}
fn create_client_socket(&self, socket: TcpStream) -> io::Result<ClientSocket> {
@@ -381,7 +463,7 @@
handlers::handle_client_accept(&mut self.server, client_id, &mut response);
if !response.is_empty() {
- self.flush_server_messages(response, poll);
+ self.handle_response(response, poll);
}
Ok(())
@@ -438,7 +520,7 @@
}
if !response.is_empty() {
- self.flush_server_messages(response, poll);
+ self.handle_response(response, poll);
}
Ok(())
@@ -469,7 +551,7 @@
self.deregister_client(poll, client_id);
let mut response = handlers::Response::new(client_id);
handlers::handle_client_loss(&mut self.server, client_id, &mut response);
- self.flush_server_messages(response, poll);
+ self.handle_response(response, poll);
Ok(())
}
--- a/rust/hedgewars-server/src/utils.rs Tue Apr 09 00:45:14 2019 +0200
+++ b/rust/hedgewars-server/src/utils.rs Tue Apr 09 21:08:35 2019 +0300
@@ -3,7 +3,8 @@
use std::iter::Iterator;
pub const PROTOCOL_VERSION: u32 = 3;
-pub const SERVER: mio::Token = mio::Token(1_000_000_000);
+pub const SERVER_TOKEN: mio::Token = mio::Token(1_000_000_000);
+pub const IO_TOKEN: mio::Token = mio::Token(1_000_000_001);
pub fn is_name_illegal(name: &str) -> bool {
name.len() > 40