shuffle server files
authoralfadur
Tue, 28 May 2019 19:04:18 +0300
changeset 15095 c5a6e8566425
parent 15094 7732013ce64c
child 15096 e935b1ad23f3
shuffle server files
rust/hedgewars-server/src/core.rs
rust/hedgewars-server/src/core/client.rs
rust/hedgewars-server/src/core/indexslab.rs
rust/hedgewars-server/src/core/room.rs
rust/hedgewars-server/src/core/server.rs
rust/hedgewars-server/src/core/types.rs
rust/hedgewars-server/src/handlers.rs
rust/hedgewars-server/src/handlers/actions.rs
rust/hedgewars-server/src/handlers/checker.rs
rust/hedgewars-server/src/handlers/common.rs
rust/hedgewars-server/src/handlers/inanteroom.rs
rust/hedgewars-server/src/handlers/inlobby.rs
rust/hedgewars-server/src/handlers/inroom.rs
rust/hedgewars-server/src/main.rs
rust/hedgewars-server/src/protocol.rs
rust/hedgewars-server/src/protocol/messages.rs
rust/hedgewars-server/src/protocol/parser.rs
rust/hedgewars-server/src/protocol/test.rs
rust/hedgewars-server/src/server.rs
rust/hedgewars-server/src/server/actions.rs
rust/hedgewars-server/src/server/client.rs
rust/hedgewars-server/src/server/core.rs
rust/hedgewars-server/src/server/coretypes.rs
rust/hedgewars-server/src/server/database.rs
rust/hedgewars-server/src/server/handlers.rs
rust/hedgewars-server/src/server/handlers/checker.rs
rust/hedgewars-server/src/server/handlers/common.rs
rust/hedgewars-server/src/server/handlers/inroom.rs
rust/hedgewars-server/src/server/handlers/lobby.rs
rust/hedgewars-server/src/server/handlers/loggingin.rs
rust/hedgewars-server/src/server/indexslab.rs
rust/hedgewars-server/src/server/io.rs
rust/hedgewars-server/src/server/network.rs
rust/hedgewars-server/src/server/room.rs
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rust/hedgewars-server/src/core.rs	Tue May 28 19:04:18 2019 +0300
@@ -0,0 +1,5 @@
+pub mod types;
+pub mod indexslab;
+pub mod client;
+pub mod room;
+pub mod server;
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rust/hedgewars-server/src/core/client.rs	Tue May 28 19:04:18 2019 +0300
@@ -0,0 +1,109 @@
+use super::types::ClientId;
+use bitflags::*;
+
+bitflags! {
+    pub struct ClientFlags: u16 {
+        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 NONE = 0b0000_0000;
+        const DEFAULT = Self::NONE.bits;
+    }
+}
+
+pub struct HWClient {
+    pub id: ClientId,
+    pub room_id: Option<usize>,
+    pub nick: String,
+    pub protocol_number: u16,
+    pub flags: ClientFlags,
+    pub teams_in_game: u8,
+    pub team_indices: Vec<u8>,
+    pub clan: Option<u8>,
+}
+
+impl HWClient {
+    pub fn new(id: ClientId, protocol_number: u16, nick: String) -> HWClient {
+        HWClient {
+            id,
+            nick,
+            protocol_number,
+            room_id: None,
+            flags: ClientFlags::DEFAULT,
+            teams_in_game: 0,
+            team_indices: Vec::new(),
+            clan: None,
+        }
+    }
+
+    fn contains(&self, mask: ClientFlags) -> bool {
+        self.flags.contains(mask)
+    }
+
+    fn set(&mut self, mask: ClientFlags, value: bool) {
+        self.flags.set(mask, value);
+    }
+
+    pub fn is_admin(&self) -> bool {
+        self.contains(ClientFlags::IS_ADMIN)
+    }
+    pub fn is_master(&self) -> bool {
+        self.contains(ClientFlags::IS_MASTER)
+    }
+    pub fn is_ready(&self) -> bool {
+        self.contains(ClientFlags::IS_READY)
+    }
+    pub fn is_in_game(&self) -> bool {
+        self.contains(ClientFlags::IS_IN_GAME)
+    }
+    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)
+    }
+    pub fn has_super_power(&self) -> bool {
+        self.contains(ClientFlags::HAS_SUPER_POWER)
+    }
+    pub fn is_registered(&self) -> bool {
+        self.contains(ClientFlags::IS_REGISTERED)
+    }
+
+    pub fn set_is_admin(&mut self, value: bool) {
+        self.set(ClientFlags::IS_ADMIN, value)
+    }
+    pub fn set_is_master(&mut self, value: bool) {
+        self.set(ClientFlags::IS_MASTER, value)
+    }
+    pub fn set_is_ready(&mut self, value: bool) {
+        self.set(ClientFlags::IS_READY, value)
+    }
+    pub fn set_is_in_game(&mut self, value: bool) {
+        self.set(ClientFlags::IS_IN_GAME, value)
+    }
+    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)
+    }
+    pub fn set_has_super_power(&mut self, value: bool) {
+        self.set(ClientFlags::HAS_SUPER_POWER, value)
+    }
+    pub fn set_is_registered(&mut self, value: bool) {
+        self.set(ClientFlags::IS_REGISTERED, value)
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rust/hedgewars-server/src/core/indexslab.rs	Tue May 28 19:04:18 2019 +0300
@@ -0,0 +1,71 @@
+use std::{
+    iter,
+    mem::replace,
+    ops::{Index, IndexMut},
+};
+
+pub struct IndexSlab<T> {
+    data: Vec<Option<T>>,
+}
+
+impl<T> IndexSlab<T> {
+    pub fn new() -> Self {
+        Self { data: Vec::new() }
+    }
+
+    pub fn with_capacity(capacity: usize) -> Self {
+        Self {
+            data: Vec::with_capacity(capacity),
+        }
+    }
+
+    pub fn insert(&mut self, index: usize, value: T) {
+        if index >= self.data.len() {
+            self.data.reserve(index - self.data.len() + 1);
+            self.data.extend((self.data.len()..index).map(|_| None));
+            self.data.push(Some(value))
+        } else {
+            self.data[index] = Some(value);
+        }
+    }
+
+    pub fn contains(&self, index: usize) -> bool {
+        self.data.get(index).and_then(|x| x.as_ref()).is_some()
+    }
+
+    pub fn remove(&mut self, index: usize) -> Option<T> {
+        if let Some(x) = self.data.get_mut(index) {
+            replace(x, None)
+        } else {
+            None
+        }
+    }
+
+    pub fn iter(&self) -> impl Iterator<Item = (usize, &T)> {
+        self.data
+            .iter()
+            .enumerate()
+            .filter_map(|(index, opt)| opt.as_ref().and_then(|x| Some((index, x))))
+    }
+
+    pub fn iter_mut(&mut self) -> impl Iterator<Item = (usize, &mut T)> {
+        self.data
+            .iter_mut()
+            .enumerate()
+            .filter_map(|(index, opt)| opt.as_mut().and_then(|x| Some((index, x))))
+    }
+}
+
+impl<T> Index<usize> for IndexSlab<T> {
+    type Output = T;
+
+    fn index(&self, index: usize) -> &Self::Output {
+        self.data[index].as_ref().unwrap()
+    }
+}
+
+impl<T> IndexMut<usize> for IndexSlab<T> {
+    fn index_mut(&mut self, index: usize) -> &mut Self::Output {
+        self.data[index].as_mut().unwrap()
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rust/hedgewars-server/src/core/room.rs	Tue May 28 19:04:18 2019 +0300
@@ -0,0 +1,349 @@
+use super::{
+    client::HWClient,
+    types::{
+        ClientId, GameCfg, GameCfg::*, RoomConfig, RoomId, TeamInfo, Voting, MAX_HEDGEHOGS_PER_TEAM,
+    },
+};
+use bitflags::*;
+use serde::{Deserialize, Serialize};
+use serde_derive::{Deserialize, Serialize};
+use serde_yaml;
+use std::{collections::HashMap, iter};
+
+pub const MAX_TEAMS_IN_ROOM: u8 = 8;
+pub const MAX_HEDGEHOGS_IN_ROOM: u8 = MAX_HEDGEHOGS_PER_TEAM * MAX_HEDGEHOGS_PER_TEAM;
+
+fn client_teams_impl(
+    teams: &[(ClientId, TeamInfo)],
+    client_id: ClientId,
+) -> impl Iterator<Item = &TeamInfo> + Clone {
+    teams
+        .iter()
+        .filter(move |(id, _)| *id == client_id)
+        .map(|(_, t)| t)
+}
+
+pub struct GameInfo {
+    pub teams_in_game: u8,
+    pub teams_at_start: Vec<(ClientId, TeamInfo)>,
+    pub left_teams: Vec<String>,
+    pub msg_log: Vec<String>,
+    pub sync_msg: Option<String>,
+    pub is_paused: bool,
+    config: RoomConfig,
+}
+
+impl GameInfo {
+    fn new(teams: Vec<(ClientId, TeamInfo)>, config: RoomConfig) -> GameInfo {
+        GameInfo {
+            left_teams: Vec::new(),
+            msg_log: Vec::new(),
+            sync_msg: None,
+            is_paused: false,
+            teams_in_game: teams.len() as u8,
+            teams_at_start: teams,
+            config,
+        }
+    }
+
+    pub fn client_teams(&self, client_id: ClientId) -> impl Iterator<Item = &TeamInfo> + Clone {
+        client_teams_impl(&self.teams_at_start, client_id)
+    }
+}
+
+#[derive(Serialize, Deserialize)]
+pub struct RoomSave {
+    pub location: String,
+    config: RoomConfig,
+}
+
+bitflags! {
+    pub struct RoomFlags: u8 {
+        const FIXED = 0b0000_0001;
+        const RESTRICTED_JOIN = 0b0000_0010;
+        const RESTRICTED_TEAM_ADD = 0b0000_0100;
+        const RESTRICTED_UNREGISTERED_PLAYERS = 0b0000_1000;
+    }
+}
+
+pub struct HWRoom {
+    pub id: RoomId,
+    pub master_id: Option<ClientId>,
+    pub name: String,
+    pub password: Option<String>,
+    pub greeting: String,
+    pub protocol_number: u16,
+    pub flags: RoomFlags,
+
+    pub players_number: u8,
+    pub default_hedgehog_number: u8,
+    pub max_teams: u8,
+    pub ready_players_number: u8,
+    pub teams: Vec<(ClientId, TeamInfo)>,
+    config: RoomConfig,
+    pub voting: Option<Voting>,
+    pub saves: HashMap<String, RoomSave>,
+    pub game_info: Option<GameInfo>,
+}
+
+impl HWRoom {
+    pub fn new(id: RoomId) -> HWRoom {
+        HWRoom {
+            id,
+            master_id: None,
+            name: String::new(),
+            password: None,
+            greeting: "".to_string(),
+            flags: RoomFlags::empty(),
+            protocol_number: 0,
+            players_number: 0,
+            default_hedgehog_number: 4,
+            max_teams: MAX_TEAMS_IN_ROOM,
+            ready_players_number: 0,
+            teams: Vec::new(),
+            config: RoomConfig::new(),
+            voting: None,
+            saves: HashMap::new(),
+            game_info: None,
+        }
+    }
+
+    pub fn hedgehogs_number(&self) -> u8 {
+        self.teams.iter().map(|(_, t)| t.hedgehogs_number).sum()
+    }
+
+    pub fn addable_hedgehogs(&self) -> u8 {
+        MAX_HEDGEHOGS_IN_ROOM - self.hedgehogs_number()
+    }
+
+    pub fn add_team(
+        &mut self,
+        owner_id: ClientId,
+        mut team: TeamInfo,
+        preserve_color: bool,
+    ) -> &TeamInfo {
+        if !preserve_color {
+            team.color = iter::repeat(())
+                .enumerate()
+                .map(|(i, _)| i as u8)
+                .take(u8::max_value() as usize + 1)
+                .find(|i| self.teams.iter().all(|(_, t)| t.color != *i))
+                .unwrap_or(0u8)
+        };
+        team.hedgehogs_number = if self.teams.is_empty() {
+            self.default_hedgehog_number
+        } else {
+            self.teams[0]
+                .1
+                .hedgehogs_number
+                .min(self.addable_hedgehogs())
+        };
+        self.teams.push((owner_id, team));
+        &self.teams.last().unwrap().1
+    }
+
+    pub fn remove_team(&mut self, name: &str) {
+        if let Some(index) = self.teams.iter().position(|(_, t)| t.name == name) {
+            self.teams.remove(index);
+        }
+    }
+
+    pub fn set_hedgehogs_number(&mut self, n: u8) -> Vec<String> {
+        let mut names = Vec::new();
+        let teams = match self.game_info {
+            Some(ref mut info) => &mut info.teams_at_start,
+            None => &mut self.teams,
+        };
+
+        if teams.len() as u8 * n <= MAX_HEDGEHOGS_IN_ROOM {
+            for (_, team) in teams.iter_mut() {
+                team.hedgehogs_number = n;
+                names.push(team.name.clone())
+            }
+            self.default_hedgehog_number = n;
+        }
+        names
+    }
+
+    pub fn find_team_and_owner_mut<F>(&mut self, f: F) -> Option<(ClientId, &mut TeamInfo)>
+    where
+        F: Fn(&TeamInfo) -> bool,
+    {
+        self.teams
+            .iter_mut()
+            .find(|(_, t)| f(t))
+            .map(|(id, t)| (*id, t))
+    }
+
+    pub fn find_team<F>(&self, f: F) -> Option<&TeamInfo>
+    where
+        F: Fn(&TeamInfo) -> bool,
+    {
+        self.teams
+            .iter()
+            .find_map(|(_, t)| Some(t).filter(|t| f(&t)))
+    }
+
+    pub fn client_teams(&self, client_id: ClientId) -> impl Iterator<Item = &TeamInfo> {
+        client_teams_impl(&self.teams, client_id)
+    }
+
+    pub fn client_team_indices(&self, client_id: ClientId) -> Vec<u8> {
+        self.teams
+            .iter()
+            .enumerate()
+            .filter(move |(_, (id, _))| *id == client_id)
+            .map(|(i, _)| i as u8)
+            .collect()
+    }
+
+    pub fn clan_team_owners(&self, color: u8) -> impl Iterator<Item = ClientId> + '_ {
+        self.teams
+            .iter()
+            .filter(move |(_, t)| t.color == color)
+            .map(|(id, _)| *id)
+    }
+
+    pub fn find_team_owner(&self, team_name: &str) -> Option<(ClientId, &str)> {
+        self.teams
+            .iter()
+            .find(|(_, t)| t.name == team_name)
+            .map(|(id, t)| (*id, &t.name[..]))
+    }
+
+    pub fn find_team_color(&self, owner_id: ClientId) -> Option<u8> {
+        self.client_teams(owner_id).nth(0).map(|t| t.color)
+    }
+
+    pub fn has_multiple_clans(&self) -> bool {
+        self.teams.iter().min_by_key(|(_, t)| t.color)
+            != self.teams.iter().max_by_key(|(_, t)| t.color)
+    }
+
+    pub fn set_config(&mut self, cfg: GameCfg) {
+        self.config.set_config(cfg);
+    }
+
+    pub fn start_round(&mut self) {
+        if self.game_info.is_none() {
+            self.game_info = Some(GameInfo::new(self.teams.clone(), self.config.clone()));
+        }
+    }
+
+    pub fn is_fixed(&self) -> bool {
+        self.flags.contains(RoomFlags::FIXED)
+    }
+    pub fn is_join_restricted(&self) -> bool {
+        self.flags.contains(RoomFlags::RESTRICTED_JOIN)
+    }
+    pub fn is_team_add_restricted(&self) -> bool {
+        self.flags.contains(RoomFlags::RESTRICTED_TEAM_ADD)
+    }
+    pub fn are_unregistered_players_restricted(&self) -> bool {
+        self.flags
+            .contains(RoomFlags::RESTRICTED_UNREGISTERED_PLAYERS)
+    }
+
+    pub fn set_is_fixed(&mut self, value: bool) {
+        self.flags.set(RoomFlags::FIXED, value)
+    }
+    pub fn set_join_restriction(&mut self, value: bool) {
+        self.flags.set(RoomFlags::RESTRICTED_JOIN, value)
+    }
+    pub fn set_team_add_restriction(&mut self, value: bool) {
+        self.flags.set(RoomFlags::RESTRICTED_TEAM_ADD, value)
+    }
+    pub fn set_unregistered_players_restriction(&mut self, value: bool) {
+        self.flags
+            .set(RoomFlags::RESTRICTED_UNREGISTERED_PLAYERS, value)
+    }
+
+    fn flags_string(&self) -> String {
+        let mut result = "-".to_string();
+        if self.game_info.is_some() {
+            result += "g"
+        }
+        if self.password.is_some() {
+            result += "p"
+        }
+        if self.is_join_restricted() {
+            result += "j"
+        }
+        if self.are_unregistered_players_restricted() {
+            result += "r"
+        }
+        result
+    }
+
+    pub fn info(&self, master: Option<&HWClient>) -> Vec<String> {
+        let c = &self.config;
+        vec![
+            self.flags_string(),
+            self.name.clone(),
+            self.players_number.to_string(),
+            self.teams.len().to_string(),
+            master.map_or("[]", |c| &c.nick).to_string(),
+            c.map_type.to_string(),
+            c.script.to_string(),
+            c.scheme.name.to_string(),
+            c.ammo.name.to_string(),
+        ]
+    }
+
+    pub fn active_config(&self) -> &RoomConfig {
+        match self.game_info {
+            Some(ref info) => &info.config,
+            None => &self.config,
+        }
+    }
+
+    pub fn map_config(&self) -> Vec<String> {
+        match self.game_info {
+            Some(ref info) => info.config.to_map_config(),
+            None => self.config.to_map_config(),
+        }
+    }
+
+    pub fn game_config(&self) -> Vec<GameCfg> {
+        match self.game_info {
+            Some(ref info) => info.config.to_game_config(),
+            None => self.config.to_game_config(),
+        }
+    }
+
+    pub fn save_config(&mut self, name: String, location: String) {
+        self.saves.insert(
+            name,
+            RoomSave {
+                location,
+                config: self.config.clone(),
+            },
+        );
+    }
+
+    pub fn load_config(&mut self, name: &str) -> Option<&str> {
+        if let Some(save) = self.saves.get(name) {
+            self.config = save.config.clone();
+            Some(&save.location[..])
+        } else {
+            None
+        }
+    }
+
+    pub fn delete_config(&mut self, name: &str) -> bool {
+        self.saves.remove(name).is_some()
+    }
+
+    pub fn get_saves(&self) -> Result<String, serde_yaml::Error> {
+        serde_yaml::to_string(&(&self.greeting, &self.saves))
+    }
+
+    pub fn set_saves(&mut self, text: &str) -> Result<(), serde_yaml::Error> {
+        serde_yaml::from_str::<(String, HashMap<String, RoomSave>)>(text).map(
+            |(greeting, saves)| {
+                self.greeting = greeting;
+                self.saves = saves;
+            },
+        )
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rust/hedgewars-server/src/core/server.rs	Tue May 28 19:04:18 2019 +0300
@@ -0,0 +1,285 @@
+use super::{
+    client::HWClient,
+    types::{ClientId, RoomId},
+    indexslab::IndexSlab,
+    room::HWRoom,
+};
+use crate::{
+    utils,
+    protocol::messages::HWProtocolMessage::Greeting
+};
+
+use bitflags::*;
+use log::*;
+use slab;
+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 is_checker: bool,
+}
+
+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,
+            is_checker: false,
+        };
+        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);
+        client
+    }
+}
+
+pub struct ServerGreetings {
+    pub for_latest_protocol: String,
+    pub for_old_protocols: String,
+}
+
+impl ServerGreetings {
+    fn new() -> Self {
+        Self {
+            for_latest_protocol: "\u{1f994} is watching".to_string(),
+            for_old_protocols: "\u{1f994} is watching".to_string(),
+        }
+    }
+}
+
+bitflags! {
+    pub struct ServerFlags: u8 {
+        const REGISTERED_ONLY = 0b0000_1000;
+    }
+}
+
+pub struct HWServer {
+    pub clients: IndexSlab<HWClient>,
+    pub rooms: Slab<HWRoom>,
+    pub anteroom: HWAnteroom,
+    pub latest_protocol: u16,
+    pub flags: ServerFlags,
+    pub greetings: ServerGreetings,
+}
+
+impl HWServer {
+    pub fn new(clients_limit: usize, rooms_limit: usize) -> Self {
+        let rooms = Slab::with_capacity(rooms_limit);
+        let clients = IndexSlab::with_capacity(clients_limit);
+        Self {
+            clients,
+            rooms,
+            anteroom: HWAnteroom::new(clients_limit),
+            greetings: ServerGreetings::new(),
+            latest_protocol: 58,
+            flags: ServerFlags::empty(),
+        }
+    }
+
+    pub fn add_client(&mut self, client_id: ClientId, data: HWAnteClient) {
+        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);
+            self.clients.insert(client_id, client);
+        }
+    }
+
+    pub fn remove_client(&mut self, client_id: ClientId) {
+        self.clients.remove(client_id);
+    }
+
+    pub fn get_greetings(&self, client_id: ClientId) -> &str {
+        if self.clients[client_id].protocol_number < self.latest_protocol {
+            &self.greetings.for_old_protocols
+        } else {
+            &self.greetings.for_latest_protocol
+        }
+    }
+
+    #[inline]
+    pub fn create_room(
+        &mut self,
+        creator_id: ClientId,
+        name: String,
+        password: Option<String>,
+    ) -> RoomId {
+        create_room(
+            &mut self.clients[creator_id],
+            &mut self.rooms,
+            name,
+            password,
+        )
+    }
+
+    #[inline]
+    pub fn move_to_room(&mut self, client_id: ClientId, room_id: RoomId) {
+        move_to_room(&mut self.clients[client_id], &mut self.rooms[room_id])
+    }
+
+    pub fn has_room(&self, name: &str) -> bool {
+        self.find_room(name).is_some()
+    }
+
+    pub fn find_room(&self, name: &str) -> Option<&HWRoom> {
+        self.rooms
+            .iter()
+            .find_map(|(_, r)| Some(r).filter(|r| r.name == name))
+    }
+
+    pub fn find_room_mut(&mut self, name: &str) -> Option<&mut HWRoom> {
+        self.rooms
+            .iter_mut()
+            .find_map(|(_, r)| Some(r).filter(|r| r.name == name))
+    }
+
+    pub fn find_client(&self, nick: &str) -> Option<&HWClient> {
+        self.clients
+            .iter()
+            .find_map(|(_, c)| Some(c).filter(|c| c.nick == nick))
+    }
+
+    pub fn find_client_mut(&mut self, nick: &str) -> Option<&mut HWClient> {
+        self.clients
+            .iter_mut()
+            .find_map(|(_, c)| Some(c).filter(|c| c.nick == nick))
+    }
+
+    pub fn all_clients(&self) -> impl Iterator<Item = ClientId> + '_ {
+        self.clients.iter().map(|(id, _)| id)
+    }
+
+    pub fn filter_clients<'a, F>(&'a self, f: F) -> impl Iterator<Item = ClientId> + 'a
+    where
+        F: Fn(&(usize, &HWClient)) -> bool + 'a,
+    {
+        self.clients.iter().filter(f).map(|(_, c)| c.id)
+    }
+
+    pub fn filter_rooms<'a, F>(&'a self, f: F) -> impl Iterator<Item = RoomId> + 'a
+    where
+        F: Fn(&(usize, &HWRoom)) -> bool + 'a,
+    {
+        self.rooms.iter().filter(f).map(|(_, c)| c.id)
+    }
+
+    pub fn collect_clients<F>(&self, f: F) -> Vec<ClientId>
+    where
+        F: Fn(&(usize, &HWClient)) -> bool,
+    {
+        self.filter_clients(f).collect()
+    }
+
+    pub fn collect_nicks<F>(&self, f: F) -> Vec<String>
+    where
+        F: Fn(&(usize, &HWClient)) -> bool,
+    {
+        self.clients
+            .iter()
+            .filter(f)
+            .map(|(_, c)| c.nick.clone())
+            .collect()
+    }
+
+    pub fn lobby_clients(&self) -> impl Iterator<Item = ClientId> + '_ {
+        self.filter_clients(|(_, c)| c.room_id == None)
+    }
+
+    pub fn room_clients(&self, room_id: RoomId) -> impl Iterator<Item = ClientId> + '_ {
+        self.filter_clients(move |(_, c)| c.room_id == Some(room_id))
+    }
+
+    pub fn protocol_clients(&self, protocol: u16) -> impl Iterator<Item = ClientId> + '_ {
+        self.filter_clients(move |(_, c)| c.protocol_number == protocol)
+    }
+
+    pub fn protocol_rooms(&self, protocol: u16) -> impl Iterator<Item = RoomId> + '_ {
+        self.filter_rooms(move |(_, r)| r.protocol_number == protocol)
+    }
+
+    pub fn other_clients_in_room(&self, self_id: ClientId) -> Vec<ClientId> {
+        let room_id = self.clients[self_id].room_id;
+        self.collect_clients(|(id, c)| *id != self_id && c.room_id == room_id)
+    }
+
+    pub fn is_registered_only(&self) -> bool {
+        self.flags.contains(ServerFlags::REGISTERED_ONLY)
+    }
+
+    pub fn set_is_registered_only(&mut self, value: bool) {
+        self.flags.set(ServerFlags::REGISTERED_ONLY, value)
+    }
+}
+
+fn allocate_room(rooms: &mut Slab<HWRoom>) -> &mut HWRoom {
+    let entry = rooms.vacant_entry();
+    let room = HWRoom::new(entry.key());
+    entry.insert(room)
+}
+
+fn create_room(
+    client: &mut HWClient,
+    rooms: &mut Slab<HWRoom>,
+    name: String,
+    password: Option<String>,
+) -> RoomId {
+    let room = allocate_room(rooms);
+
+    room.master_id = Some(client.id);
+    room.name = name;
+    room.password = password;
+    room.protocol_number = client.protocol_number;
+
+    room.players_number = 1;
+    room.ready_players_number = 1;
+
+    client.room_id = Some(room.id);
+    client.set_is_master(true);
+    client.set_is_ready(true);
+    client.set_is_joined_mid_game(false);
+
+    room.id
+}
+
+fn move_to_room(client: &mut HWClient, room: &mut HWRoom) {
+    debug_assert!(client.room_id != Some(room.id));
+
+    room.players_number += 1;
+
+    client.room_id = Some(room.id);
+    client.set_is_joined_mid_game(room.game_info.is_some());
+    client.set_is_in_game(room.game_info.is_some());
+
+    if let Some(ref mut info) = room.game_info {
+        let teams = info.client_teams(client.id);
+        client.teams_in_game = teams.clone().count() as u8;
+        client.clan = teams.clone().next().map(|t| t.color);
+        let team_names: Vec<_> = teams.map(|t| t.name.clone()).collect();
+
+        if !team_names.is_empty() {
+            info.left_teams.retain(|name| !team_names.contains(&name));
+            info.teams_in_game += team_names.len() as u8;
+            room.teams = info
+                .teams_at_start
+                .iter()
+                .filter(|(_, t)| !team_names.contains(&t.name))
+                .cloned()
+                .collect();
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rust/hedgewars-server/src/core/types.rs	Tue May 28 19:04:18 2019 +0300
@@ -0,0 +1,193 @@
+use serde_derive::{Deserialize, Serialize};
+
+pub type ClientId = usize;
+pub type RoomId = usize;
+
+pub const MAX_HEDGEHOGS_PER_TEAM: u8 = 8;
+
+#[derive(PartialEq, Eq, Clone, Debug)]
+pub enum ServerVar {
+    MOTDNew(String),
+    MOTDOld(String),
+    LatestProto(u16),
+}
+
+#[derive(PartialEq, Eq, Clone, Debug)]
+pub enum GameCfg {
+    FeatureSize(u32),
+    MapType(String),
+    MapGenerator(u32),
+    MazeSize(u32),
+    Seed(String),
+    Template(u32),
+
+    Ammo(String, Option<String>),
+    Scheme(String, Vec<String>),
+    Script(String),
+    Theme(String),
+    DrawnMap(String),
+}
+
+#[derive(PartialEq, Eq, Clone, Debug)]
+pub struct TeamInfo {
+    pub owner: String,
+    pub name: String,
+    pub color: u8,
+    pub grave: String,
+    pub fort: String,
+    pub voice_pack: String,
+    pub flag: String,
+    pub difficulty: u8,
+    pub hedgehogs_number: u8,
+    pub hedgehogs: [HedgehogInfo; MAX_HEDGEHOGS_PER_TEAM as usize],
+}
+
+#[derive(PartialEq, Eq, Clone, Debug)]
+pub struct HedgehogInfo {
+    pub name: String,
+    pub hat: String,
+}
+
+#[derive(Clone, Serialize, Deserialize)]
+pub struct Ammo {
+    pub name: String,
+    pub settings: Option<String>,
+}
+
+#[derive(Clone, Serialize, Deserialize)]
+pub struct Scheme {
+    pub name: String,
+    pub settings: Vec<String>,
+}
+
+#[derive(Clone, Serialize, Deserialize)]
+pub struct RoomConfig {
+    pub feature_size: u32,
+    pub map_type: String,
+    pub map_generator: u32,
+    pub maze_size: u32,
+    pub seed: String,
+    pub template: u32,
+
+    pub ammo: Ammo,
+    pub scheme: Scheme,
+    pub script: String,
+    pub theme: String,
+    pub drawn_map: Option<String>,
+}
+
+impl RoomConfig {
+    pub fn new() -> RoomConfig {
+        RoomConfig {
+            feature_size: 12,
+            map_type: "+rnd+".to_string(),
+            map_generator: 0,
+            maze_size: 0,
+            seed: "seed".to_string(),
+            template: 0,
+
+            ammo: Ammo {
+                name: "Default".to_string(),
+                settings: None,
+            },
+            scheme: Scheme {
+                name: "Default".to_string(),
+                settings: Vec::new(),
+            },
+            script: "Normal".to_string(),
+            theme: "\u{1f994}".to_string(),
+            drawn_map: None,
+        }
+    }
+
+    pub fn set_config(&mut self, cfg: GameCfg) {
+        match cfg {
+            GameCfg::FeatureSize(s) => self.feature_size = s,
+            GameCfg::MapType(t) => self.map_type = t,
+            GameCfg::MapGenerator(g) => self.map_generator = g,
+            GameCfg::MazeSize(s) => self.maze_size = s,
+            GameCfg::Seed(s) => self.seed = s,
+            GameCfg::Template(t) => self.template = t,
+
+            GameCfg::Ammo(n, s) => {
+                self.ammo = Ammo {
+                    name: n,
+                    settings: s,
+                }
+            }
+            GameCfg::Scheme(n, s) => {
+                self.scheme = Scheme {
+                    name: n,
+                    settings: s,
+                }
+            }
+            GameCfg::Script(s) => self.script = s,
+            GameCfg::Theme(t) => self.theme = t,
+            GameCfg::DrawnMap(m) => self.drawn_map = Some(m),
+        };
+    }
+
+    pub fn to_map_config(&self) -> Vec<String> {
+        vec![
+            self.feature_size.to_string(),
+            self.map_type.to_string(),
+            self.map_generator.to_string(),
+            self.maze_size.to_string(),
+            self.seed.to_string(),
+            self.template.to_string(),
+        ]
+    }
+
+    pub fn to_game_config(&self) -> Vec<GameCfg> {
+        use GameCfg::*;
+        let mut v = vec![
+            Ammo(self.ammo.name.to_string(), self.ammo.settings.clone()),
+            Scheme(self.scheme.name.to_string(), self.scheme.settings.clone()),
+            Script(self.script.to_string()),
+            Theme(self.theme.to_string()),
+        ];
+        if let Some(ref m) = self.drawn_map {
+            v.push(DrawnMap(m.to_string()))
+        }
+        v
+    }
+}
+
+pub struct Replay {
+    pub config: RoomConfig,
+    pub teams: Vec<TeamInfo>,
+    pub message_log: Vec<String>,
+}
+
+#[derive(PartialEq, Eq, Clone, Debug)]
+pub enum VoteType {
+    Kick(String),
+    Map(Option<String>),
+    Pause,
+    NewSeed,
+    HedgehogsPerTeam(u8),
+}
+
+pub struct Vote {
+    pub is_pro: bool,
+    pub is_forced: bool,
+}
+
+#[derive(Clone, Debug)]
+pub struct Voting {
+    pub ttl: u32,
+    pub voters: Vec<ClientId>,
+    pub votes: Vec<(ClientId, bool)>,
+    pub kind: VoteType,
+}
+
+impl Voting {
+    pub fn new(kind: VoteType, voters: Vec<ClientId>) -> Voting {
+        Voting {
+            kind,
+            voters,
+            ttl: 2,
+            votes: Vec::new(),
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rust/hedgewars-server/src/handlers.rs	Tue May 28 19:04:18 2019 +0300
@@ -0,0 +1,402 @@
+use mio;
+use std::{collections::HashMap, io, io::Write};
+
+use self::{
+    actions::{Destination, DestinationGroup, PendingMessage},
+    inanteroom::LoginResult
+};
+use crate::{
+    core::{
+        server::HWServer,
+        types::{ClientId, Replay, RoomId, GameCfg, TeamInfo},
+        room::RoomSave
+    },
+    protocol::messages::{
+        server_chat,
+        HWProtocolMessage,
+        HWServerMessage,
+        HWServerMessage::*,
+        global_chat,
+        HWProtocolMessage::EngineMessage
+    },
+    utils,
+};
+use base64::encode;
+use log::*;
+use rand::{thread_rng, RngCore};
+
+mod actions;
+mod checker;
+mod common;
+mod inroom;
+mod inlobby;
+mod inanteroom;
+
+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,
+    },
+    GetReplay {
+        id: u32,
+    },
+    SaveRoom {
+        room_id: RoomId,
+        filename: String,
+        contents: String,
+    },
+    LoadRoom {
+        room_id: RoomId,
+        filename: String,
+    },
+}
+
+pub enum IoResult {
+    Account(Option<AccountInfo>),
+    Replay(Option<Replay>),
+    SaveRoom(RoomId, bool),
+    LoadRoom(RoomId, Option<String>),
+}
+
+pub struct Response {
+    client_id: ClientId,
+    messages: Vec<PendingMessage>,
+    io_tasks: Vec<IoTask>,
+    removed_clients: Vec<ClientId>,
+}
+
+impl Response {
+    pub fn new(client_id: ClientId) -> Self {
+        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.io_tasks.is_empty()
+    }
+
+    #[inline]
+    pub fn len(&self) -> usize {
+        self.messages.len()
+    }
+
+    #[inline]
+    pub fn client_id(&self) -> ClientId {
+        self.client_id
+    }
+
+    #[inline]
+    pub fn add(&mut self, message: PendingMessage) {
+        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,
+    ) -> impl Iterator<Item = (Vec<ClientId>, HWServerMessage)> + 'a {
+        let client_id = self.client_id;
+        self.messages.drain(..).map(move |m| {
+            let ids = get_recipients(server, client_id, m.destination);
+            (ids, m.message)
+        })
+    }
+
+    pub fn remove_client(&mut self, client_id: ClientId) {
+        self.removed_clients.push(client_id);
+    }
+
+    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 {
+    fn extend<T: IntoIterator<Item = PendingMessage>>(&mut self, iter: T) {
+        for msg in iter {
+            self.add(msg)
+        }
+    }
+}
+
+fn get_recipients(
+    server: &HWServer,
+    client_id: ClientId,
+    destination: Destination,
+) -> Vec<ClientId> {
+    match destination {
+        Destination::ToSelf => vec![client_id],
+        Destination::ToId(id) => vec![id],
+        Destination::ToIds(ids) => ids,
+        Destination::ToAll { group, skip_self } => {
+            let mut ids: Vec<_> = match group {
+                DestinationGroup::All => server.all_clients().collect(),
+                DestinationGroup::Lobby => server.lobby_clients().collect(),
+                DestinationGroup::Protocol(proto) => server.protocol_clients(proto).collect(),
+                DestinationGroup::Room(id) => server.room_clients(id).collect(),
+            };
+
+            if skip_self {
+                if let Some(index) = ids.iter().position(|id| *id == client_id) {
+                    ids.remove(index);
+                }
+            }
+
+            ids
+        }
+    }
+}
+
+pub fn handle(
+    server: &mut HWServer,
+    client_id: ClientId,
+    response: &mut Response,
+    message: HWProtocolMessage,
+) {
+    match message {
+        HWProtocolMessage::Ping => response.add(Pong.send_self()),
+        _ => {
+            if server.anteroom.clients.contains(client_id) {
+                match inanteroom::handle(server, client_id, response, message) {
+                    LoginResult::Unchanged => (),
+                    LoginResult::Complete => {
+                        if let Some(client) = server.anteroom.remove_client(client_id) {
+                            server.add_client(client_id, client);
+                            common::join_lobby(server, response);
+                        }
+                    }
+                    LoginResult::Exit => {
+                        server.anteroom.remove_client(client_id);
+                        response.remove_client(client_id);
+                    }
+                }
+            } else if server.clients.contains(client_id) {
+                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());
+                    }
+                    HWProtocolMessage::Info(nick) => {
+                        if let Some(client) = server.find_client(&nick) {
+                            let admin_sign = if client.is_admin() { "@" } else { "" };
+                            let master_sign = if client.is_master() { "+" } else { "" };
+                            let room_info = match client.room_id {
+                                Some(room_id) => {
+                                    let room = &server.rooms[room_id];
+                                    let status = match room.game_info {
+                                        Some(_) if client.teams_in_game == 0 => "(spectating)",
+                                        Some(_) => "(playing)",
+                                        None => "",
+                                    };
+                                    format!(
+                                        "[{}{}room {}]{}",
+                                        admin_sign, master_sign, room.name, status
+                                    )
+                                }
+                                None => format!("[{}lobby]", admin_sign),
+                            };
+
+                            let info = vec![
+                                client.nick.clone(),
+                                "[]".to_string(),
+                                utils::protocol_version_string(client.protocol_number).to_string(),
+                                room_info,
+                            ];
+                            response.add(Info(info).send_self())
+                        } else {
+                            response
+                                .add(server_chat("Player is not online.".to_string()).send_self())
+                        }
+                    }
+                    HWProtocolMessage::ToggleServerRegisteredOnly => {
+                        if !server.clients[client_id].is_admin() {
+                            response.add(Warning("Access denied.".to_string()).send_self());
+                        } else {
+                            server.set_is_registered_only(server.is_registered_only());
+                            let msg = if server.is_registered_only() {
+                                "This server no longer allows unregistered players to join."
+                            } else {
+                                "This server now allows unregistered players to join."
+                            };
+                            response.add(server_chat(msg.to_string()).send_all());
+                        }
+                    }
+                    HWProtocolMessage::Global(msg) => {
+                        if !server.clients[client_id].is_admin() {
+                            response.add(Warning("Access denied.".to_string()).send_self());
+                        } else {
+                            response.add(global_chat(msg).send_all())
+                        }
+                    }
+                    HWProtocolMessage::SuperPower => {
+                        if !server.clients[client_id].is_admin() {
+                            response.add(Warning("Access denied.".to_string()).send_self());
+                        } else {
+                            server.clients[client_id].set_has_super_power(true);
+                            response
+                                .add(server_chat("Super power activated.".to_string()).send_self())
+                        }
+                    }
+                    HWProtocolMessage::Watch(id) => {
+                        #[cfg(feature = "official-server")]
+                        {
+                            response.request_io(IoTask::GetReplay { id })
+                        }
+
+                        #[cfg(not(feature = "official-server"))]
+                        {
+                            response.add(
+                                Warning("This server does not support replays!".to_string())
+                                    .send_self(),
+                            );
+                        }
+                    }
+                    _ => match server.clients[client_id].room_id {
+                        None => inlobby::handle(server, client_id, response, message),
+                        Some(room_id) => {
+                            inroom::handle(server, client_id, response, room_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::SERVER_VERSION).send_self());
+}
+
+pub fn handle_client_loss(server: &mut HWServer, client_id: ClientId, response: &mut Response) {
+    if server.anteroom.remove_client(client_id).is_none() {
+        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)) => {
+            if !info.is_registered && server.is_registered_only() {
+                response.add(
+                    Bye("This server only allows registered users to join.".to_string())
+                        .send_self(),
+                );
+                response.remove_client(client_id);
+            } else {
+                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_registered(info.is_registered);
+                    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);
+        }
+        IoResult::Replay(Some(replay)) => {
+            let protocol = server.clients[client_id].protocol_number;
+            let start_msg = if protocol < 58 {
+                RoomJoined(vec![server.clients[client_id].nick.clone()])
+            } else {
+                ReplayStart
+            };
+            response.add(start_msg.send_self());
+
+            common::get_room_config_impl(&replay.config, client_id, response);
+            common::get_teams(replay.teams.iter(), client_id, response);
+            response.add(RunGame.send_self());
+            response.add(ForwardEngineMessage(replay.message_log).send_self());
+
+            if protocol < 58 {
+                response.add(Kicked.send_self());
+            }
+        }
+        IoResult::Replay(None) => {
+            response.add(Warning("Could't load the replay".to_string()).send_self())
+        }
+        IoResult::SaveRoom(_, true) => {
+            response.add(server_chat("Room configs saved successfully.".to_string()).send_self());
+        }
+        IoResult::SaveRoom(_, false) => {
+            response.add(Warning("Unable to save the room configs.".to_string()).send_self());
+        }
+        IoResult::LoadRoom(room_id, Some(contents)) => {
+            if let Some(ref mut room) = server.rooms.get_mut(room_id) {
+                match room.set_saves(&contents) {
+                    Ok(_) => response.add(
+                        server_chat("Room configs loaded successfully.".to_string()).send_self(),
+                    ),
+                    Err(e) => {
+                        warn!("Error while deserializing the room configs: {}", e);
+                        response.add(
+                            Warning("Unable to deserialize the room configs.".to_string())
+                                .send_self(),
+                        );
+                    }
+                }
+            }
+        }
+        IoResult::LoadRoom(_, None) => {
+            response.add(Warning("Unable to load the room configs.".to_string()).send_self());
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rust/hedgewars-server/src/handlers/actions.rs	Tue May 28 19:04:18 2019 +0300
@@ -0,0 +1,115 @@
+use crate::{
+    core::{
+        client::HWClient,
+        server::HWServer,
+        types::{ClientId, GameCfg, RoomId, VoteType},
+        room::HWRoom,
+        room::{GameInfo, RoomFlags}
+    },
+    protocol::messages::{server_chat, HWProtocolMessage, HWServerMessage, HWServerMessage::*},
+    utils::to_engine_msg,
+};
+use rand::{distributions::Uniform, thread_rng, Rng};
+use std::{io, io::Write, iter::once, mem::replace};
+
+pub enum DestinationGroup {
+    All,
+    Lobby,
+    Room(RoomId),
+    Protocol(u16),
+}
+
+pub enum Destination {
+    ToId(ClientId),
+    ToIds(Vec<ClientId>),
+    ToSelf,
+    ToAll {
+        group: DestinationGroup,
+        skip_self: bool,
+    },
+}
+
+pub struct PendingMessage {
+    pub destination: Destination,
+    pub message: HWServerMessage,
+}
+
+impl PendingMessage {
+    pub fn send(message: HWServerMessage, client_id: ClientId) -> PendingMessage {
+        PendingMessage {
+            destination: Destination::ToId(client_id),
+            message,
+        }
+    }
+
+    pub fn send_many(message: HWServerMessage, client_ids: Vec<ClientId>) -> PendingMessage {
+        PendingMessage {
+            destination: Destination::ToIds(client_ids),
+            message,
+        }
+    }
+
+    pub fn send_self(message: HWServerMessage) -> PendingMessage {
+        PendingMessage {
+            destination: Destination::ToSelf,
+            message,
+        }
+    }
+
+    pub fn send_all(message: HWServerMessage) -> PendingMessage {
+        let destination = Destination::ToAll {
+            group: DestinationGroup::All,
+            skip_self: false,
+        };
+        PendingMessage {
+            destination,
+            message,
+        }
+    }
+
+    pub fn in_room(mut self, clients_room_id: RoomId) -> PendingMessage {
+        if let Destination::ToAll { ref mut group, .. } = self.destination {
+            *group = DestinationGroup::Room(clients_room_id)
+        }
+        self
+    }
+
+    pub fn in_lobby(mut self) -> PendingMessage {
+        if let Destination::ToAll { ref mut group, .. } = self.destination {
+            *group = DestinationGroup::Lobby
+        }
+        self
+    }
+
+    pub fn with_protocol(mut self, protocol_number: u16) -> PendingMessage {
+        if let Destination::ToAll { ref mut group, .. } = self.destination {
+            *group = DestinationGroup::Protocol(protocol_number)
+        }
+        self
+    }
+
+    pub fn but_self(mut self) -> PendingMessage {
+        if let Destination::ToAll {
+            ref mut skip_self, ..
+        } = self.destination
+        {
+            *skip_self = true
+        }
+        self
+    }
+}
+
+impl HWServerMessage {
+    pub fn send(self, client_id: ClientId) -> PendingMessage {
+        PendingMessage::send(self, client_id)
+    }
+    pub fn send_many(self, client_ids: Vec<ClientId>) -> PendingMessage {
+        PendingMessage::send_many(self, client_ids)
+    }
+    pub fn send_self(self) -> PendingMessage {
+        PendingMessage::send_self(self)
+    }
+    pub fn send_all(self) -> PendingMessage {
+        PendingMessage::send_all(self)
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rust/hedgewars-server/src/handlers/checker.rs	Tue May 28 19:04:18 2019 +0300
@@ -0,0 +1,13 @@
+use log::*;
+use mio;
+
+use crate::{
+    protocol::messages::HWProtocolMessage,
+    core::{server::HWServer, types::ClientId},
+};
+
+pub fn handle(_server: &mut HWServer, _client_id: ClientId, message: HWProtocolMessage) {
+    match message {
+        _ => warn!("Unknown command"),
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rust/hedgewars-server/src/handlers/common.rs	Tue May 28 19:04:18 2019 +0300
@@ -0,0 +1,661 @@
+use crate::{
+    protocol::messages::{
+        server_chat,
+        add_flags, remove_flags,
+        HWProtocolMessage::{self, Rnd},
+        HWServerMessage::{self, *},
+        ProtocolFlags as Flags,
+    },
+    core::{
+        client::HWClient,
+        server::HWServer,
+        types::{ClientId, GameCfg, RoomId, TeamInfo, Vote, VoteType},
+        room::HWRoom,
+    },
+    utils::to_engine_msg,
+};
+
+use super::Response;
+
+use crate::core::types::RoomConfig;
+use rand::{self, seq::SliceRandom, thread_rng, Rng};
+use std::{iter::once, mem::replace};
+
+pub fn rnd_reply(options: &[String]) -> HWServerMessage {
+    let mut rng = thread_rng();
+
+    let reply = if options.is_empty() {
+        (*&["heads", "tails"].choose(&mut rng).unwrap()).to_string()
+    } else {
+        options.choose(&mut rng).unwrap().clone()
+    };
+
+    ChatMsg {
+        nick: "[random]".to_string(),
+        msg: reply,
+    }
+}
+
+pub fn join_lobby(server: &mut HWServer, response: &mut Response) {
+    let client_id = response.client_id();
+
+    let client = &server.clients[client_id];
+    let nick = vec![client.nick.clone()];
+    let mut flags = vec![];
+    if client.is_registered() {
+        flags.push(Flags::Registered)
+    }
+    if client.is_admin() {
+        flags.push(Flags::Admin)
+    }
+    if client.is_contributor() {
+        flags.push(Flags::Contributor)
+    }
+
+    let all_nicks: Vec<_> = server.collect_nicks(|_| true);
+
+    let mut flag_selectors = [
+        (
+            Flags::Registered,
+            server.collect_nicks(|(_, c)| c.is_registered()),
+        ),
+        (Flags::Admin, server.collect_nicks(|(_, c)| c.is_admin())),
+        (
+            Flags::Contributor,
+            server.collect_nicks(|(_, c)| c.is_contributor()),
+        ),
+        (
+            Flags::InRoom,
+            server.collect_nicks(|(_, c)| c.room_id.is_some()),
+        ),
+    ];
+
+    let server_msg = ServerMessage(server.get_greetings(client_id).to_string());
+
+    let rooms_msg = Rooms(
+        server
+            .rooms
+            .iter()
+            .filter(|(_, r)| r.protocol_number == client.protocol_number)
+            .flat_map(|(_, r)| r.info(r.master_id.map(|id| &server.clients[id])))
+            .collect(),
+    );
+
+    response.add(LobbyJoined(nick).send_all().but_self());
+    response.add(
+        ClientFlags(add_flags(&flags), all_nicks.clone())
+            .send_all()
+            .but_self(),
+    );
+
+    response.add(LobbyJoined(all_nicks).send_self());
+    for (flag, nicks) in &mut flag_selectors {
+        if !nicks.is_empty() {
+            response.add(ClientFlags(add_flags(&[*flag]), replace(nicks, vec![])).send_self());
+        }
+    }
+
+    response.add(server_msg.send_self());
+    response.add(rooms_msg.send_self());
+}
+
+pub fn remove_teams(
+    room: &mut HWRoom,
+    team_names: Vec<String>,
+    is_in_game: bool,
+    response: &mut Response,
+) {
+    if let Some(ref mut info) = room.game_info {
+        for team_name in &team_names {
+            info.left_teams.push(team_name.clone());
+
+            if is_in_game {
+                let msg = once(b'F').chain(team_name.bytes());
+                response.add(
+                    ForwardEngineMessage(vec![to_engine_msg(msg)])
+                        .send_all()
+                        .in_room(room.id)
+                        .but_self(),
+                );
+
+                info.teams_in_game -= 1;
+
+                let remove_msg = to_engine_msg(once(b'F').chain(team_name.bytes()));
+                if let Some(m) = &info.sync_msg {
+                    info.msg_log.push(m.clone());
+                    info.sync_msg = None
+                }
+                info.msg_log.push(remove_msg.clone());
+
+                response.add(
+                    ForwardEngineMessage(vec![remove_msg])
+                        .send_all()
+                        .in_room(room.id)
+                        .but_self(),
+                );
+            }
+        }
+    }
+
+    for team_name in team_names {
+        room.remove_team(&team_name);
+        response.add(TeamRemove(team_name).send_all().in_room(room.id));
+    }
+}
+
+fn remove_client_from_room(
+    client: &mut HWClient,
+    room: &mut HWRoom,
+    response: &mut Response,
+    msg: &str,
+) {
+    room.players_number -= 1;
+    if room.players_number > 0 || room.is_fixed() {
+        if client.is_ready() && room.ready_players_number > 0 {
+            room.ready_players_number -= 1;
+        }
+
+        let team_names: Vec<_> = room
+            .client_teams(client.id)
+            .map(|t| t.name.clone())
+            .collect();
+        remove_teams(room, team_names, client.is_in_game(), response);
+
+        if room.players_number > 0 {
+            response.add(
+                RoomLeft(client.nick.clone(), msg.to_string())
+                    .send_all()
+                    .in_room(room.id)
+                    .but_self(),
+            );
+        }
+
+        if client.is_master() && !room.is_fixed() {
+            client.set_is_master(false);
+            response.add(
+                ClientFlags(
+                    remove_flags(&[Flags::RoomMaster]),
+                    vec![client.nick.clone()],
+                )
+                .send_all()
+                .in_room(room.id),
+            );
+            room.master_id = None;
+        }
+    }
+
+    client.room_id = None;
+
+    let update_msg = if room.players_number == 0 && !room.is_fixed() {
+        RoomRemove(room.name.clone())
+    } else {
+        RoomUpdated(room.name.clone(), room.info(Some(&client)))
+    };
+    response.add(update_msg.send_all().with_protocol(room.protocol_number));
+
+    response.add(ClientFlags(remove_flags(&[Flags::InRoom]), vec![client.nick.clone()]).send_all());
+}
+
+pub fn change_master(
+    server: &mut HWServer,
+    room_id: RoomId,
+    new_master_id: ClientId,
+    response: &mut Response,
+) {
+    let room = &mut server.rooms[room_id];
+    if let Some(master_id) = room.master_id {
+        server.clients[master_id].set_is_master(false);
+        response.add(
+            ClientFlags(
+                remove_flags(&[Flags::RoomMaster]),
+                vec![server.clients[master_id].nick.clone()],
+            )
+            .send_all()
+            .in_room(room_id),
+        )
+    }
+
+    room.master_id = Some(new_master_id);
+    server.clients[new_master_id].set_is_master(true);
+
+    response.add(
+        ClientFlags(
+            add_flags(&[Flags::RoomMaster]),
+            vec![server.clients[new_master_id].nick.clone()],
+        )
+        .send_all()
+        .in_room(room_id),
+    );
+}
+
+pub fn enter_room(
+    server: &mut HWServer,
+    client_id: ClientId,
+    room_id: RoomId,
+    response: &mut Response,
+) {
+    let nick = server.clients[client_id].nick.clone();
+    server.move_to_room(client_id, room_id);
+
+    response.add(RoomJoined(vec![nick.clone()]).send_all().in_room(room_id));
+    response.add(ClientFlags(add_flags(&[Flags::InRoom]), vec![nick]).send_all());
+    let nicks = server.collect_nicks(|(_, c)| c.room_id == Some(room_id));
+    response.add(RoomJoined(nicks).send_self());
+
+    get_room_teams(server, room_id, client_id, response);
+
+    let room = &server.rooms[room_id];
+    get_room_config(room, client_id, response);
+
+    let mut flag_selectors = [
+        (
+            Flags::RoomMaster,
+            server.collect_nicks(|(_, c)| c.is_master()),
+        ),
+        (Flags::Ready, server.collect_nicks(|(_, c)| c.is_ready())),
+        (Flags::InGame, server.collect_nicks(|(_, c)| c.is_in_game())),
+    ];
+
+    for (flag, nicks) in &mut flag_selectors {
+        response.add(ClientFlags(add_flags(&[*flag]), replace(nicks, vec![])).send_self());
+    }
+
+    if !room.greeting.is_empty() {
+        response.add(
+            ChatMsg {
+                nick: "[greeting]".to_string(),
+                msg: room.greeting.clone(),
+            }
+            .send_self(),
+        );
+    }
+}
+
+pub fn exit_room(server: &mut HWServer, client_id: ClientId, response: &mut Response, msg: &str) {
+    let client = &mut server.clients[client_id];
+
+    if let Some(room_id) = client.room_id {
+        let room = &mut server.rooms[room_id];
+
+        remove_client_from_room(client, room, response, msg);
+
+        if !room.is_fixed() {
+            if room.players_number == 0 {
+                server.rooms.remove(room_id);
+            } else if room.master_id == None {
+                let new_master_id = server.room_clients(room_id).next();
+                if let Some(new_master_id) = new_master_id {
+                    let new_master_nick = server.clients[new_master_id].nick.clone();
+                    let room = &mut server.rooms[room_id];
+                    room.master_id = Some(new_master_id);
+                    server.clients[new_master_id].set_is_master(true);
+
+                    if room.protocol_number < 42 {
+                        room.name = new_master_nick.clone();
+                    }
+
+                    room.set_join_restriction(false);
+                    room.set_team_add_restriction(false);
+                    room.set_unregistered_players_restriction(true);
+
+                    response.add(
+                        ClientFlags(add_flags(&[Flags::RoomMaster]), vec![new_master_nick])
+                            .send_all()
+                            .in_room(room.id),
+                    );
+                }
+            }
+        }
+    }
+}
+
+pub fn remove_client(server: &mut HWServer, response: &mut Response, msg: String) {
+    let client_id = response.client_id();
+    let client = &mut server.clients[client_id];
+    let nick = client.nick.clone();
+
+    exit_room(server, client_id, response, &msg);
+
+    server.remove_client(client_id);
+
+    response.add(LobbyLeft(nick, msg.to_string()).send_all());
+    response.add(Bye("User quit: ".to_string() + &msg).send_self());
+    response.remove_client(client_id);
+}
+
+pub fn get_room_update(
+    room_name: Option<String>,
+    room: &HWRoom,
+    master: Option<&HWClient>,
+    response: &mut Response,
+) {
+    let update_msg = RoomUpdated(room_name.unwrap_or(room.name.clone()), room.info(master));
+    response.add(update_msg.send_all().with_protocol(room.protocol_number));
+}
+
+pub fn get_room_config_impl(config: &RoomConfig, to_client: ClientId, response: &mut Response) {
+    response.add(ConfigEntry("FULLMAPCONFIG".to_string(), config.to_map_config()).send(to_client));
+    for cfg in config.to_game_config() {
+        response.add(cfg.to_server_msg().send(to_client));
+    }
+}
+
+pub fn get_room_config(room: &HWRoom, to_client: ClientId, response: &mut Response) {
+    get_room_config_impl(room.active_config(), to_client, response);
+}
+
+pub fn get_teams<'a, I>(teams: I, to_client: ClientId, response: &mut Response)
+where
+    I: Iterator<Item = &'a TeamInfo>,
+{
+    for team in teams {
+        response.add(TeamAdd(team.to_protocol()).send(to_client));
+        response.add(TeamColor(team.name.clone(), team.color).send(to_client));
+        response.add(HedgehogsNumber(team.name.clone(), team.hedgehogs_number).send(to_client));
+    }
+}
+
+pub fn get_room_teams(
+    server: &HWServer,
+    room_id: RoomId,
+    to_client: ClientId,
+    response: &mut Response,
+) {
+    let room = &server.rooms[room_id];
+    let current_teams = match room.game_info {
+        Some(ref info) => &info.teams_at_start,
+        None => &room.teams,
+    };
+
+    get_teams(current_teams.iter().map(|(_, t)| t), to_client, response);
+}
+
+pub fn get_room_flags(
+    server: &HWServer,
+    room_id: RoomId,
+    to_client: ClientId,
+    response: &mut Response,
+) {
+    let room = &server.rooms[room_id];
+    if let Some(id) = room.master_id {
+        response.add(
+            ClientFlags(
+                add_flags(&[Flags::RoomMaster]),
+                vec![server.clients[id].nick.clone()],
+            )
+            .send(to_client),
+        );
+    }
+    let nicks: Vec<_> = server
+        .clients
+        .iter()
+        .filter(|(_, c)| c.room_id == Some(room_id) && c.is_ready())
+        .map(|(_, c)| c.nick.clone())
+        .collect();
+    if !nicks.is_empty() {
+        response.add(ClientFlags(add_flags(&[Flags::Ready]), nicks).send(to_client));
+    }
+}
+
+pub fn apply_voting_result(
+    server: &mut HWServer,
+    room_id: RoomId,
+    response: &mut Response,
+    kind: VoteType,
+) {
+    match kind {
+        VoteType::Kick(nick) => {
+            if let Some(client) = server.find_client(&nick) {
+                if client.room_id == Some(room_id) {
+                    let id = client.id;
+                    response.add(Kicked.send(id));
+                    exit_room(server, id, response, "kicked");
+                }
+            }
+        }
+        VoteType::Map(None) => (),
+        VoteType::Map(Some(name)) => {
+            if let Some(location) = server.rooms[room_id].load_config(&name) {
+                response.add(
+                    server_chat(location.to_string())
+                        .send_all()
+                        .in_room(room_id),
+                );
+                let room = &server.rooms[room_id];
+                let room_master = if let Some(id) = room.master_id {
+                    Some(&server.clients[id])
+                } else {
+                    None
+                };
+                get_room_update(None, room, room_master, response);
+
+                for (_, client) in server.clients.iter() {
+                    if client.room_id == Some(room_id) {
+                        super::common::get_room_config(&server.rooms[room_id], client.id, response);
+                    }
+                }
+            }
+        }
+        VoteType::Pause => {
+            if let Some(ref mut info) = server.rooms[room_id].game_info {
+                info.is_paused = !info.is_paused;
+                response.add(
+                    server_chat("Pause toggled.".to_string())
+                        .send_all()
+                        .in_room(room_id),
+                );
+                response.add(
+                    ForwardEngineMessage(vec![to_engine_msg(once(b'I'))])
+                        .send_all()
+                        .in_room(room_id),
+                );
+            }
+        }
+        VoteType::NewSeed => {
+            let seed = thread_rng().gen_range(0, 1_000_000_000).to_string();
+            let cfg = GameCfg::Seed(seed);
+            response.add(cfg.to_server_msg().send_all().in_room(room_id));
+            server.rooms[room_id].set_config(cfg);
+        }
+        VoteType::HedgehogsPerTeam(number) => {
+            let r = &mut server.rooms[room_id];
+            let nicks = r.set_hedgehogs_number(number);
+
+            response.extend(
+                nicks
+                    .into_iter()
+                    .map(|n| HedgehogsNumber(n, number).send_all().in_room(room_id)),
+            );
+        }
+    }
+}
+
+fn add_vote(room: &mut HWRoom, response: &mut Response, vote: Vote) -> Option<bool> {
+    let client_id = response.client_id;
+    let mut result = None;
+
+    if let Some(ref mut voting) = room.voting {
+        if vote.is_forced || voting.votes.iter().all(|(id, _)| client_id != *id) {
+            response.add(server_chat("Your vote has been counted.".to_string()).send_self());
+            voting.votes.push((client_id, vote.is_pro));
+            let i = voting.votes.iter();
+            let pro = i.clone().filter(|(_, v)| *v).count();
+            let contra = i.filter(|(_, v)| !*v).count();
+            let success_quota = voting.voters.len() / 2 + 1;
+            if vote.is_forced && vote.is_pro || pro >= success_quota {
+                result = Some(true);
+            } else if vote.is_forced && !vote.is_pro || contra > voting.voters.len() - success_quota
+            {
+                result = Some(false);
+            }
+        } else {
+            response.add(server_chat("You already have voted.".to_string()).send_self());
+        }
+    } else {
+        response.add(server_chat("There's no voting going on.".to_string()).send_self());
+    }
+
+    result
+}
+
+pub fn submit_vote(server: &mut HWServer, vote: Vote, response: &mut Response) {
+    let client_id = response.client_id;
+    let client = &server.clients[client_id];
+
+    if let Some(room_id) = client.room_id {
+        let room = &mut server.rooms[room_id];
+
+        if let Some(res) = add_vote(room, response, vote) {
+            response.add(
+                server_chat("Voting closed.".to_string())
+                    .send_all()
+                    .in_room(room.id),
+            );
+            let voting = replace(&mut room.voting, None).unwrap();
+            if res {
+                apply_voting_result(server, room_id, response, voting.kind);
+            }
+        }
+    }
+}
+
+pub fn start_game(server: &mut HWServer, room_id: RoomId, response: &mut Response) {
+    let (room_clients, room_nicks): (Vec<_>, Vec<_>) = server
+        .clients
+        .iter()
+        .map(|(id, c)| (id, c.nick.clone()))
+        .unzip();
+    let room = &mut server.rooms[room_id];
+
+    if !room.has_multiple_clans() {
+        response.add(
+            Warning("The game can't be started with less than two clans!".to_string()).send_self(),
+        );
+    } else if room.protocol_number <= 43 && room.players_number != room.ready_players_number {
+        response.add(Warning("Not all players are ready".to_string()).send_self());
+    } else if room.game_info.is_some() {
+        response.add(Warning("The game is already in progress".to_string()).send_self());
+    } else {
+        room.start_round();
+        for id in room_clients {
+            let c = &mut server.clients[id];
+            c.set_is_in_game(true);
+            c.team_indices = room.client_team_indices(c.id);
+        }
+        response.add(RunGame.send_all().in_room(room.id));
+        response.add(
+            ClientFlags(add_flags(&[Flags::InGame]), room_nicks)
+                .send_all()
+                .in_room(room.id),
+        );
+
+        let room_master = if let Some(id) = room.master_id {
+            Some(&server.clients[id])
+        } else {
+            None
+        };
+        get_room_update(None, room, room_master, response);
+    }
+}
+
+pub fn end_game(server: &mut HWServer, room_id: RoomId, response: &mut Response) {
+    let room = &mut server.rooms[room_id];
+    room.ready_players_number = 1;
+    let room_master = if let Some(id) = room.master_id {
+        Some(&server.clients[id])
+    } else {
+        None
+    };
+    get_room_update(None, room, room_master, response);
+    response.add(RoundFinished.send_all().in_room(room_id));
+
+    if let Some(info) = replace(&mut room.game_info, None) {
+        for (_, client) in server.clients.iter() {
+            if client.room_id == Some(room_id) && client.is_joined_mid_game() {
+                super::common::get_room_config(room, client.id, response);
+                response.extend(
+                    info.left_teams
+                        .iter()
+                        .map(|name| TeamRemove(name.clone()).send(client.id)),
+                );
+            }
+        }
+    }
+
+    let nicks: Vec<_> = server
+        .clients
+        .iter_mut()
+        .filter(|(_, c)| c.room_id == Some(room_id))
+        .map(|(_, c)| {
+            c.set_is_ready(c.is_master());
+            c.set_is_joined_mid_game(false);
+            c
+        })
+        .filter_map(|c| {
+            if !c.is_master() {
+                Some(c.nick.clone())
+            } else {
+                None
+            }
+        })
+        .collect();
+
+    if !nicks.is_empty() {
+        let msg = if room.protocol_number < 38 {
+            LegacyReady(false, nicks)
+        } else {
+            ClientFlags(remove_flags(&[Flags::Ready]), nicks)
+        };
+        response.add(msg.send_all().in_room(room_id));
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use crate::protocol::messages::HWServerMessage::ChatMsg;
+    use crate::server::actions::PendingMessage;
+
+    fn reply2string(r: HWServerMessage) -> String {
+        match r {
+            ChatMsg { msg: p, .. } => String::from(p),
+            _ => panic!("expected a ChatMsg"),
+        }
+    }
+
+    fn run_handle_test(opts: Vec<String>) {
+        let opts2 = opts.clone();
+        for opt in opts {
+            while reply2string(rnd_reply(&opts2)) != opt {}
+        }
+    }
+
+    /// This test terminates almost surely.
+    #[test]
+    fn test_handle_rnd_empty() {
+        run_handle_test(vec![])
+    }
+
+    /// This test terminates almost surely.
+    #[test]
+    fn test_handle_rnd_nonempty() {
+        run_handle_test(vec!["A".to_owned(), "B".to_owned(), "C".to_owned()])
+    }
+
+    /// This test terminates almost surely (strong law of large numbers)
+    #[test]
+    fn test_distribution() {
+        let eps = 0.000001;
+        let lim = 0.5;
+        let opts = vec![0.to_string(), 1.to_string()];
+        let mut ones = 0;
+        let mut tries = 0;
+
+        while tries < 1000 || ((ones as f64 / tries as f64) - lim).abs() >= eps {
+            tries += 1;
+            if reply2string(rnd_reply(&opts)) == 1.to_string() {
+                ones += 1;
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rust/hedgewars-server/src/handlers/inanteroom.rs	Tue May 28 19:04:18 2019 +0300
@@ -0,0 +1,148 @@
+use mio;
+
+use crate::{
+    protocol::messages::{
+        HWProtocolMessage::LoadRoom,
+        HWProtocolMessage,
+        HWServerMessage::*},
+    core::{
+        client::HWClient,
+        server::{HWServer, HWAnteClient, HWAnteroom},
+        types::ClientId
+    },
+    utils::is_name_illegal
+};
+
+use log::*;
+#[cfg(feature = "official-server")]
+use openssl::sha::sha1;
+use std::{
+    fmt::{Formatter, LowerHex},
+    num::NonZeroU16,
+};
+
+pub enum LoginResult {
+    Unchanged,
+    Complete,
+    Exit,
+}
+
+fn completion_result<'a, I>(
+    mut other_clients: I,
+    client: &mut HWAnteClient,
+    response: &mut super::Response,
+) -> LoginResult
+where
+    I: Iterator<Item = (ClientId, &'a HWClient)>,
+{
+    let has_nick_clash =
+        other_clients.any(|(_, c)| !c.is_checker() && 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
+        }
+    } else {
+        #[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(
+    server: &mut HWServer,
+    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.anteroom.clients[client_id];
+
+            if client.nick.is_some() {
+                response.add(Error("Nickname already provided.".to_string()).send_self());
+                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());
+                LoginResult::Exit
+            } else {
+                client.nick = Some(nick.clone());
+                response.add(Nick(nick).send_self());
+
+                if client.protocol_number.is_some() {
+                    completion_result(server.clients.iter(), client, response)
+                } else {
+                    LoginResult::Unchanged
+                }
+            }
+        }
+        HWProtocolMessage::Proto(proto) => {
+            let client = &mut server.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 = NonZeroU16::new(proto);
+                response.add(Proto(proto).send_self());
+
+                if client.nick.is_some() {
+                    completion_result(server.clients.iter(), client, response)
+                } else {
+                    LoginResult::Unchanged
+                }
+            }
+        }
+        #[cfg(feature = "official-server")]
+        HWProtocolMessage::Password(hash, salt) => {
+            let client = &server.anteroom.clients[client_id];
+
+            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 server.anteroom.clients[client_id];
+            if protocol == 0 {
+                response.add(Error("Bad number.".to_string()).send_self());
+                LoginResult::Unchanged
+            } else {
+                client.protocol_number = NonZeroU16::new(protocol);
+                client.nick = Some(nick);
+                client.is_checker = true;
+                LoginResult::Complete
+            }
+        }
+        _ => {
+            warn!("Incorrect command in logging-in state");
+            LoginResult::Unchanged
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rust/hedgewars-server/src/handlers/inlobby.rs	Tue May 28 19:04:18 2019 +0300
@@ -0,0 +1,164 @@
+use mio;
+
+use super::common::rnd_reply;
+use crate::{
+    protocol::messages::{
+        add_flags, remove_flags, server_chat, HWProtocolMessage, HWServerMessage::*,
+        ProtocolFlags as Flags,
+    },
+    core::{
+        client::HWClient,
+        server::HWServer,
+        types::{ClientId, ServerVar},
+    },
+    utils::is_name_illegal,
+};
+use log::*;
+use std::{collections::HashSet, convert::identity};
+
+pub fn handle(
+    server: &mut HWServer,
+    client_id: ClientId,
+    response: &mut super::Response,
+    message: HWProtocolMessage,
+) {
+    use crate::protocol::messages::HWProtocolMessage::*;
+    match message {
+        CreateRoom(name, password) => {
+            if is_name_illegal(&name) {
+                response.add(Warning("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: $()*+?[]^{|}".to_string()).send_self());
+            } else if server.has_room(&name) {
+                response.add(
+                    Warning("A room with the same name already exists.".to_string()).send_self(),
+                );
+            } else {
+                let flags_msg = ClientFlags(
+                    add_flags(&[Flags::RoomMaster, Flags::Ready]),
+                    vec![server.clients[client_id].nick.clone()],
+                );
+
+                let room_id = server.create_room(client_id, name, password);
+                let room = &server.rooms[room_id];
+                let client = &server.clients[client_id];
+
+                response.add(
+                    RoomAdd(room.info(Some(&client)))
+                        .send_all()
+                        .with_protocol(room.protocol_number),
+                );
+                response.add(RoomJoined(vec![client.nick.clone()]).send_self());
+                response.add(flags_msg.send_self());
+
+                response.add(
+                    ClientFlags(add_flags(&[Flags::InRoom]), vec![client.nick.clone()]).send_self(),
+                );
+            };
+        }
+        Chat(msg) => {
+            response.add(
+                ChatMsg {
+                    nick: server.clients[client_id].nick.clone(),
+                    msg,
+                }
+                .send_all()
+                .in_lobby()
+                .but_self(),
+            );
+        }
+        JoinRoom(name, _password) => {
+            let room = server.rooms.iter().find(|(_, r)| r.name == name);
+            let room_id = room.map(|(_, r)| r.id);
+
+            let client = &mut server.clients[client_id];
+
+            if let Some((_, room)) = room {
+                if client.protocol_number != room.protocol_number {
+                    response.add(
+                        Warning("Room version incompatible to your Hedgewars version!".to_string())
+                            .send_self(),
+                    );
+                } else if room.is_join_restricted() {
+                    response.add(
+                        Warning(
+                            "Access denied. This room currently doesn't allow joining.".to_string(),
+                        )
+                        .send_self(),
+                    );
+                } else if room.players_number == u8::max_value() {
+                    response.add(Warning("This room is already full".to_string()).send_self());
+                } else if let Some(room_id) = room_id {
+                    super::common::enter_room(server, client_id, room_id, response);
+                }
+            } else {
+                response.add(Warning("No such room.".to_string()).send_self());
+            }
+        }
+        Follow(nick) => {
+            if let Some(HWClient {
+                room_id: Some(room_id),
+                ..
+            }) = server.find_client(&nick)
+            {
+                let room = &server.rooms[*room_id];
+                response.add(Joining(room.name.clone()).send_self());
+                super::common::enter_room(server, client_id, *room_id, response);
+            }
+        }
+        SetServerVar(var) => {
+            if !server.clients[client_id].is_admin() {
+                response.add(Warning("Access denied.".to_string()).send_self());
+            } else {
+                match var {
+                    ServerVar::MOTDNew(msg) => server.greetings.for_latest_protocol = msg,
+                    ServerVar::MOTDOld(msg) => server.greetings.for_old_protocols = msg,
+                    ServerVar::LatestProto(n) => server.latest_protocol = n,
+                }
+            }
+        }
+        GetServerVar => {
+            if !server.clients[client_id].is_admin() {
+                response.add(Warning("Access denied.".to_string()).send_self());
+            } else {
+                let vars: Vec<_> = [
+                    ServerVar::MOTDNew(server.greetings.for_latest_protocol.clone()),
+                    ServerVar::MOTDOld(server.greetings.for_old_protocols.clone()),
+                    ServerVar::LatestProto(server.latest_protocol),
+                ]
+                .iter()
+                .flat_map(|v| v.to_protocol())
+                .collect();
+                response.add(ServerVars(vars).send_self());
+            }
+        }
+        Rnd(v) => {
+            response.add(rnd_reply(&v).send_self());
+        }
+        Stats => {
+            let mut protocols: HashSet<_> = server
+                .clients
+                .iter()
+                .map(|(_, c)| c.protocol_number)
+                .chain(server.rooms.iter().map(|(_, r)| r.protocol_number))
+                .collect();
+            let mut protocols: Vec<_> = protocols.drain().collect();
+            protocols.sort();
+
+            let mut html = Vec::with_capacity(protocols.len() + 2);
+
+            html.push("<table>".to_string());
+            for protocol in protocols {
+                html.push(format!(
+                    "<tr><td>{}</td><td>{}</td><td>{}</td></tr>",
+                    super::utils::protocol_version_string(protocol),
+                    server.protocol_clients(protocol).count(),
+                    server.protocol_rooms(protocol).count()
+                ));
+            }
+            html.push("</table>".to_string());
+
+            response.add(Warning(html.join("")).send_self());
+        }
+        List => warn!("Deprecated LIST message received"),
+        _ => warn!("Incorrect command in lobby state"),
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rust/hedgewars-server/src/handlers/inroom.rs	Tue May 28 19:04:18 2019 +0300
@@ -0,0 +1,615 @@
+use mio;
+
+use super::common::rnd_reply;
+use crate::utils::to_engine_msg;
+use crate::{
+    protocol::messages::{
+        add_flags, remove_flags, server_chat, HWProtocolMessage, HWServerMessage::*,
+        ProtocolFlags as Flags,
+    },
+    core::{
+        server::HWServer,
+        types,
+        types::{ClientId, GameCfg, RoomId, VoteType, Voting, MAX_HEDGEHOGS_PER_TEAM},
+        room::{HWRoom, RoomFlags, MAX_TEAMS_IN_ROOM},
+    },
+    utils::is_name_illegal,
+};
+use base64::{decode, encode};
+use log::*;
+use std::{cmp::min, iter::once, mem::swap};
+
+#[derive(Clone)]
+struct ByMsg<'a> {
+    messages: &'a [u8],
+}
+
+impl<'a> Iterator for ByMsg<'a> {
+    type Item = &'a [u8];
+
+    fn next(&mut self) -> Option<<Self as Iterator>::Item> {
+        if let Some(size) = self.messages.get(0) {
+            let (msg, next) = self.messages.split_at(*size as usize + 1);
+            self.messages = next;
+            Some(msg)
+        } else {
+            None
+        }
+    }
+}
+
+fn by_msg(source: &[u8]) -> ByMsg {
+    ByMsg { messages: source }
+}
+
+const VALID_MESSAGES: &[u8] =
+    b"M#+LlRrUuDdZzAaSjJ,NpPwtgfhbc12345\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8A";
+const NON_TIMED_MESSAGES: &[u8] = b"M#hb";
+
+#[cfg(canhazslicepatterns)]
+fn is_msg_valid(msg: &[u8], team_indices: &[u8]) -> bool {
+    match msg {
+        [size, typ, body..] => {
+            VALID_MESSAGES.contains(typ)
+                && match body {
+                    [1...MAX_HEDGEHOGS_PER_TEAM, team, ..] if *typ == b'h' => {
+                        team_indices.contains(team)
+                    }
+                    _ => *typ != b'h',
+                }
+        }
+        _ => false,
+    }
+}
+
+fn is_msg_valid(msg: &[u8], _team_indices: &[u8]) -> bool {
+    if let Some(typ) = msg.get(1) {
+        VALID_MESSAGES.contains(typ)
+    } else {
+        false
+    }
+}
+
+fn is_msg_empty(msg: &[u8]) -> bool {
+    msg.get(1).filter(|t| **t == b'+').is_some()
+}
+
+fn is_msg_timed(msg: &[u8]) -> bool {
+    msg.get(1)
+        .filter(|t| !NON_TIMED_MESSAGES.contains(t))
+        .is_some()
+}
+
+fn voting_description(kind: &VoteType) -> String {
+    format!(
+        "New voting started: {}",
+        match kind {
+            VoteType::Kick(nick) => format!("kick {}", nick),
+            VoteType::Map(name) => format!("map {}", name.as_ref().unwrap()),
+            VoteType::Pause => "pause".to_string(),
+            VoteType::NewSeed => "new seed".to_string(),
+            VoteType::HedgehogsPerTeam(number) => format!("hedgehogs per team: {}", number),
+        }
+    )
+}
+
+fn room_message_flag(msg: &HWProtocolMessage) -> RoomFlags {
+    use crate::protocol::messages::HWProtocolMessage::*;
+    match msg {
+        ToggleRestrictJoin => RoomFlags::RESTRICTED_JOIN,
+        ToggleRestrictTeams => RoomFlags::RESTRICTED_TEAM_ADD,
+        ToggleRegisteredOnly => RoomFlags::RESTRICTED_UNREGISTERED_PLAYERS,
+        _ => RoomFlags::empty(),
+    }
+}
+
+pub fn handle(
+    server: &mut HWServer,
+    client_id: ClientId,
+    response: &mut super::Response,
+    room_id: RoomId,
+    message: HWProtocolMessage,
+) {
+    let client = &mut server.clients[client_id];
+    let room = &mut server.rooms[room_id];
+
+    use crate::protocol::messages::HWProtocolMessage::*;
+    match message {
+        Part(msg) => {
+            let msg = match msg {
+                Some(s) => format!("part: {}", s),
+                None => "part".to_string(),
+            };
+            super::common::exit_room(server, client_id, response, &msg);
+        }
+        Chat(msg) => {
+            response.add(
+                ChatMsg {
+                    nick: client.nick.clone(),
+                    msg,
+                }
+                .send_all()
+                .in_room(room_id),
+            );
+        }
+        TeamChat(msg) => {
+            let room = &server.rooms[room_id];
+            if let Some(ref info) = room.game_info {
+                if let Some(clan_color) = room.find_team_color(client_id) {
+                    let client = &server.clients[client_id];
+                    let engine_msg =
+                        to_engine_msg(format!("b{}]{}\x20\x20", client.nick, msg).bytes());
+                    let team = room.clan_team_owners(clan_color).collect();
+                    response.add(ForwardEngineMessage(vec![engine_msg]).send_many(team))
+                }
+            }
+        }
+        Fix => {
+            if client.is_admin() {
+                room.set_is_fixed(true);
+                room.set_join_restriction(false);
+                room.set_team_add_restriction(false);
+                room.set_unregistered_players_restriction(true);
+            }
+        }
+        Unfix => {
+            if client.is_admin() {
+                room.set_is_fixed(false);
+            }
+        }
+        Greeting(text) => {
+            if client.is_admin() || client.is_master() && !room.is_fixed() {
+                room.greeting = text;
+            }
+        }
+        MaxTeams(count) => {
+            if !client.is_master() {
+                response.add(Warning("You're not the room master!".to_string()).send_self());
+            } else if !(2..=MAX_TEAMS_IN_ROOM).contains(&count) {
+                response
+                    .add(Warning("/maxteams: specify number from 2 to 8".to_string()).send_self());
+            } else {
+                server.rooms[room_id].max_teams = count;
+            }
+        }
+        RoomName(new_name) => {
+            if is_name_illegal(&new_name) {
+                response.add(Warning("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: $()*+?[]^{|}".to_string()).send_self());
+            } else if server.has_room(&new_name) {
+                response.add(
+                    Warning("A room with the same name already exists.".to_string()).send_self(),
+                );
+            } else {
+                let room = &mut server.rooms[room_id];
+                if room.is_fixed() || room.master_id != Some(client_id) {
+                    response.add(Warning("Access denied.".to_string()).send_self());
+                } else {
+                    let mut old_name = new_name.clone();
+                    let client = &server.clients[client_id];
+                    swap(&mut room.name, &mut old_name);
+                    super::common::get_room_update(Some(old_name), room, Some(&client), response);
+                }
+            }
+        }
+        ToggleReady => {
+            let flags = if client.is_ready() {
+                room.ready_players_number -= 1;
+                remove_flags(&[Flags::Ready])
+            } else {
+                room.ready_players_number += 1;
+                add_flags(&[Flags::Ready])
+            };
+
+            let msg = if client.protocol_number < 38 {
+                LegacyReady(client.is_ready(), vec![client.nick.clone()])
+            } else {
+                ClientFlags(flags, vec![client.nick.clone()])
+            };
+            response.add(msg.send_all().in_room(room.id));
+            client.set_is_ready(!client.is_ready());
+
+            if room.is_fixed() && room.ready_players_number == room.players_number {
+                super::common::start_game(server, room_id, response);
+            }
+        }
+        AddTeam(mut info) => {
+            if room.teams.len() >= room.max_teams as usize {
+                response.add(Warning("Too many teams!".to_string()).send_self());
+            } else if room.addable_hedgehogs() == 0 {
+                response.add(Warning("Too many hedgehogs!".to_string()).send_self());
+            } else if room.find_team(|t| t.name == info.name) != None {
+                response.add(
+                    Warning("There's already a team with same name in the list.".to_string())
+                        .send_self(),
+                );
+            } else if room.game_info.is_some() {
+                response.add(
+                    Warning("Joining not possible: Round is in progress.".to_string()).send_self(),
+                );
+            } else if room.is_team_add_restricted() {
+                response.add(
+                    Warning("This room currently does not allow adding new teams.".to_string())
+                        .send_self(),
+                );
+            } else {
+                info.owner = client.nick.clone();
+                let team = room.add_team(client.id, *info, client.protocol_number < 42);
+                client.teams_in_game += 1;
+                client.clan = Some(team.color);
+                response.add(TeamAccepted(team.name.clone()).send_self());
+                response.add(
+                    TeamAdd(team.to_protocol())
+                        .send_all()
+                        .in_room(room_id)
+                        .but_self(),
+                );
+                response.add(
+                    TeamColor(team.name.clone(), team.color)
+                        .send_all()
+                        .in_room(room_id),
+                );
+                response.add(
+                    HedgehogsNumber(team.name.clone(), team.hedgehogs_number)
+                        .send_all()
+                        .in_room(room_id),
+                );
+
+                let room_master = if let Some(id) = room.master_id {
+                    Some(&server.clients[id])
+                } else {
+                    None
+                };
+                super::common::get_room_update(None, room, room_master, response);
+            }
+        }
+        RemoveTeam(name) => match room.find_team_owner(&name) {
+            None => response.add(
+                Warning("Error: The team you tried to remove does not exist.".to_string())
+                    .send_self(),
+            ),
+            Some((id, _)) if id != client_id => response
+                .add(Warning("You can't remove a team you don't own.".to_string()).send_self()),
+            Some((_, name)) => {
+                client.teams_in_game -= 1;
+                client.clan = room.find_team_color(client.id);
+                super::common::remove_teams(
+                    room,
+                    vec![name.to_string()],
+                    client.is_in_game(),
+                    response,
+                );
+
+                match room.game_info {
+                    Some(ref info) if info.teams_in_game == 0 => {
+                        super::common::end_game(server, room_id, response)
+                    }
+                    _ => (),
+                }
+            }
+        },
+        SetHedgehogsNumber(team_name, number) => {
+            let addable_hedgehogs = room.addable_hedgehogs();
+            if let Some((_, team)) = room.find_team_and_owner_mut(|t| t.name == team_name) {
+                let max_hedgehogs = min(
+                    MAX_HEDGEHOGS_PER_TEAM,
+                    addable_hedgehogs + team.hedgehogs_number,
+                );
+                if !client.is_master() {
+                    response.add(Error("You're not the room master!".to_string()).send_self());
+                } else if !(1..=max_hedgehogs).contains(&number) {
+                    response
+                        .add(HedgehogsNumber(team.name.clone(), team.hedgehogs_number).send_self());
+                } else {
+                    team.hedgehogs_number = number;
+                    response.add(
+                        HedgehogsNumber(team.name.clone(), number)
+                            .send_all()
+                            .in_room(room_id)
+                            .but_self(),
+                    );
+                }
+            } else {
+                response.add(Warning("No such team.".to_string()).send_self());
+            }
+        }
+        SetTeamColor(team_name, color) => {
+            if let Some((owner, team)) = room.find_team_and_owner_mut(|t| t.name == team_name) {
+                if !client.is_master() {
+                    response.add(Error("You're not the room master!".to_string()).send_self());
+                } else {
+                    team.color = color;
+                    response.add(
+                        TeamColor(team.name.clone(), color)
+                            .send_all()
+                            .in_room(room_id)
+                            .but_self(),
+                    );
+                    server.clients[owner].clan = Some(color);
+                }
+            } else {
+                response.add(Warning("No such team.".to_string()).send_self());
+            }
+        }
+        Cfg(cfg) => {
+            if room.is_fixed() {
+                response.add(Warning("Access denied.".to_string()).send_self());
+            } else if !client.is_master() {
+                response.add(Error("You're not the room master!".to_string()).send_self());
+            } else {
+                let cfg = match cfg {
+                    GameCfg::Scheme(name, mut values) => {
+                        if client.protocol_number == 49 && values.len() >= 2 {
+                            let mut s = "X".repeat(50);
+                            s.push_str(&values.pop().unwrap());
+                            values.push(s);
+                        }
+                        GameCfg::Scheme(name, values)
+                    }
+                    cfg => cfg,
+                };
+
+                response.add(cfg.to_server_msg().send_all().in_room(room.id).but_self());
+                room.set_config(cfg);
+            }
+        }
+        Save(name, location) => {
+            response.add(
+                server_chat(format!("Room config saved as {}", name))
+                    .send_all()
+                    .in_room(room_id),
+            );
+            room.save_config(name, location);
+        }
+        #[cfg(feature = "official-server")]
+        SaveRoom(filename) => {
+            if client.is_admin() {
+                match room.get_saves() {
+                    Ok(contents) => response.request_io(super::IoTask::SaveRoom {
+                        room_id,
+                        filename,
+                        contents,
+                    }),
+                    Err(e) => {
+                        warn!("Error while serializing the room configs: {}", e);
+                        response.add(
+                            Warning("Unable to serialize the room configs.".to_string())
+                                .send_self(),
+                        )
+                    }
+                }
+            }
+        }
+        #[cfg(feature = "official-server")]
+        LoadRoom(filename) => {
+            if client.is_admin() {
+                response.request_io(super::IoTask::LoadRoom { room_id, filename });
+            }
+        }
+        Delete(name) => {
+            if !room.delete_config(&name) {
+                response.add(Warning(format!("Save doesn't exist: {}", name)).send_self());
+            } else {
+                response.add(
+                    server_chat(format!("Room config {} has been deleted", name))
+                        .send_all()
+                        .in_room(room_id),
+                );
+            }
+        }
+        CallVote(None) => {
+            response.add(server_chat("Available callvote commands: kick <nickname>, map <name>, pause, newseed, hedgehogs <number>".to_string())
+                .send_self());
+        }
+        CallVote(Some(kind)) => {
+            let is_in_game = room.game_info.is_some();
+            let error = match &kind {
+                VoteType::Kick(nick) => {
+                    if server
+                        .find_client(&nick)
+                        .filter(|c| c.room_id == Some(room_id))
+                        .is_some()
+                    {
+                        None
+                    } else {
+                        Some("/callvote kick: No such user!".to_string())
+                    }
+                }
+                VoteType::Map(None) => {
+                    let names: Vec<_> = server.rooms[room_id].saves.keys().cloned().collect();
+                    if names.is_empty() {
+                        Some("/callvote map: No maps saved in this room!".to_string())
+                    } else {
+                        Some(format!("Available maps: {}", names.join(", ")))
+                    }
+                }
+                VoteType::Map(Some(name)) => {
+                    if room.saves.get(&name[..]).is_some() {
+                        None
+                    } else {
+                        Some("/callvote map: No such map!".to_string())
+                    }
+                }
+                VoteType::Pause => {
+                    if is_in_game {
+                        None
+                    } else {
+                        Some("/callvote pause: No game in progress!".to_string())
+                    }
+                }
+                VoteType::NewSeed => None,
+                VoteType::HedgehogsPerTeam(number) => match number {
+                    1...MAX_HEDGEHOGS_PER_TEAM => None,
+                    _ => Some("/callvote hedgehogs: Specify number from 1 to 8.".to_string()),
+                },
+            };
+
+            match error {
+                None => {
+                    let msg = voting_description(&kind);
+                    let voting = Voting::new(kind, server.room_clients(client_id).collect());
+                    let room = &mut server.rooms[room_id];
+                    room.voting = Some(voting);
+                    response.add(server_chat(msg).send_all().in_room(room_id));
+                    super::common::submit_vote(
+                        server,
+                        types::Vote {
+                            is_pro: true,
+                            is_forced: false,
+                        },
+                        response,
+                    );
+                }
+                Some(msg) => {
+                    response.add(server_chat(msg).send_self());
+                }
+            }
+        }
+        Vote(vote) => {
+            super::common::submit_vote(
+                server,
+                types::Vote {
+                    is_pro: vote,
+                    is_forced: false,
+                },
+                response,
+            );
+        }
+        ForceVote(vote) => {
+            let is_forced = client.is_admin();
+            super::common::submit_vote(
+                server,
+                types::Vote {
+                    is_pro: vote,
+                    is_forced,
+                },
+                response,
+            );
+        }
+        ToggleRestrictJoin | ToggleRestrictTeams | ToggleRegisteredOnly => {
+            if client.is_master() {
+                room.flags.toggle(room_message_flag(&message));
+                super::common::get_room_update(None, room, Some(&client), response);
+            }
+        }
+        StartGame => {
+            super::common::start_game(server, room_id, response);
+        }
+        EngineMessage(em) => {
+            if client.teams_in_game > 0 {
+                let decoding = decode(&em[..]).unwrap();
+                let messages = by_msg(&decoding);
+                let valid = messages.filter(|m| is_msg_valid(m, &client.team_indices));
+                let non_empty = valid.clone().filter(|m| !is_msg_empty(m));
+                let sync_msg = valid.clone().filter(|m| is_msg_timed(m)).last().map(|m| {
+                    if is_msg_empty(m) {
+                        Some(encode(m))
+                    } else {
+                        None
+                    }
+                });
+
+                let em_response = encode(&valid.flat_map(|msg| msg).cloned().collect::<Vec<_>>());
+                if !em_response.is_empty() {
+                    response.add(
+                        ForwardEngineMessage(vec![em_response])
+                            .send_all()
+                            .in_room(room.id)
+                            .but_self(),
+                    );
+                }
+                let em_log = encode(&non_empty.flat_map(|msg| msg).cloned().collect::<Vec<_>>());
+                if let Some(ref mut info) = room.game_info {
+                    if !em_log.is_empty() {
+                        info.msg_log.push(em_log);
+                    }
+                    if let Some(msg) = sync_msg {
+                        info.sync_msg = msg;
+                    }
+                }
+            }
+        }
+        RoundFinished => {
+            let mut game_ended = false;
+            if client.is_in_game() {
+                client.set_is_in_game(false);
+                response.add(
+                    ClientFlags(remove_flags(&[Flags::InGame]), vec![client.nick.clone()])
+                        .send_all()
+                        .in_room(room.id),
+                );
+                let team_names: Vec<_> = room
+                    .client_teams(client_id)
+                    .map(|t| t.name.clone())
+                    .collect();
+
+                if let Some(ref mut info) = room.game_info {
+                    info.teams_in_game -= team_names.len() as u8;
+                    if info.teams_in_game == 0 {
+                        game_ended = true;
+                    }
+
+                    for team_name in team_names {
+                        let msg = once(b'F').chain(team_name.bytes());
+                        response.add(
+                            ForwardEngineMessage(vec![to_engine_msg(msg)])
+                                .send_all()
+                                .in_room(room_id)
+                                .but_self(),
+                        );
+
+                        let remove_msg = to_engine_msg(once(b'F').chain(team_name.bytes()));
+                        if let Some(m) = &info.sync_msg {
+                            info.msg_log.push(m.clone());
+                        }
+                        if info.sync_msg.is_some() {
+                            info.sync_msg = None
+                        }
+                        info.msg_log.push(remove_msg.clone());
+                        response.add(
+                            ForwardEngineMessage(vec![remove_msg])
+                                .send_all()
+                                .in_room(room_id)
+                                .but_self(),
+                        );
+                    }
+                }
+            }
+            if game_ended {
+                super::common::end_game(server, room_id, response)
+            }
+        }
+        Rnd(v) => {
+            let result = rnd_reply(&v);
+            let mut echo = vec!["/rnd".to_string()];
+            echo.extend(v.into_iter());
+            let chat_msg = ChatMsg {
+                nick: server.clients[client_id].nick.clone(),
+                msg: echo.join(" "),
+            };
+            response.add(chat_msg.send_all().in_room(room_id));
+            response.add(result.send_all().in_room(room_id));
+        }
+        Delegate(nick) => {
+            let delegate_id = server.find_client(&nick).map(|c| (c.id, c.room_id));
+            let client = &server.clients[client_id];
+            if !(client.is_admin() || client.is_master()) {
+                response.add(
+                    Warning("You're not the room master or a server admin!".to_string())
+                        .send_self(),
+                )
+            } else {
+                match delegate_id {
+                    None => response.add(Warning("Player is not online.".to_string()).send_self()),
+                    Some((id, _)) if id == client_id => response
+                        .add(Warning("You're already the room master.".to_string()).send_self()),
+                    Some((_, id)) if id != Some(room_id) => response
+                        .add(Warning("The player is not in your room.".to_string()).send_self()),
+                    Some((id, _)) => {
+                        super::common::change_master(server, room_id, id, response);
+                    }
+                }
+            }
+        }
+        _ => warn!("Unimplemented!"),
+    }
+}
--- a/rust/hedgewars-server/src/main.rs	Tue May 28 17:49:04 2019 +0200
+++ b/rust/hedgewars-server/src/main.rs	Tue May 28 19:04:18 2019 +0300
@@ -6,7 +6,9 @@
 use mio::{net::*, *};
 use std::{env, str::FromStr as _, time::Duration};
 
+mod core;
 mod protocol;
+mod handlers;
 mod server;
 mod utils;
 
--- a/rust/hedgewars-server/src/protocol.rs	Tue May 28 17:49:04 2019 +0200
+++ b/rust/hedgewars-server/src/protocol.rs	Tue May 28 19:04:18 2019 +0300
@@ -1,4 +1,4 @@
-use crate::protocol::parser::message;
+use self::parser::message;
 use log::*;
 use netbuf;
 use nom::{Err, ErrorKind, IResult};
--- a/rust/hedgewars-server/src/protocol/messages.rs	Tue May 28 17:49:04 2019 +0200
+++ b/rust/hedgewars-server/src/protocol/messages.rs	Tue May 28 19:04:18 2019 +0300
@@ -1,4 +1,4 @@
-use crate::server::coretypes::{GameCfg, HedgehogInfo, ServerVar, TeamInfo, VoteType};
+use crate::core::types::{GameCfg, HedgehogInfo, ServerVar, TeamInfo, VoteType};
 use std::{convert::From, iter::once, ops};
 
 #[derive(PartialEq, Eq, Clone, Debug)]
@@ -183,7 +183,7 @@
 
 impl GameCfg {
     pub fn to_protocol(&self) -> (String, Vec<String>) {
-        use crate::server::coretypes::GameCfg::*;
+        use crate::core::types::GameCfg::*;
         match self {
             FeatureSize(s) => ("FEATURE_SIZE".to_string(), vec![s.to_string()]),
             MapType(t) => ("MAP".to_string(), vec![t.to_string()]),
--- a/rust/hedgewars-server/src/protocol/parser.rs	Tue May 28 17:49:04 2019 +0200
+++ b/rust/hedgewars-server/src/protocol/parser.rs	Tue May 28 19:04:18 2019 +0300
@@ -14,8 +14,10 @@
     str::{FromStr, Utf8Error},
 };
 
-use super::messages::{HWProtocolMessage, HWProtocolMessage::*};
-use crate::server::coretypes::{
+use super::{
+    messages::{HWProtocolMessage, HWProtocolMessage::*},
+};
+use crate::core::types::{
     GameCfg, HedgehogInfo, ServerVar, TeamInfo, VoteType, MAX_HEDGEHOGS_PER_TEAM,
 };
 
--- a/rust/hedgewars-server/src/protocol/test.rs	Tue May 28 17:49:04 2019 +0200
+++ b/rust/hedgewars-server/src/protocol/test.rs	Tue May 28 19:04:18 2019 +0300
@@ -4,7 +4,7 @@
     test_runner::{Reason, TestRunner},
 };
 
-use crate::server::coretypes::{GameCfg, HedgehogInfo, ServerVar, ServerVar::*, TeamInfo};
+use crate::core::types::{GameCfg, HedgehogInfo, ServerVar, ServerVar::*, TeamInfo};
 
 use super::messages::{HWProtocolMessage, HWProtocolMessage::*};
 
@@ -75,7 +75,7 @@
     type Parameters = ();
 
     fn arbitrary_with(_args: <Self as Arbitrary>::Parameters) -> <Self as Arbitrary>::Strategy {
-        use crate::server::coretypes::GameCfg::*;
+        use crate::core::types::GameCfg::*;
         (0..10)
             .no_shrink()
             .prop_flat_map(|i| {
--- a/rust/hedgewars-server/src/server.rs	Tue May 28 17:49:04 2019 +0200
+++ b/rust/hedgewars-server/src/server.rs	Tue May 28 19:04:18 2019 +0300
@@ -1,12 +1,5 @@
-mod actions;
-pub mod client;
-pub mod core;
-pub mod coretypes;
 #[cfg(feature = "official-server")]
 mod database;
-mod handlers;
-pub mod indexslab;
 #[cfg(feature = "official-server")]
 pub mod io;
-pub mod network;
-pub mod room;
+pub mod network;
\ No newline at end of file
--- a/rust/hedgewars-server/src/server/actions.rs	Tue May 28 17:49:04 2019 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,119 +0,0 @@
-use super::{
-    client::HWClient,
-    core::HWServer,
-    coretypes::{ClientId, GameCfg, RoomId, VoteType},
-    handlers,
-    room::HWRoom,
-    room::{GameInfo, RoomFlags},
-};
-use crate::{
-    protocol::messages::{server_chat, HWProtocolMessage, HWServerMessage, HWServerMessage::*},
-    utils::to_engine_msg,
-};
-use rand::{distributions::Uniform, thread_rng, Rng};
-use std::{io, io::Write, iter::once, mem::replace};
-
-#[cfg(feature = "official-server")]
-use super::database;
-
-pub enum DestinationGroup {
-    All,
-    Lobby,
-    Room(RoomId),
-    Protocol(u16),
-}
-
-pub enum Destination {
-    ToId(ClientId),
-    ToIds(Vec<ClientId>),
-    ToSelf,
-    ToAll {
-        group: DestinationGroup,
-        skip_self: bool,
-    },
-}
-
-pub struct PendingMessage {
-    pub destination: Destination,
-    pub message: HWServerMessage,
-}
-
-impl PendingMessage {
-    pub fn send(message: HWServerMessage, client_id: ClientId) -> PendingMessage {
-        PendingMessage {
-            destination: Destination::ToId(client_id),
-            message,
-        }
-    }
-
-    pub fn send_many(message: HWServerMessage, client_ids: Vec<ClientId>) -> PendingMessage {
-        PendingMessage {
-            destination: Destination::ToIds(client_ids),
-            message,
-        }
-    }
-
-    pub fn send_self(message: HWServerMessage) -> PendingMessage {
-        PendingMessage {
-            destination: Destination::ToSelf,
-            message,
-        }
-    }
-
-    pub fn send_all(message: HWServerMessage) -> PendingMessage {
-        let destination = Destination::ToAll {
-            group: DestinationGroup::All,
-            skip_self: false,
-        };
-        PendingMessage {
-            destination,
-            message,
-        }
-    }
-
-    pub fn in_room(mut self, clients_room_id: RoomId) -> PendingMessage {
-        if let Destination::ToAll { ref mut group, .. } = self.destination {
-            *group = DestinationGroup::Room(clients_room_id)
-        }
-        self
-    }
-
-    pub fn in_lobby(mut self) -> PendingMessage {
-        if let Destination::ToAll { ref mut group, .. } = self.destination {
-            *group = DestinationGroup::Lobby
-        }
-        self
-    }
-
-    pub fn with_protocol(mut self, protocol_number: u16) -> PendingMessage {
-        if let Destination::ToAll { ref mut group, .. } = self.destination {
-            *group = DestinationGroup::Protocol(protocol_number)
-        }
-        self
-    }
-
-    pub fn but_self(mut self) -> PendingMessage {
-        if let Destination::ToAll {
-            ref mut skip_self, ..
-        } = self.destination
-        {
-            *skip_self = true
-        }
-        self
-    }
-}
-
-impl HWServerMessage {
-    pub fn send(self, client_id: ClientId) -> PendingMessage {
-        PendingMessage::send(self, client_id)
-    }
-    pub fn send_many(self, client_ids: Vec<ClientId>) -> PendingMessage {
-        PendingMessage::send_many(self, client_ids)
-    }
-    pub fn send_self(self) -> PendingMessage {
-        PendingMessage::send_self(self)
-    }
-    pub fn send_all(self) -> PendingMessage {
-        PendingMessage::send_all(self)
-    }
-}
--- a/rust/hedgewars-server/src/server/client.rs	Tue May 28 17:49:04 2019 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,109 +0,0 @@
-use super::coretypes::ClientId;
-use bitflags::*;
-
-bitflags! {
-    pub struct ClientFlags: u16 {
-        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 NONE = 0b0000_0000;
-        const DEFAULT = Self::NONE.bits;
-    }
-}
-
-pub struct HWClient {
-    pub id: ClientId,
-    pub room_id: Option<usize>,
-    pub nick: String,
-    pub protocol_number: u16,
-    pub flags: ClientFlags,
-    pub teams_in_game: u8,
-    pub team_indices: Vec<u8>,
-    pub clan: Option<u8>,
-}
-
-impl HWClient {
-    pub fn new(id: ClientId, protocol_number: u16, nick: String) -> HWClient {
-        HWClient {
-            id,
-            nick,
-            protocol_number,
-            room_id: None,
-            flags: ClientFlags::DEFAULT,
-            teams_in_game: 0,
-            team_indices: Vec::new(),
-            clan: None,
-        }
-    }
-
-    fn contains(&self, mask: ClientFlags) -> bool {
-        self.flags.contains(mask)
-    }
-
-    fn set(&mut self, mask: ClientFlags, value: bool) {
-        self.flags.set(mask, value);
-    }
-
-    pub fn is_admin(&self) -> bool {
-        self.contains(ClientFlags::IS_ADMIN)
-    }
-    pub fn is_master(&self) -> bool {
-        self.contains(ClientFlags::IS_MASTER)
-    }
-    pub fn is_ready(&self) -> bool {
-        self.contains(ClientFlags::IS_READY)
-    }
-    pub fn is_in_game(&self) -> bool {
-        self.contains(ClientFlags::IS_IN_GAME)
-    }
-    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)
-    }
-    pub fn has_super_power(&self) -> bool {
-        self.contains(ClientFlags::HAS_SUPER_POWER)
-    }
-    pub fn is_registered(&self) -> bool {
-        self.contains(ClientFlags::IS_REGISTERED)
-    }
-
-    pub fn set_is_admin(&mut self, value: bool) {
-        self.set(ClientFlags::IS_ADMIN, value)
-    }
-    pub fn set_is_master(&mut self, value: bool) {
-        self.set(ClientFlags::IS_MASTER, value)
-    }
-    pub fn set_is_ready(&mut self, value: bool) {
-        self.set(ClientFlags::IS_READY, value)
-    }
-    pub fn set_is_in_game(&mut self, value: bool) {
-        self.set(ClientFlags::IS_IN_GAME, value)
-    }
-    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)
-    }
-    pub fn set_has_super_power(&mut self, value: bool) {
-        self.set(ClientFlags::HAS_SUPER_POWER, value)
-    }
-    pub fn set_is_registered(&mut self, value: bool) {
-        self.set(ClientFlags::IS_REGISTERED, value)
-    }
-}
--- a/rust/hedgewars-server/src/server/core.rs	Tue May 28 17:49:04 2019 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,283 +0,0 @@
-use super::{
-    client::HWClient,
-    coretypes::{ClientId, RoomId},
-    indexslab::IndexSlab,
-    room::HWRoom,
-};
-use crate::utils;
-
-use crate::protocol::messages::HWProtocolMessage::Greeting;
-use bitflags::*;
-use log::*;
-use slab;
-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 is_checker: bool,
-}
-
-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,
-            is_checker: false,
-        };
-        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);
-        client
-    }
-}
-
-pub struct ServerGreetings {
-    pub for_latest_protocol: String,
-    pub for_old_protocols: String,
-}
-
-impl ServerGreetings {
-    fn new() -> Self {
-        Self {
-            for_latest_protocol: "\u{1f994} is watching".to_string(),
-            for_old_protocols: "\u{1f994} is watching".to_string(),
-        }
-    }
-}
-
-bitflags! {
-    pub struct ServerFlags: u8 {
-        const REGISTERED_ONLY = 0b0000_1000;
-    }
-}
-
-pub struct HWServer {
-    pub clients: IndexSlab<HWClient>,
-    pub rooms: Slab<HWRoom>,
-    pub anteroom: HWAnteroom,
-    pub latest_protocol: u16,
-    pub flags: ServerFlags,
-    pub greetings: ServerGreetings,
-}
-
-impl HWServer {
-    pub fn new(clients_limit: usize, rooms_limit: usize) -> Self {
-        let rooms = Slab::with_capacity(rooms_limit);
-        let clients = IndexSlab::with_capacity(clients_limit);
-        Self {
-            clients,
-            rooms,
-            anteroom: HWAnteroom::new(clients_limit),
-            greetings: ServerGreetings::new(),
-            latest_protocol: 58,
-            flags: ServerFlags::empty(),
-        }
-    }
-
-    pub fn add_client(&mut self, client_id: ClientId, data: HWAnteClient) {
-        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);
-            self.clients.insert(client_id, client);
-        }
-    }
-
-    pub fn remove_client(&mut self, client_id: ClientId) {
-        self.clients.remove(client_id);
-    }
-
-    pub fn get_greetings(&self, client_id: ClientId) -> &str {
-        if self.clients[client_id].protocol_number < self.latest_protocol {
-            &self.greetings.for_old_protocols
-        } else {
-            &self.greetings.for_latest_protocol
-        }
-    }
-
-    #[inline]
-    pub fn create_room(
-        &mut self,
-        creator_id: ClientId,
-        name: String,
-        password: Option<String>,
-    ) -> RoomId {
-        create_room(
-            &mut self.clients[creator_id],
-            &mut self.rooms,
-            name,
-            password,
-        )
-    }
-
-    #[inline]
-    pub fn move_to_room(&mut self, client_id: ClientId, room_id: RoomId) {
-        move_to_room(&mut self.clients[client_id], &mut self.rooms[room_id])
-    }
-
-    pub fn has_room(&self, name: &str) -> bool {
-        self.find_room(name).is_some()
-    }
-
-    pub fn find_room(&self, name: &str) -> Option<&HWRoom> {
-        self.rooms
-            .iter()
-            .find_map(|(_, r)| Some(r).filter(|r| r.name == name))
-    }
-
-    pub fn find_room_mut(&mut self, name: &str) -> Option<&mut HWRoom> {
-        self.rooms
-            .iter_mut()
-            .find_map(|(_, r)| Some(r).filter(|r| r.name == name))
-    }
-
-    pub fn find_client(&self, nick: &str) -> Option<&HWClient> {
-        self.clients
-            .iter()
-            .find_map(|(_, c)| Some(c).filter(|c| c.nick == nick))
-    }
-
-    pub fn find_client_mut(&mut self, nick: &str) -> Option<&mut HWClient> {
-        self.clients
-            .iter_mut()
-            .find_map(|(_, c)| Some(c).filter(|c| c.nick == nick))
-    }
-
-    pub fn all_clients(&self) -> impl Iterator<Item = ClientId> + '_ {
-        self.clients.iter().map(|(id, _)| id)
-    }
-
-    pub fn filter_clients<'a, F>(&'a self, f: F) -> impl Iterator<Item = ClientId> + 'a
-    where
-        F: Fn(&(usize, &HWClient)) -> bool + 'a,
-    {
-        self.clients.iter().filter(f).map(|(_, c)| c.id)
-    }
-
-    pub fn filter_rooms<'a, F>(&'a self, f: F) -> impl Iterator<Item = RoomId> + 'a
-    where
-        F: Fn(&(usize, &HWRoom)) -> bool + 'a,
-    {
-        self.rooms.iter().filter(f).map(|(_, c)| c.id)
-    }
-
-    pub fn collect_clients<F>(&self, f: F) -> Vec<ClientId>
-    where
-        F: Fn(&(usize, &HWClient)) -> bool,
-    {
-        self.filter_clients(f).collect()
-    }
-
-    pub fn collect_nicks<F>(&self, f: F) -> Vec<String>
-    where
-        F: Fn(&(usize, &HWClient)) -> bool,
-    {
-        self.clients
-            .iter()
-            .filter(f)
-            .map(|(_, c)| c.nick.clone())
-            .collect()
-    }
-
-    pub fn lobby_clients(&self) -> impl Iterator<Item = ClientId> + '_ {
-        self.filter_clients(|(_, c)| c.room_id == None)
-    }
-
-    pub fn room_clients(&self, room_id: RoomId) -> impl Iterator<Item = ClientId> + '_ {
-        self.filter_clients(move |(_, c)| c.room_id == Some(room_id))
-    }
-
-    pub fn protocol_clients(&self, protocol: u16) -> impl Iterator<Item = ClientId> + '_ {
-        self.filter_clients(move |(_, c)| c.protocol_number == protocol)
-    }
-
-    pub fn protocol_rooms(&self, protocol: u16) -> impl Iterator<Item = RoomId> + '_ {
-        self.filter_rooms(move |(_, r)| r.protocol_number == protocol)
-    }
-
-    pub fn other_clients_in_room(&self, self_id: ClientId) -> Vec<ClientId> {
-        let room_id = self.clients[self_id].room_id;
-        self.collect_clients(|(id, c)| *id != self_id && c.room_id == room_id)
-    }
-
-    pub fn is_registered_only(&self) -> bool {
-        self.flags.contains(ServerFlags::REGISTERED_ONLY)
-    }
-
-    pub fn set_is_registered_only(&mut self, value: bool) {
-        self.flags.set(ServerFlags::REGISTERED_ONLY, value)
-    }
-}
-
-fn allocate_room(rooms: &mut Slab<HWRoom>) -> &mut HWRoom {
-    let entry = rooms.vacant_entry();
-    let room = HWRoom::new(entry.key());
-    entry.insert(room)
-}
-
-fn create_room(
-    client: &mut HWClient,
-    rooms: &mut Slab<HWRoom>,
-    name: String,
-    password: Option<String>,
-) -> RoomId {
-    let room = allocate_room(rooms);
-
-    room.master_id = Some(client.id);
-    room.name = name;
-    room.password = password;
-    room.protocol_number = client.protocol_number;
-
-    room.players_number = 1;
-    room.ready_players_number = 1;
-
-    client.room_id = Some(room.id);
-    client.set_is_master(true);
-    client.set_is_ready(true);
-    client.set_is_joined_mid_game(false);
-
-    room.id
-}
-
-fn move_to_room(client: &mut HWClient, room: &mut HWRoom) {
-    debug_assert!(client.room_id != Some(room.id));
-
-    room.players_number += 1;
-
-    client.room_id = Some(room.id);
-    client.set_is_joined_mid_game(room.game_info.is_some());
-    client.set_is_in_game(room.game_info.is_some());
-
-    if let Some(ref mut info) = room.game_info {
-        let teams = info.client_teams(client.id);
-        client.teams_in_game = teams.clone().count() as u8;
-        client.clan = teams.clone().next().map(|t| t.color);
-        let team_names: Vec<_> = teams.map(|t| t.name.clone()).collect();
-
-        if !team_names.is_empty() {
-            info.left_teams.retain(|name| !team_names.contains(&name));
-            info.teams_in_game += team_names.len() as u8;
-            room.teams = info
-                .teams_at_start
-                .iter()
-                .filter(|(_, t)| !team_names.contains(&t.name))
-                .cloned()
-                .collect();
-        }
-    }
-}
--- a/rust/hedgewars-server/src/server/coretypes.rs	Tue May 28 17:49:04 2019 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,193 +0,0 @@
-use serde_derive::{Deserialize, Serialize};
-
-pub type ClientId = usize;
-pub type RoomId = usize;
-
-pub const MAX_HEDGEHOGS_PER_TEAM: u8 = 8;
-
-#[derive(PartialEq, Eq, Clone, Debug)]
-pub enum ServerVar {
-    MOTDNew(String),
-    MOTDOld(String),
-    LatestProto(u16),
-}
-
-#[derive(PartialEq, Eq, Clone, Debug)]
-pub enum GameCfg {
-    FeatureSize(u32),
-    MapType(String),
-    MapGenerator(u32),
-    MazeSize(u32),
-    Seed(String),
-    Template(u32),
-
-    Ammo(String, Option<String>),
-    Scheme(String, Vec<String>),
-    Script(String),
-    Theme(String),
-    DrawnMap(String),
-}
-
-#[derive(PartialEq, Eq, Clone, Debug)]
-pub struct TeamInfo {
-    pub owner: String,
-    pub name: String,
-    pub color: u8,
-    pub grave: String,
-    pub fort: String,
-    pub voice_pack: String,
-    pub flag: String,
-    pub difficulty: u8,
-    pub hedgehogs_number: u8,
-    pub hedgehogs: [HedgehogInfo; MAX_HEDGEHOGS_PER_TEAM as usize],
-}
-
-#[derive(PartialEq, Eq, Clone, Debug)]
-pub struct HedgehogInfo {
-    pub name: String,
-    pub hat: String,
-}
-
-#[derive(Clone, Serialize, Deserialize)]
-pub struct Ammo {
-    pub name: String,
-    pub settings: Option<String>,
-}
-
-#[derive(Clone, Serialize, Deserialize)]
-pub struct Scheme {
-    pub name: String,
-    pub settings: Vec<String>,
-}
-
-#[derive(Clone, Serialize, Deserialize)]
-pub struct RoomConfig {
-    pub feature_size: u32,
-    pub map_type: String,
-    pub map_generator: u32,
-    pub maze_size: u32,
-    pub seed: String,
-    pub template: u32,
-
-    pub ammo: Ammo,
-    pub scheme: Scheme,
-    pub script: String,
-    pub theme: String,
-    pub drawn_map: Option<String>,
-}
-
-impl RoomConfig {
-    pub fn new() -> RoomConfig {
-        RoomConfig {
-            feature_size: 12,
-            map_type: "+rnd+".to_string(),
-            map_generator: 0,
-            maze_size: 0,
-            seed: "seed".to_string(),
-            template: 0,
-
-            ammo: Ammo {
-                name: "Default".to_string(),
-                settings: None,
-            },
-            scheme: Scheme {
-                name: "Default".to_string(),
-                settings: Vec::new(),
-            },
-            script: "Normal".to_string(),
-            theme: "\u{1f994}".to_string(),
-            drawn_map: None,
-        }
-    }
-
-    pub fn set_config(&mut self, cfg: GameCfg) {
-        match cfg {
-            GameCfg::FeatureSize(s) => self.feature_size = s,
-            GameCfg::MapType(t) => self.map_type = t,
-            GameCfg::MapGenerator(g) => self.map_generator = g,
-            GameCfg::MazeSize(s) => self.maze_size = s,
-            GameCfg::Seed(s) => self.seed = s,
-            GameCfg::Template(t) => self.template = t,
-
-            GameCfg::Ammo(n, s) => {
-                self.ammo = Ammo {
-                    name: n,
-                    settings: s,
-                }
-            }
-            GameCfg::Scheme(n, s) => {
-                self.scheme = Scheme {
-                    name: n,
-                    settings: s,
-                }
-            }
-            GameCfg::Script(s) => self.script = s,
-            GameCfg::Theme(t) => self.theme = t,
-            GameCfg::DrawnMap(m) => self.drawn_map = Some(m),
-        };
-    }
-
-    pub fn to_map_config(&self) -> Vec<String> {
-        vec![
-            self.feature_size.to_string(),
-            self.map_type.to_string(),
-            self.map_generator.to_string(),
-            self.maze_size.to_string(),
-            self.seed.to_string(),
-            self.template.to_string(),
-        ]
-    }
-
-    pub fn to_game_config(&self) -> Vec<GameCfg> {
-        use crate::server::coretypes::GameCfg::*;
-        let mut v = vec![
-            Ammo(self.ammo.name.to_string(), self.ammo.settings.clone()),
-            Scheme(self.scheme.name.to_string(), self.scheme.settings.clone()),
-            Script(self.script.to_string()),
-            Theme(self.theme.to_string()),
-        ];
-        if let Some(ref m) = self.drawn_map {
-            v.push(DrawnMap(m.to_string()))
-        }
-        v
-    }
-}
-
-pub struct Replay {
-    pub config: RoomConfig,
-    pub teams: Vec<TeamInfo>,
-    pub message_log: Vec<String>,
-}
-
-#[derive(PartialEq, Eq, Clone, Debug)]
-pub enum VoteType {
-    Kick(String),
-    Map(Option<String>),
-    Pause,
-    NewSeed,
-    HedgehogsPerTeam(u8),
-}
-
-pub struct Vote {
-    pub is_pro: bool,
-    pub is_forced: bool,
-}
-
-#[derive(Clone, Debug)]
-pub struct Voting {
-    pub ttl: u32,
-    pub voters: Vec<ClientId>,
-    pub votes: Vec<(ClientId, bool)>,
-    pub kind: VoteType,
-}
-
-impl Voting {
-    pub fn new(kind: VoteType, voters: Vec<ClientId>) -> Voting {
-        Voting {
-            kind,
-            voters,
-            ttl: 2,
-            votes: Vec::new(),
-        }
-    }
-}
--- a/rust/hedgewars-server/src/server/database.rs	Tue May 28 17:49:04 2019 +0200
+++ b/rust/hedgewars-server/src/server/database.rs	Tue May 28 19:04:18 2019 +0300
@@ -2,8 +2,9 @@
 use mysql::{error::DriverError, error::Error, from_row_opt, params};
 use openssl::sha::sha1;
 
-use super::handlers::AccountInfo;
-use crate::server::handlers::Sha1Digest;
+use crate::{
+    handlers::{Sha1Digest, AccountInfo}
+};
 
 const GET_ACCOUNT_QUERY: &str =
     r"SELECT CASE WHEN users.status = 1 THEN users.pass ELSE '' END,
--- a/rust/hedgewars-server/src/server/handlers.rs	Tue May 28 17:49:04 2019 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,396 +0,0 @@
-use mio;
-use std::{collections::HashMap, io, io::Write};
-
-use super::{
-    actions::{Destination, DestinationGroup},
-    core::HWServer,
-    coretypes::{ClientId, Replay, RoomId},
-    room::RoomSave,
-};
-use crate::{
-    protocol::messages::{server_chat, HWProtocolMessage, HWServerMessage, HWServerMessage::*},
-    server::actions::PendingMessage,
-    utils,
-};
-use base64::encode;
-use log::*;
-use rand::{thread_rng, RngCore};
-
-mod checker;
-mod common;
-mod inroom;
-mod lobby;
-mod loggingin;
-
-use self::loggingin::LoginResult;
-use crate::protocol::messages::global_chat;
-use crate::protocol::messages::HWProtocolMessage::EngineMessage;
-use crate::server::coretypes::{GameCfg, TeamInfo};
-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,
-    },
-    GetReplay {
-        id: u32,
-    },
-    SaveRoom {
-        room_id: RoomId,
-        filename: String,
-        contents: String,
-    },
-    LoadRoom {
-        room_id: RoomId,
-        filename: String,
-    },
-}
-
-pub enum IoResult {
-    Account(Option<AccountInfo>),
-    Replay(Option<Replay>),
-    SaveRoom(RoomId, bool),
-    LoadRoom(RoomId, Option<String>),
-}
-
-pub struct Response {
-    client_id: ClientId,
-    messages: Vec<PendingMessage>,
-    io_tasks: Vec<IoTask>,
-    removed_clients: Vec<ClientId>,
-}
-
-impl Response {
-    pub fn new(client_id: ClientId) -> Self {
-        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.io_tasks.is_empty()
-    }
-
-    #[inline]
-    pub fn len(&self) -> usize {
-        self.messages.len()
-    }
-
-    #[inline]
-    pub fn client_id(&self) -> ClientId {
-        self.client_id
-    }
-
-    #[inline]
-    pub fn add(&mut self, message: PendingMessage) {
-        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,
-    ) -> impl Iterator<Item = (Vec<ClientId>, HWServerMessage)> + 'a {
-        let client_id = self.client_id;
-        self.messages.drain(..).map(move |m| {
-            let ids = get_recipients(server, client_id, m.destination);
-            (ids, m.message)
-        })
-    }
-
-    pub fn remove_client(&mut self, client_id: ClientId) {
-        self.removed_clients.push(client_id);
-    }
-
-    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 {
-    fn extend<T: IntoIterator<Item = PendingMessage>>(&mut self, iter: T) {
-        for msg in iter {
-            self.add(msg)
-        }
-    }
-}
-
-fn get_recipients(
-    server: &HWServer,
-    client_id: ClientId,
-    destination: Destination,
-) -> Vec<ClientId> {
-    match destination {
-        Destination::ToSelf => vec![client_id],
-        Destination::ToId(id) => vec![id],
-        Destination::ToIds(ids) => ids,
-        Destination::ToAll { group, skip_self } => {
-            let mut ids: Vec<_> = match group {
-                DestinationGroup::All => server.all_clients().collect(),
-                DestinationGroup::Lobby => server.lobby_clients().collect(),
-                DestinationGroup::Protocol(proto) => server.protocol_clients(proto).collect(),
-                DestinationGroup::Room(id) => server.room_clients(id).collect(),
-            };
-
-            if skip_self {
-                if let Some(index) = ids.iter().position(|id| *id == client_id) {
-                    ids.remove(index);
-                }
-            }
-
-            ids
-        }
-    }
-}
-
-pub fn handle(
-    server: &mut HWServer,
-    client_id: ClientId,
-    response: &mut Response,
-    message: HWProtocolMessage,
-) {
-    match message {
-        HWProtocolMessage::Ping => response.add(Pong.send_self()),
-        _ => {
-            if server.anteroom.clients.contains(client_id) {
-                match loggingin::handle(server, client_id, response, message) {
-                    LoginResult::Unchanged => (),
-                    LoginResult::Complete => {
-                        if let Some(client) = server.anteroom.remove_client(client_id) {
-                            server.add_client(client_id, client);
-                            common::join_lobby(server, response);
-                        }
-                    }
-                    LoginResult::Exit => {
-                        server.anteroom.remove_client(client_id);
-                        response.remove_client(client_id);
-                    }
-                }
-            } else if server.clients.contains(client_id) {
-                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());
-                    }
-                    HWProtocolMessage::Info(nick) => {
-                        if let Some(client) = server.find_client(&nick) {
-                            let admin_sign = if client.is_admin() { "@" } else { "" };
-                            let master_sign = if client.is_master() { "+" } else { "" };
-                            let room_info = match client.room_id {
-                                Some(room_id) => {
-                                    let room = &server.rooms[room_id];
-                                    let status = match room.game_info {
-                                        Some(_) if client.teams_in_game == 0 => "(spectating)",
-                                        Some(_) => "(playing)",
-                                        None => "",
-                                    };
-                                    format!(
-                                        "[{}{}room {}]{}",
-                                        admin_sign, master_sign, room.name, status
-                                    )
-                                }
-                                None => format!("[{}lobby]", admin_sign),
-                            };
-
-                            let info = vec![
-                                client.nick.clone(),
-                                "[]".to_string(),
-                                utils::protocol_version_string(client.protocol_number).to_string(),
-                                room_info,
-                            ];
-                            response.add(Info(info).send_self())
-                        } else {
-                            response
-                                .add(server_chat("Player is not online.".to_string()).send_self())
-                        }
-                    }
-                    HWProtocolMessage::ToggleServerRegisteredOnly => {
-                        if !server.clients[client_id].is_admin() {
-                            response.add(Warning("Access denied.".to_string()).send_self());
-                        } else {
-                            server.set_is_registered_only(server.is_registered_only());
-                            let msg = if server.is_registered_only() {
-                                "This server no longer allows unregistered players to join."
-                            } else {
-                                "This server now allows unregistered players to join."
-                            };
-                            response.add(server_chat(msg.to_string()).send_all());
-                        }
-                    }
-                    HWProtocolMessage::Global(msg) => {
-                        if !server.clients[client_id].is_admin() {
-                            response.add(Warning("Access denied.".to_string()).send_self());
-                        } else {
-                            response.add(global_chat(msg).send_all())
-                        }
-                    }
-                    HWProtocolMessage::SuperPower => {
-                        if !server.clients[client_id].is_admin() {
-                            response.add(Warning("Access denied.".to_string()).send_self());
-                        } else {
-                            server.clients[client_id].set_has_super_power(true);
-                            response
-                                .add(server_chat("Super power activated.".to_string()).send_self())
-                        }
-                    }
-                    HWProtocolMessage::Watch(id) => {
-                        #[cfg(feature = "official-server")]
-                        {
-                            response.request_io(IoTask::GetReplay { id })
-                        }
-
-                        #[cfg(not(feature = "official-server"))]
-                        {
-                            response.add(
-                                Warning("This server does not support replays!".to_string())
-                                    .send_self(),
-                            );
-                        }
-                    }
-                    _ => 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)
-                        }
-                    },
-                }
-            }
-        }
-    }
-}
-
-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::SERVER_VERSION).send_self());
-}
-
-pub fn handle_client_loss(server: &mut HWServer, client_id: ClientId, response: &mut Response) {
-    if server.anteroom.remove_client(client_id).is_none() {
-        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)) => {
-            if !info.is_registered && server.is_registered_only() {
-                response.add(
-                    Bye("This server only allows registered users to join.".to_string())
-                        .send_self(),
-                );
-                response.remove_client(client_id);
-            } else {
-                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_registered(info.is_registered);
-                    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);
-        }
-        IoResult::Replay(Some(replay)) => {
-            let protocol = server.clients[client_id].protocol_number;
-            let start_msg = if protocol < 58 {
-                RoomJoined(vec![server.clients[client_id].nick.clone()])
-            } else {
-                ReplayStart
-            };
-            response.add(start_msg.send_self());
-
-            common::get_room_config_impl(&replay.config, client_id, response);
-            common::get_teams(replay.teams.iter(), client_id, response);
-            response.add(RunGame.send_self());
-            response.add(ForwardEngineMessage(replay.message_log).send_self());
-
-            if protocol < 58 {
-                response.add(Kicked.send_self());
-            }
-        }
-        IoResult::Replay(None) => {
-            response.add(Warning("Could't load the replay".to_string()).send_self())
-        }
-        IoResult::SaveRoom(_, true) => {
-            response.add(server_chat("Room configs saved successfully.".to_string()).send_self());
-        }
-        IoResult::SaveRoom(_, false) => {
-            response.add(Warning("Unable to save the room configs.".to_string()).send_self());
-        }
-        IoResult::LoadRoom(room_id, Some(contents)) => {
-            if let Some(ref mut room) = server.rooms.get_mut(room_id) {
-                match room.set_saves(&contents) {
-                    Ok(_) => response.add(
-                        server_chat("Room configs loaded successfully.".to_string()).send_self(),
-                    ),
-                    Err(e) => {
-                        warn!("Error while deserializing the room configs: {}", e);
-                        response.add(
-                            Warning("Unable to deserialize the room configs.".to_string())
-                                .send_self(),
-                        );
-                    }
-                }
-            }
-        }
-        IoResult::LoadRoom(_, None) => {
-            response.add(Warning("Unable to load the room configs.".to_string()).send_self());
-        }
-    }
-}
--- a/rust/hedgewars-server/src/server/handlers/checker.rs	Tue May 28 17:49:04 2019 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,13 +0,0 @@
-use log::*;
-use mio;
-
-use crate::{
-    protocol::messages::HWProtocolMessage,
-    server::{core::HWServer, coretypes::ClientId},
-};
-
-pub fn handle(_server: &mut HWServer, _client_id: ClientId, message: HWProtocolMessage) {
-    match message {
-        _ => warn!("Unknown command"),
-    }
-}
--- a/rust/hedgewars-server/src/server/handlers/common.rs	Tue May 28 17:49:04 2019 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,661 +0,0 @@
-use crate::{
-    protocol::messages::server_chat,
-    protocol::messages::{
-        add_flags, remove_flags,
-        HWProtocolMessage::{self, Rnd},
-        HWServerMessage::{self, *},
-        ProtocolFlags as Flags,
-    },
-    server::{
-        client::HWClient,
-        core::HWServer,
-        coretypes::{ClientId, GameCfg, RoomId, TeamInfo, Vote, VoteType},
-        room::HWRoom,
-    },
-    utils::to_engine_msg,
-};
-
-use super::Response;
-
-use crate::server::coretypes::RoomConfig;
-use rand::{self, seq::SliceRandom, thread_rng, Rng};
-use std::{iter::once, mem::replace};
-
-pub fn rnd_reply(options: &[String]) -> HWServerMessage {
-    let mut rng = thread_rng();
-
-    let reply = if options.is_empty() {
-        (*&["heads", "tails"].choose(&mut rng).unwrap()).to_string()
-    } else {
-        options.choose(&mut rng).unwrap().clone()
-    };
-
-    ChatMsg {
-        nick: "[random]".to_string(),
-        msg: reply,
-    }
-}
-
-pub fn join_lobby(server: &mut HWServer, response: &mut Response) {
-    let client_id = response.client_id();
-
-    let client = &server.clients[client_id];
-    let nick = vec![client.nick.clone()];
-    let mut flags = vec![];
-    if client.is_registered() {
-        flags.push(Flags::Registered)
-    }
-    if client.is_admin() {
-        flags.push(Flags::Admin)
-    }
-    if client.is_contributor() {
-        flags.push(Flags::Contributor)
-    }
-
-    let all_nicks: Vec<_> = server.collect_nicks(|_| true);
-
-    let mut flag_selectors = [
-        (
-            Flags::Registered,
-            server.collect_nicks(|(_, c)| c.is_registered()),
-        ),
-        (Flags::Admin, server.collect_nicks(|(_, c)| c.is_admin())),
-        (
-            Flags::Contributor,
-            server.collect_nicks(|(_, c)| c.is_contributor()),
-        ),
-        (
-            Flags::InRoom,
-            server.collect_nicks(|(_, c)| c.room_id.is_some()),
-        ),
-    ];
-
-    let server_msg = ServerMessage(server.get_greetings(client_id).to_string());
-
-    let rooms_msg = Rooms(
-        server
-            .rooms
-            .iter()
-            .filter(|(_, r)| r.protocol_number == client.protocol_number)
-            .flat_map(|(_, r)| r.info(r.master_id.map(|id| &server.clients[id])))
-            .collect(),
-    );
-
-    response.add(LobbyJoined(nick).send_all().but_self());
-    response.add(
-        ClientFlags(add_flags(&flags), all_nicks.clone())
-            .send_all()
-            .but_self(),
-    );
-
-    response.add(LobbyJoined(all_nicks).send_self());
-    for (flag, nicks) in &mut flag_selectors {
-        if !nicks.is_empty() {
-            response.add(ClientFlags(add_flags(&[*flag]), replace(nicks, vec![])).send_self());
-        }
-    }
-
-    response.add(server_msg.send_self());
-    response.add(rooms_msg.send_self());
-}
-
-pub fn remove_teams(
-    room: &mut HWRoom,
-    team_names: Vec<String>,
-    is_in_game: bool,
-    response: &mut Response,
-) {
-    if let Some(ref mut info) = room.game_info {
-        for team_name in &team_names {
-            info.left_teams.push(team_name.clone());
-
-            if is_in_game {
-                let msg = once(b'F').chain(team_name.bytes());
-                response.add(
-                    ForwardEngineMessage(vec![to_engine_msg(msg)])
-                        .send_all()
-                        .in_room(room.id)
-                        .but_self(),
-                );
-
-                info.teams_in_game -= 1;
-
-                let remove_msg = to_engine_msg(once(b'F').chain(team_name.bytes()));
-                if let Some(m) = &info.sync_msg {
-                    info.msg_log.push(m.clone());
-                    info.sync_msg = None
-                }
-                info.msg_log.push(remove_msg.clone());
-
-                response.add(
-                    ForwardEngineMessage(vec![remove_msg])
-                        .send_all()
-                        .in_room(room.id)
-                        .but_self(),
-                );
-            }
-        }
-    }
-
-    for team_name in team_names {
-        room.remove_team(&team_name);
-        response.add(TeamRemove(team_name).send_all().in_room(room.id));
-    }
-}
-
-fn remove_client_from_room(
-    client: &mut HWClient,
-    room: &mut HWRoom,
-    response: &mut Response,
-    msg: &str,
-) {
-    room.players_number -= 1;
-    if room.players_number > 0 || room.is_fixed() {
-        if client.is_ready() && room.ready_players_number > 0 {
-            room.ready_players_number -= 1;
-        }
-
-        let team_names: Vec<_> = room
-            .client_teams(client.id)
-            .map(|t| t.name.clone())
-            .collect();
-        remove_teams(room, team_names, client.is_in_game(), response);
-
-        if room.players_number > 0 {
-            response.add(
-                RoomLeft(client.nick.clone(), msg.to_string())
-                    .send_all()
-                    .in_room(room.id)
-                    .but_self(),
-            );
-        }
-
-        if client.is_master() && !room.is_fixed() {
-            client.set_is_master(false);
-            response.add(
-                ClientFlags(
-                    remove_flags(&[Flags::RoomMaster]),
-                    vec![client.nick.clone()],
-                )
-                .send_all()
-                .in_room(room.id),
-            );
-            room.master_id = None;
-        }
-    }
-
-    client.room_id = None;
-
-    let update_msg = if room.players_number == 0 && !room.is_fixed() {
-        RoomRemove(room.name.clone())
-    } else {
-        RoomUpdated(room.name.clone(), room.info(Some(&client)))
-    };
-    response.add(update_msg.send_all().with_protocol(room.protocol_number));
-
-    response.add(ClientFlags(remove_flags(&[Flags::InRoom]), vec![client.nick.clone()]).send_all());
-}
-
-pub fn change_master(
-    server: &mut HWServer,
-    room_id: RoomId,
-    new_master_id: ClientId,
-    response: &mut Response,
-) {
-    let room = &mut server.rooms[room_id];
-    if let Some(master_id) = room.master_id {
-        server.clients[master_id].set_is_master(false);
-        response.add(
-            ClientFlags(
-                remove_flags(&[Flags::RoomMaster]),
-                vec![server.clients[master_id].nick.clone()],
-            )
-            .send_all()
-            .in_room(room_id),
-        )
-    }
-
-    room.master_id = Some(new_master_id);
-    server.clients[new_master_id].set_is_master(true);
-
-    response.add(
-        ClientFlags(
-            add_flags(&[Flags::RoomMaster]),
-            vec![server.clients[new_master_id].nick.clone()],
-        )
-        .send_all()
-        .in_room(room_id),
-    );
-}
-
-pub fn enter_room(
-    server: &mut HWServer,
-    client_id: ClientId,
-    room_id: RoomId,
-    response: &mut Response,
-) {
-    let nick = server.clients[client_id].nick.clone();
-    server.move_to_room(client_id, room_id);
-
-    response.add(RoomJoined(vec![nick.clone()]).send_all().in_room(room_id));
-    response.add(ClientFlags(add_flags(&[Flags::InRoom]), vec![nick]).send_all());
-    let nicks = server.collect_nicks(|(_, c)| c.room_id == Some(room_id));
-    response.add(RoomJoined(nicks).send_self());
-
-    get_room_teams(server, room_id, client_id, response);
-
-    let room = &server.rooms[room_id];
-    get_room_config(room, client_id, response);
-
-    let mut flag_selectors = [
-        (
-            Flags::RoomMaster,
-            server.collect_nicks(|(_, c)| c.is_master()),
-        ),
-        (Flags::Ready, server.collect_nicks(|(_, c)| c.is_ready())),
-        (Flags::InGame, server.collect_nicks(|(_, c)| c.is_in_game())),
-    ];
-
-    for (flag, nicks) in &mut flag_selectors {
-        response.add(ClientFlags(add_flags(&[*flag]), replace(nicks, vec![])).send_self());
-    }
-
-    if !room.greeting.is_empty() {
-        response.add(
-            ChatMsg {
-                nick: "[greeting]".to_string(),
-                msg: room.greeting.clone(),
-            }
-            .send_self(),
-        );
-    }
-}
-
-pub fn exit_room(server: &mut HWServer, client_id: ClientId, response: &mut Response, msg: &str) {
-    let client = &mut server.clients[client_id];
-
-    if let Some(room_id) = client.room_id {
-        let room = &mut server.rooms[room_id];
-
-        remove_client_from_room(client, room, response, msg);
-
-        if !room.is_fixed() {
-            if room.players_number == 0 {
-                server.rooms.remove(room_id);
-            } else if room.master_id == None {
-                let new_master_id = server.room_clients(room_id).next();
-                if let Some(new_master_id) = new_master_id {
-                    let new_master_nick = server.clients[new_master_id].nick.clone();
-                    let room = &mut server.rooms[room_id];
-                    room.master_id = Some(new_master_id);
-                    server.clients[new_master_id].set_is_master(true);
-
-                    if room.protocol_number < 42 {
-                        room.name = new_master_nick.clone();
-                    }
-
-                    room.set_join_restriction(false);
-                    room.set_team_add_restriction(false);
-                    room.set_unregistered_players_restriction(true);
-
-                    response.add(
-                        ClientFlags(add_flags(&[Flags::RoomMaster]), vec![new_master_nick])
-                            .send_all()
-                            .in_room(room.id),
-                    );
-                }
-            }
-        }
-    }
-}
-
-pub fn remove_client(server: &mut HWServer, response: &mut Response, msg: String) {
-    let client_id = response.client_id();
-    let client = &mut server.clients[client_id];
-    let nick = client.nick.clone();
-
-    exit_room(server, client_id, response, &msg);
-
-    server.remove_client(client_id);
-
-    response.add(LobbyLeft(nick, msg.to_string()).send_all());
-    response.add(Bye("User quit: ".to_string() + &msg).send_self());
-    response.remove_client(client_id);
-}
-
-pub fn get_room_update(
-    room_name: Option<String>,
-    room: &HWRoom,
-    master: Option<&HWClient>,
-    response: &mut Response,
-) {
-    let update_msg = RoomUpdated(room_name.unwrap_or(room.name.clone()), room.info(master));
-    response.add(update_msg.send_all().with_protocol(room.protocol_number));
-}
-
-pub fn get_room_config_impl(config: &RoomConfig, to_client: ClientId, response: &mut Response) {
-    response.add(ConfigEntry("FULLMAPCONFIG".to_string(), config.to_map_config()).send(to_client));
-    for cfg in config.to_game_config() {
-        response.add(cfg.to_server_msg().send(to_client));
-    }
-}
-
-pub fn get_room_config(room: &HWRoom, to_client: ClientId, response: &mut Response) {
-    get_room_config_impl(room.active_config(), to_client, response);
-}
-
-pub fn get_teams<'a, I>(teams: I, to_client: ClientId, response: &mut Response)
-where
-    I: Iterator<Item = &'a TeamInfo>,
-{
-    for team in teams {
-        response.add(TeamAdd(team.to_protocol()).send(to_client));
-        response.add(TeamColor(team.name.clone(), team.color).send(to_client));
-        response.add(HedgehogsNumber(team.name.clone(), team.hedgehogs_number).send(to_client));
-    }
-}
-
-pub fn get_room_teams(
-    server: &HWServer,
-    room_id: RoomId,
-    to_client: ClientId,
-    response: &mut Response,
-) {
-    let room = &server.rooms[room_id];
-    let current_teams = match room.game_info {
-        Some(ref info) => &info.teams_at_start,
-        None => &room.teams,
-    };
-
-    get_teams(current_teams.iter().map(|(_, t)| t), to_client, response);
-}
-
-pub fn get_room_flags(
-    server: &HWServer,
-    room_id: RoomId,
-    to_client: ClientId,
-    response: &mut Response,
-) {
-    let room = &server.rooms[room_id];
-    if let Some(id) = room.master_id {
-        response.add(
-            ClientFlags(
-                add_flags(&[Flags::RoomMaster]),
-                vec![server.clients[id].nick.clone()],
-            )
-            .send(to_client),
-        );
-    }
-    let nicks: Vec<_> = server
-        .clients
-        .iter()
-        .filter(|(_, c)| c.room_id == Some(room_id) && c.is_ready())
-        .map(|(_, c)| c.nick.clone())
-        .collect();
-    if !nicks.is_empty() {
-        response.add(ClientFlags(add_flags(&[Flags::Ready]), nicks).send(to_client));
-    }
-}
-
-pub fn apply_voting_result(
-    server: &mut HWServer,
-    room_id: RoomId,
-    response: &mut Response,
-    kind: VoteType,
-) {
-    match kind {
-        VoteType::Kick(nick) => {
-            if let Some(client) = server.find_client(&nick) {
-                if client.room_id == Some(room_id) {
-                    let id = client.id;
-                    response.add(Kicked.send(id));
-                    exit_room(server, id, response, "kicked");
-                }
-            }
-        }
-        VoteType::Map(None) => (),
-        VoteType::Map(Some(name)) => {
-            if let Some(location) = server.rooms[room_id].load_config(&name) {
-                response.add(
-                    server_chat(location.to_string())
-                        .send_all()
-                        .in_room(room_id),
-                );
-                let room = &server.rooms[room_id];
-                let room_master = if let Some(id) = room.master_id {
-                    Some(&server.clients[id])
-                } else {
-                    None
-                };
-                get_room_update(None, room, room_master, response);
-
-                for (_, client) in server.clients.iter() {
-                    if client.room_id == Some(room_id) {
-                        super::common::get_room_config(&server.rooms[room_id], client.id, response);
-                    }
-                }
-            }
-        }
-        VoteType::Pause => {
-            if let Some(ref mut info) = server.rooms[room_id].game_info {
-                info.is_paused = !info.is_paused;
-                response.add(
-                    server_chat("Pause toggled.".to_string())
-                        .send_all()
-                        .in_room(room_id),
-                );
-                response.add(
-                    ForwardEngineMessage(vec![to_engine_msg(once(b'I'))])
-                        .send_all()
-                        .in_room(room_id),
-                );
-            }
-        }
-        VoteType::NewSeed => {
-            let seed = thread_rng().gen_range(0, 1_000_000_000).to_string();
-            let cfg = GameCfg::Seed(seed);
-            response.add(cfg.to_server_msg().send_all().in_room(room_id));
-            server.rooms[room_id].set_config(cfg);
-        }
-        VoteType::HedgehogsPerTeam(number) => {
-            let r = &mut server.rooms[room_id];
-            let nicks = r.set_hedgehogs_number(number);
-
-            response.extend(
-                nicks
-                    .into_iter()
-                    .map(|n| HedgehogsNumber(n, number).send_all().in_room(room_id)),
-            );
-        }
-    }
-}
-
-fn add_vote(room: &mut HWRoom, response: &mut Response, vote: Vote) -> Option<bool> {
-    let client_id = response.client_id;
-    let mut result = None;
-
-    if let Some(ref mut voting) = room.voting {
-        if vote.is_forced || voting.votes.iter().all(|(id, _)| client_id != *id) {
-            response.add(server_chat("Your vote has been counted.".to_string()).send_self());
-            voting.votes.push((client_id, vote.is_pro));
-            let i = voting.votes.iter();
-            let pro = i.clone().filter(|(_, v)| *v).count();
-            let contra = i.filter(|(_, v)| !*v).count();
-            let success_quota = voting.voters.len() / 2 + 1;
-            if vote.is_forced && vote.is_pro || pro >= success_quota {
-                result = Some(true);
-            } else if vote.is_forced && !vote.is_pro || contra > voting.voters.len() - success_quota
-            {
-                result = Some(false);
-            }
-        } else {
-            response.add(server_chat("You already have voted.".to_string()).send_self());
-        }
-    } else {
-        response.add(server_chat("There's no voting going on.".to_string()).send_self());
-    }
-
-    result
-}
-
-pub fn submit_vote(server: &mut HWServer, vote: Vote, response: &mut Response) {
-    let client_id = response.client_id;
-    let client = &server.clients[client_id];
-
-    if let Some(room_id) = client.room_id {
-        let room = &mut server.rooms[room_id];
-
-        if let Some(res) = add_vote(room, response, vote) {
-            response.add(
-                server_chat("Voting closed.".to_string())
-                    .send_all()
-                    .in_room(room.id),
-            );
-            let voting = replace(&mut room.voting, None).unwrap();
-            if res {
-                apply_voting_result(server, room_id, response, voting.kind);
-            }
-        }
-    }
-}
-
-pub fn start_game(server: &mut HWServer, room_id: RoomId, response: &mut Response) {
-    let (room_clients, room_nicks): (Vec<_>, Vec<_>) = server
-        .clients
-        .iter()
-        .map(|(id, c)| (id, c.nick.clone()))
-        .unzip();
-    let room = &mut server.rooms[room_id];
-
-    if !room.has_multiple_clans() {
-        response.add(
-            Warning("The game can't be started with less than two clans!".to_string()).send_self(),
-        );
-    } else if room.protocol_number <= 43 && room.players_number != room.ready_players_number {
-        response.add(Warning("Not all players are ready".to_string()).send_self());
-    } else if room.game_info.is_some() {
-        response.add(Warning("The game is already in progress".to_string()).send_self());
-    } else {
-        room.start_round();
-        for id in room_clients {
-            let c = &mut server.clients[id];
-            c.set_is_in_game(true);
-            c.team_indices = room.client_team_indices(c.id);
-        }
-        response.add(RunGame.send_all().in_room(room.id));
-        response.add(
-            ClientFlags(add_flags(&[Flags::InGame]), room_nicks)
-                .send_all()
-                .in_room(room.id),
-        );
-
-        let room_master = if let Some(id) = room.master_id {
-            Some(&server.clients[id])
-        } else {
-            None
-        };
-        get_room_update(None, room, room_master, response);
-    }
-}
-
-pub fn end_game(server: &mut HWServer, room_id: RoomId, response: &mut Response) {
-    let room = &mut server.rooms[room_id];
-    room.ready_players_number = 1;
-    let room_master = if let Some(id) = room.master_id {
-        Some(&server.clients[id])
-    } else {
-        None
-    };
-    get_room_update(None, room, room_master, response);
-    response.add(RoundFinished.send_all().in_room(room_id));
-
-    if let Some(info) = replace(&mut room.game_info, None) {
-        for (_, client) in server.clients.iter() {
-            if client.room_id == Some(room_id) && client.is_joined_mid_game() {
-                super::common::get_room_config(room, client.id, response);
-                response.extend(
-                    info.left_teams
-                        .iter()
-                        .map(|name| TeamRemove(name.clone()).send(client.id)),
-                );
-            }
-        }
-    }
-
-    let nicks: Vec<_> = server
-        .clients
-        .iter_mut()
-        .filter(|(_, c)| c.room_id == Some(room_id))
-        .map(|(_, c)| {
-            c.set_is_ready(c.is_master());
-            c.set_is_joined_mid_game(false);
-            c
-        })
-        .filter_map(|c| {
-            if !c.is_master() {
-                Some(c.nick.clone())
-            } else {
-                None
-            }
-        })
-        .collect();
-
-    if !nicks.is_empty() {
-        let msg = if room.protocol_number < 38 {
-            LegacyReady(false, nicks)
-        } else {
-            ClientFlags(remove_flags(&[Flags::Ready]), nicks)
-        };
-        response.add(msg.send_all().in_room(room_id));
-    }
-}
-
-#[cfg(test)]
-mod tests {
-    use super::*;
-    use crate::protocol::messages::HWServerMessage::ChatMsg;
-    use crate::server::actions::PendingMessage;
-
-    fn reply2string(r: HWServerMessage) -> String {
-        match r {
-            ChatMsg { msg: p, .. } => String::from(p),
-            _ => panic!("expected a ChatMsg"),
-        }
-    }
-
-    fn run_handle_test(opts: Vec<String>) {
-        let opts2 = opts.clone();
-        for opt in opts {
-            while reply2string(rnd_reply(&opts2)) != opt {}
-        }
-    }
-
-    /// This test terminates almost surely.
-    #[test]
-    fn test_handle_rnd_empty() {
-        run_handle_test(vec![])
-    }
-
-    /// This test terminates almost surely.
-    #[test]
-    fn test_handle_rnd_nonempty() {
-        run_handle_test(vec!["A".to_owned(), "B".to_owned(), "C".to_owned()])
-    }
-
-    /// This test terminates almost surely (strong law of large numbers)
-    #[test]
-    fn test_distribution() {
-        let eps = 0.000001;
-        let lim = 0.5;
-        let opts = vec![0.to_string(), 1.to_string()];
-        let mut ones = 0;
-        let mut tries = 0;
-
-        while tries < 1000 || ((ones as f64 / tries as f64) - lim).abs() >= eps {
-            tries += 1;
-            if reply2string(rnd_reply(&opts)) == 1.to_string() {
-                ones += 1;
-            }
-        }
-    }
-}
--- a/rust/hedgewars-server/src/server/handlers/inroom.rs	Tue May 28 17:49:04 2019 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,615 +0,0 @@
-use mio;
-
-use super::common::rnd_reply;
-use crate::utils::to_engine_msg;
-use crate::{
-    protocol::messages::{
-        add_flags, remove_flags, server_chat, HWProtocolMessage, HWServerMessage::*,
-        ProtocolFlags as Flags,
-    },
-    server::{
-        core::HWServer,
-        coretypes,
-        coretypes::{ClientId, GameCfg, RoomId, VoteType, Voting, MAX_HEDGEHOGS_PER_TEAM},
-        room::{HWRoom, RoomFlags, MAX_TEAMS_IN_ROOM},
-    },
-    utils::is_name_illegal,
-};
-use base64::{decode, encode};
-use log::*;
-use std::{cmp::min, iter::once, mem::swap};
-
-#[derive(Clone)]
-struct ByMsg<'a> {
-    messages: &'a [u8],
-}
-
-impl<'a> Iterator for ByMsg<'a> {
-    type Item = &'a [u8];
-
-    fn next(&mut self) -> Option<<Self as Iterator>::Item> {
-        if let Some(size) = self.messages.get(0) {
-            let (msg, next) = self.messages.split_at(*size as usize + 1);
-            self.messages = next;
-            Some(msg)
-        } else {
-            None
-        }
-    }
-}
-
-fn by_msg(source: &[u8]) -> ByMsg {
-    ByMsg { messages: source }
-}
-
-const VALID_MESSAGES: &[u8] =
-    b"M#+LlRrUuDdZzAaSjJ,NpPwtgfhbc12345\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8A";
-const NON_TIMED_MESSAGES: &[u8] = b"M#hb";
-
-#[cfg(canhazslicepatterns)]
-fn is_msg_valid(msg: &[u8], team_indices: &[u8]) -> bool {
-    match msg {
-        [size, typ, body..] => {
-            VALID_MESSAGES.contains(typ)
-                && match body {
-                    [1...MAX_HEDGEHOGS_PER_TEAM, team, ..] if *typ == b'h' => {
-                        team_indices.contains(team)
-                    }
-                    _ => *typ != b'h',
-                }
-        }
-        _ => false,
-    }
-}
-
-fn is_msg_valid(msg: &[u8], _team_indices: &[u8]) -> bool {
-    if let Some(typ) = msg.get(1) {
-        VALID_MESSAGES.contains(typ)
-    } else {
-        false
-    }
-}
-
-fn is_msg_empty(msg: &[u8]) -> bool {
-    msg.get(1).filter(|t| **t == b'+').is_some()
-}
-
-fn is_msg_timed(msg: &[u8]) -> bool {
-    msg.get(1)
-        .filter(|t| !NON_TIMED_MESSAGES.contains(t))
-        .is_some()
-}
-
-fn voting_description(kind: &VoteType) -> String {
-    format!(
-        "New voting started: {}",
-        match kind {
-            VoteType::Kick(nick) => format!("kick {}", nick),
-            VoteType::Map(name) => format!("map {}", name.as_ref().unwrap()),
-            VoteType::Pause => "pause".to_string(),
-            VoteType::NewSeed => "new seed".to_string(),
-            VoteType::HedgehogsPerTeam(number) => format!("hedgehogs per team: {}", number),
-        }
-    )
-}
-
-fn room_message_flag(msg: &HWProtocolMessage) -> RoomFlags {
-    use crate::protocol::messages::HWProtocolMessage::*;
-    match msg {
-        ToggleRestrictJoin => RoomFlags::RESTRICTED_JOIN,
-        ToggleRestrictTeams => RoomFlags::RESTRICTED_TEAM_ADD,
-        ToggleRegisteredOnly => RoomFlags::RESTRICTED_UNREGISTERED_PLAYERS,
-        _ => RoomFlags::empty(),
-    }
-}
-
-pub fn handle(
-    server: &mut HWServer,
-    client_id: ClientId,
-    response: &mut super::Response,
-    room_id: RoomId,
-    message: HWProtocolMessage,
-) {
-    let client = &mut server.clients[client_id];
-    let room = &mut server.rooms[room_id];
-
-    use crate::protocol::messages::HWProtocolMessage::*;
-    match message {
-        Part(msg) => {
-            let msg = match msg {
-                Some(s) => format!("part: {}", s),
-                None => "part".to_string(),
-            };
-            super::common::exit_room(server, client_id, response, &msg);
-        }
-        Chat(msg) => {
-            response.add(
-                ChatMsg {
-                    nick: client.nick.clone(),
-                    msg,
-                }
-                .send_all()
-                .in_room(room_id),
-            );
-        }
-        TeamChat(msg) => {
-            let room = &server.rooms[room_id];
-            if let Some(ref info) = room.game_info {
-                if let Some(clan_color) = room.find_team_color(client_id) {
-                    let client = &server.clients[client_id];
-                    let engine_msg =
-                        to_engine_msg(format!("b{}]{}\x20\x20", client.nick, msg).bytes());
-                    let team = room.clan_team_owners(clan_color).collect();
-                    response.add(ForwardEngineMessage(vec![engine_msg]).send_many(team))
-                }
-            }
-        }
-        Fix => {
-            if client.is_admin() {
-                room.set_is_fixed(true);
-                room.set_join_restriction(false);
-                room.set_team_add_restriction(false);
-                room.set_unregistered_players_restriction(true);
-            }
-        }
-        Unfix => {
-            if client.is_admin() {
-                room.set_is_fixed(false);
-            }
-        }
-        Greeting(text) => {
-            if client.is_admin() || client.is_master() && !room.is_fixed() {
-                room.greeting = text;
-            }
-        }
-        MaxTeams(count) => {
-            if !client.is_master() {
-                response.add(Warning("You're not the room master!".to_string()).send_self());
-            } else if !(2..=MAX_TEAMS_IN_ROOM).contains(&count) {
-                response
-                    .add(Warning("/maxteams: specify number from 2 to 8".to_string()).send_self());
-            } else {
-                server.rooms[room_id].max_teams = count;
-            }
-        }
-        RoomName(new_name) => {
-            if is_name_illegal(&new_name) {
-                response.add(Warning("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: $()*+?[]^{|}".to_string()).send_self());
-            } else if server.has_room(&new_name) {
-                response.add(
-                    Warning("A room with the same name already exists.".to_string()).send_self(),
-                );
-            } else {
-                let room = &mut server.rooms[room_id];
-                if room.is_fixed() || room.master_id != Some(client_id) {
-                    response.add(Warning("Access denied.".to_string()).send_self());
-                } else {
-                    let mut old_name = new_name.clone();
-                    let client = &server.clients[client_id];
-                    swap(&mut room.name, &mut old_name);
-                    super::common::get_room_update(Some(old_name), room, Some(&client), response);
-                }
-            }
-        }
-        ToggleReady => {
-            let flags = if client.is_ready() {
-                room.ready_players_number -= 1;
-                remove_flags(&[Flags::Ready])
-            } else {
-                room.ready_players_number += 1;
-                add_flags(&[Flags::Ready])
-            };
-
-            let msg = if client.protocol_number < 38 {
-                LegacyReady(client.is_ready(), vec![client.nick.clone()])
-            } else {
-                ClientFlags(flags, vec![client.nick.clone()])
-            };
-            response.add(msg.send_all().in_room(room.id));
-            client.set_is_ready(!client.is_ready());
-
-            if room.is_fixed() && room.ready_players_number == room.players_number {
-                super::common::start_game(server, room_id, response);
-            }
-        }
-        AddTeam(mut info) => {
-            if room.teams.len() >= room.max_teams as usize {
-                response.add(Warning("Too many teams!".to_string()).send_self());
-            } else if room.addable_hedgehogs() == 0 {
-                response.add(Warning("Too many hedgehogs!".to_string()).send_self());
-            } else if room.find_team(|t| t.name == info.name) != None {
-                response.add(
-                    Warning("There's already a team with same name in the list.".to_string())
-                        .send_self(),
-                );
-            } else if room.game_info.is_some() {
-                response.add(
-                    Warning("Joining not possible: Round is in progress.".to_string()).send_self(),
-                );
-            } else if room.is_team_add_restricted() {
-                response.add(
-                    Warning("This room currently does not allow adding new teams.".to_string())
-                        .send_self(),
-                );
-            } else {
-                info.owner = client.nick.clone();
-                let team = room.add_team(client.id, *info, client.protocol_number < 42);
-                client.teams_in_game += 1;
-                client.clan = Some(team.color);
-                response.add(TeamAccepted(team.name.clone()).send_self());
-                response.add(
-                    TeamAdd(team.to_protocol())
-                        .send_all()
-                        .in_room(room_id)
-                        .but_self(),
-                );
-                response.add(
-                    TeamColor(team.name.clone(), team.color)
-                        .send_all()
-                        .in_room(room_id),
-                );
-                response.add(
-                    HedgehogsNumber(team.name.clone(), team.hedgehogs_number)
-                        .send_all()
-                        .in_room(room_id),
-                );
-
-                let room_master = if let Some(id) = room.master_id {
-                    Some(&server.clients[id])
-                } else {
-                    None
-                };
-                super::common::get_room_update(None, room, room_master, response);
-            }
-        }
-        RemoveTeam(name) => match room.find_team_owner(&name) {
-            None => response.add(
-                Warning("Error: The team you tried to remove does not exist.".to_string())
-                    .send_self(),
-            ),
-            Some((id, _)) if id != client_id => response
-                .add(Warning("You can't remove a team you don't own.".to_string()).send_self()),
-            Some((_, name)) => {
-                client.teams_in_game -= 1;
-                client.clan = room.find_team_color(client.id);
-                super::common::remove_teams(
-                    room,
-                    vec![name.to_string()],
-                    client.is_in_game(),
-                    response,
-                );
-
-                match room.game_info {
-                    Some(ref info) if info.teams_in_game == 0 => {
-                        super::common::end_game(server, room_id, response)
-                    }
-                    _ => (),
-                }
-            }
-        },
-        SetHedgehogsNumber(team_name, number) => {
-            let addable_hedgehogs = room.addable_hedgehogs();
-            if let Some((_, team)) = room.find_team_and_owner_mut(|t| t.name == team_name) {
-                let max_hedgehogs = min(
-                    MAX_HEDGEHOGS_PER_TEAM,
-                    addable_hedgehogs + team.hedgehogs_number,
-                );
-                if !client.is_master() {
-                    response.add(Error("You're not the room master!".to_string()).send_self());
-                } else if !(1..=max_hedgehogs).contains(&number) {
-                    response
-                        .add(HedgehogsNumber(team.name.clone(), team.hedgehogs_number).send_self());
-                } else {
-                    team.hedgehogs_number = number;
-                    response.add(
-                        HedgehogsNumber(team.name.clone(), number)
-                            .send_all()
-                            .in_room(room_id)
-                            .but_self(),
-                    );
-                }
-            } else {
-                response.add(Warning("No such team.".to_string()).send_self());
-            }
-        }
-        SetTeamColor(team_name, color) => {
-            if let Some((owner, team)) = room.find_team_and_owner_mut(|t| t.name == team_name) {
-                if !client.is_master() {
-                    response.add(Error("You're not the room master!".to_string()).send_self());
-                } else {
-                    team.color = color;
-                    response.add(
-                        TeamColor(team.name.clone(), color)
-                            .send_all()
-                            .in_room(room_id)
-                            .but_self(),
-                    );
-                    server.clients[owner].clan = Some(color);
-                }
-            } else {
-                response.add(Warning("No such team.".to_string()).send_self());
-            }
-        }
-        Cfg(cfg) => {
-            if room.is_fixed() {
-                response.add(Warning("Access denied.".to_string()).send_self());
-            } else if !client.is_master() {
-                response.add(Error("You're not the room master!".to_string()).send_self());
-            } else {
-                let cfg = match cfg {
-                    GameCfg::Scheme(name, mut values) => {
-                        if client.protocol_number == 49 && values.len() >= 2 {
-                            let mut s = "X".repeat(50);
-                            s.push_str(&values.pop().unwrap());
-                            values.push(s);
-                        }
-                        GameCfg::Scheme(name, values)
-                    }
-                    cfg => cfg,
-                };
-
-                response.add(cfg.to_server_msg().send_all().in_room(room.id).but_self());
-                room.set_config(cfg);
-            }
-        }
-        Save(name, location) => {
-            response.add(
-                server_chat(format!("Room config saved as {}", name))
-                    .send_all()
-                    .in_room(room_id),
-            );
-            room.save_config(name, location);
-        }
-        #[cfg(feature = "official-server")]
-        SaveRoom(filename) => {
-            if client.is_admin() {
-                match room.get_saves() {
-                    Ok(contents) => response.request_io(super::IoTask::SaveRoom {
-                        room_id,
-                        filename,
-                        contents,
-                    }),
-                    Err(e) => {
-                        warn!("Error while serializing the room configs: {}", e);
-                        response.add(
-                            Warning("Unable to serialize the room configs.".to_string())
-                                .send_self(),
-                        )
-                    }
-                }
-            }
-        }
-        #[cfg(feature = "official-server")]
-        LoadRoom(filename) => {
-            if client.is_admin() {
-                response.request_io(super::IoTask::LoadRoom { room_id, filename });
-            }
-        }
-        Delete(name) => {
-            if !room.delete_config(&name) {
-                response.add(Warning(format!("Save doesn't exist: {}", name)).send_self());
-            } else {
-                response.add(
-                    server_chat(format!("Room config {} has been deleted", name))
-                        .send_all()
-                        .in_room(room_id),
-                );
-            }
-        }
-        CallVote(None) => {
-            response.add(server_chat("Available callvote commands: kick <nickname>, map <name>, pause, newseed, hedgehogs <number>".to_string())
-                .send_self());
-        }
-        CallVote(Some(kind)) => {
-            let is_in_game = room.game_info.is_some();
-            let error = match &kind {
-                VoteType::Kick(nick) => {
-                    if server
-                        .find_client(&nick)
-                        .filter(|c| c.room_id == Some(room_id))
-                        .is_some()
-                    {
-                        None
-                    } else {
-                        Some("/callvote kick: No such user!".to_string())
-                    }
-                }
-                VoteType::Map(None) => {
-                    let names: Vec<_> = server.rooms[room_id].saves.keys().cloned().collect();
-                    if names.is_empty() {
-                        Some("/callvote map: No maps saved in this room!".to_string())
-                    } else {
-                        Some(format!("Available maps: {}", names.join(", ")))
-                    }
-                }
-                VoteType::Map(Some(name)) => {
-                    if room.saves.get(&name[..]).is_some() {
-                        None
-                    } else {
-                        Some("/callvote map: No such map!".to_string())
-                    }
-                }
-                VoteType::Pause => {
-                    if is_in_game {
-                        None
-                    } else {
-                        Some("/callvote pause: No game in progress!".to_string())
-                    }
-                }
-                VoteType::NewSeed => None,
-                VoteType::HedgehogsPerTeam(number) => match number {
-                    1...MAX_HEDGEHOGS_PER_TEAM => None,
-                    _ => Some("/callvote hedgehogs: Specify number from 1 to 8.".to_string()),
-                },
-            };
-
-            match error {
-                None => {
-                    let msg = voting_description(&kind);
-                    let voting = Voting::new(kind, server.room_clients(client_id).collect());
-                    let room = &mut server.rooms[room_id];
-                    room.voting = Some(voting);
-                    response.add(server_chat(msg).send_all().in_room(room_id));
-                    super::common::submit_vote(
-                        server,
-                        coretypes::Vote {
-                            is_pro: true,
-                            is_forced: false,
-                        },
-                        response,
-                    );
-                }
-                Some(msg) => {
-                    response.add(server_chat(msg).send_self());
-                }
-            }
-        }
-        Vote(vote) => {
-            super::common::submit_vote(
-                server,
-                coretypes::Vote {
-                    is_pro: vote,
-                    is_forced: false,
-                },
-                response,
-            );
-        }
-        ForceVote(vote) => {
-            let is_forced = client.is_admin();
-            super::common::submit_vote(
-                server,
-                coretypes::Vote {
-                    is_pro: vote,
-                    is_forced,
-                },
-                response,
-            );
-        }
-        ToggleRestrictJoin | ToggleRestrictTeams | ToggleRegisteredOnly => {
-            if client.is_master() {
-                room.flags.toggle(room_message_flag(&message));
-                super::common::get_room_update(None, room, Some(&client), response);
-            }
-        }
-        StartGame => {
-            super::common::start_game(server, room_id, response);
-        }
-        EngineMessage(em) => {
-            if client.teams_in_game > 0 {
-                let decoding = decode(&em[..]).unwrap();
-                let messages = by_msg(&decoding);
-                let valid = messages.filter(|m| is_msg_valid(m, &client.team_indices));
-                let non_empty = valid.clone().filter(|m| !is_msg_empty(m));
-                let sync_msg = valid.clone().filter(|m| is_msg_timed(m)).last().map(|m| {
-                    if is_msg_empty(m) {
-                        Some(encode(m))
-                    } else {
-                        None
-                    }
-                });
-
-                let em_response = encode(&valid.flat_map(|msg| msg).cloned().collect::<Vec<_>>());
-                if !em_response.is_empty() {
-                    response.add(
-                        ForwardEngineMessage(vec![em_response])
-                            .send_all()
-                            .in_room(room.id)
-                            .but_self(),
-                    );
-                }
-                let em_log = encode(&non_empty.flat_map(|msg| msg).cloned().collect::<Vec<_>>());
-                if let Some(ref mut info) = room.game_info {
-                    if !em_log.is_empty() {
-                        info.msg_log.push(em_log);
-                    }
-                    if let Some(msg) = sync_msg {
-                        info.sync_msg = msg;
-                    }
-                }
-            }
-        }
-        RoundFinished => {
-            let mut game_ended = false;
-            if client.is_in_game() {
-                client.set_is_in_game(false);
-                response.add(
-                    ClientFlags(remove_flags(&[Flags::InGame]), vec![client.nick.clone()])
-                        .send_all()
-                        .in_room(room.id),
-                );
-                let team_names: Vec<_> = room
-                    .client_teams(client_id)
-                    .map(|t| t.name.clone())
-                    .collect();
-
-                if let Some(ref mut info) = room.game_info {
-                    info.teams_in_game -= team_names.len() as u8;
-                    if info.teams_in_game == 0 {
-                        game_ended = true;
-                    }
-
-                    for team_name in team_names {
-                        let msg = once(b'F').chain(team_name.bytes());
-                        response.add(
-                            ForwardEngineMessage(vec![to_engine_msg(msg)])
-                                .send_all()
-                                .in_room(room_id)
-                                .but_self(),
-                        );
-
-                        let remove_msg = to_engine_msg(once(b'F').chain(team_name.bytes()));
-                        if let Some(m) = &info.sync_msg {
-                            info.msg_log.push(m.clone());
-                        }
-                        if info.sync_msg.is_some() {
-                            info.sync_msg = None
-                        }
-                        info.msg_log.push(remove_msg.clone());
-                        response.add(
-                            ForwardEngineMessage(vec![remove_msg])
-                                .send_all()
-                                .in_room(room_id)
-                                .but_self(),
-                        );
-                    }
-                }
-            }
-            if game_ended {
-                super::common::end_game(server, room_id, response)
-            }
-        }
-        Rnd(v) => {
-            let result = rnd_reply(&v);
-            let mut echo = vec!["/rnd".to_string()];
-            echo.extend(v.into_iter());
-            let chat_msg = ChatMsg {
-                nick: server.clients[client_id].nick.clone(),
-                msg: echo.join(" "),
-            };
-            response.add(chat_msg.send_all().in_room(room_id));
-            response.add(result.send_all().in_room(room_id));
-        }
-        Delegate(nick) => {
-            let delegate_id = server.find_client(&nick).map(|c| (c.id, c.room_id));
-            let client = &server.clients[client_id];
-            if !(client.is_admin() || client.is_master()) {
-                response.add(
-                    Warning("You're not the room master or a server admin!".to_string())
-                        .send_self(),
-                )
-            } else {
-                match delegate_id {
-                    None => response.add(Warning("Player is not online.".to_string()).send_self()),
-                    Some((id, _)) if id == client_id => response
-                        .add(Warning("You're already the room master.".to_string()).send_self()),
-                    Some((_, id)) if id != Some(room_id) => response
-                        .add(Warning("The player is not in your room.".to_string()).send_self()),
-                    Some((id, _)) => {
-                        super::common::change_master(server, room_id, id, response);
-                    }
-                }
-            }
-        }
-        _ => warn!("Unimplemented!"),
-    }
-}
--- a/rust/hedgewars-server/src/server/handlers/lobby.rs	Tue May 28 17:49:04 2019 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,164 +0,0 @@
-use mio;
-
-use super::common::rnd_reply;
-use crate::{
-    protocol::messages::{
-        add_flags, remove_flags, server_chat, HWProtocolMessage, HWServerMessage::*,
-        ProtocolFlags as Flags,
-    },
-    server::{
-        client::HWClient,
-        core::HWServer,
-        coretypes::{ClientId, ServerVar},
-    },
-    utils::is_name_illegal,
-};
-use log::*;
-use std::{collections::HashSet, convert::identity};
-
-pub fn handle(
-    server: &mut HWServer,
-    client_id: ClientId,
-    response: &mut super::Response,
-    message: HWProtocolMessage,
-) {
-    use crate::protocol::messages::HWProtocolMessage::*;
-    match message {
-        CreateRoom(name, password) => {
-            if is_name_illegal(&name) {
-                response.add(Warning("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: $()*+?[]^{|}".to_string()).send_self());
-            } else if server.has_room(&name) {
-                response.add(
-                    Warning("A room with the same name already exists.".to_string()).send_self(),
-                );
-            } else {
-                let flags_msg = ClientFlags(
-                    add_flags(&[Flags::RoomMaster, Flags::Ready]),
-                    vec![server.clients[client_id].nick.clone()],
-                );
-
-                let room_id = server.create_room(client_id, name, password);
-                let room = &server.rooms[room_id];
-                let client = &server.clients[client_id];
-
-                response.add(
-                    RoomAdd(room.info(Some(&client)))
-                        .send_all()
-                        .with_protocol(room.protocol_number),
-                );
-                response.add(RoomJoined(vec![client.nick.clone()]).send_self());
-                response.add(flags_msg.send_self());
-
-                response.add(
-                    ClientFlags(add_flags(&[Flags::InRoom]), vec![client.nick.clone()]).send_self(),
-                );
-            };
-        }
-        Chat(msg) => {
-            response.add(
-                ChatMsg {
-                    nick: server.clients[client_id].nick.clone(),
-                    msg,
-                }
-                .send_all()
-                .in_lobby()
-                .but_self(),
-            );
-        }
-        JoinRoom(name, _password) => {
-            let room = server.rooms.iter().find(|(_, r)| r.name == name);
-            let room_id = room.map(|(_, r)| r.id);
-
-            let client = &mut server.clients[client_id];
-
-            if let Some((_, room)) = room {
-                if client.protocol_number != room.protocol_number {
-                    response.add(
-                        Warning("Room version incompatible to your Hedgewars version!".to_string())
-                            .send_self(),
-                    );
-                } else if room.is_join_restricted() {
-                    response.add(
-                        Warning(
-                            "Access denied. This room currently doesn't allow joining.".to_string(),
-                        )
-                        .send_self(),
-                    );
-                } else if room.players_number == u8::max_value() {
-                    response.add(Warning("This room is already full".to_string()).send_self());
-                } else if let Some(room_id) = room_id {
-                    super::common::enter_room(server, client_id, room_id, response);
-                }
-            } else {
-                response.add(Warning("No such room.".to_string()).send_self());
-            }
-        }
-        Follow(nick) => {
-            if let Some(HWClient {
-                room_id: Some(room_id),
-                ..
-            }) = server.find_client(&nick)
-            {
-                let room = &server.rooms[*room_id];
-                response.add(Joining(room.name.clone()).send_self());
-                super::common::enter_room(server, client_id, *room_id, response);
-            }
-        }
-        SetServerVar(var) => {
-            if !server.clients[client_id].is_admin() {
-                response.add(Warning("Access denied.".to_string()).send_self());
-            } else {
-                match var {
-                    ServerVar::MOTDNew(msg) => server.greetings.for_latest_protocol = msg,
-                    ServerVar::MOTDOld(msg) => server.greetings.for_old_protocols = msg,
-                    ServerVar::LatestProto(n) => server.latest_protocol = n,
-                }
-            }
-        }
-        GetServerVar => {
-            if !server.clients[client_id].is_admin() {
-                response.add(Warning("Access denied.".to_string()).send_self());
-            } else {
-                let vars: Vec<_> = [
-                    ServerVar::MOTDNew(server.greetings.for_latest_protocol.clone()),
-                    ServerVar::MOTDOld(server.greetings.for_old_protocols.clone()),
-                    ServerVar::LatestProto(server.latest_protocol),
-                ]
-                .iter()
-                .flat_map(|v| v.to_protocol())
-                .collect();
-                response.add(ServerVars(vars).send_self());
-            }
-        }
-        Rnd(v) => {
-            response.add(rnd_reply(&v).send_self());
-        }
-        Stats => {
-            let mut protocols: HashSet<_> = server
-                .clients
-                .iter()
-                .map(|(_, c)| c.protocol_number)
-                .chain(server.rooms.iter().map(|(_, r)| r.protocol_number))
-                .collect();
-            let mut protocols: Vec<_> = protocols.drain().collect();
-            protocols.sort();
-
-            let mut html = Vec::with_capacity(protocols.len() + 2);
-
-            html.push("<table>".to_string());
-            for protocol in protocols {
-                html.push(format!(
-                    "<tr><td>{}</td><td>{}</td><td>{}</td></tr>",
-                    super::utils::protocol_version_string(protocol),
-                    server.protocol_clients(protocol).count(),
-                    server.protocol_rooms(protocol).count()
-                ));
-            }
-            html.push("</table>".to_string());
-
-            response.add(Warning(html.join("")).send_self());
-        }
-        List => warn!("Deprecated LIST message received"),
-        _ => warn!("Incorrect command in lobby state"),
-    }
-}
--- a/rust/hedgewars-server/src/server/handlers/loggingin.rs	Tue May 28 17:49:04 2019 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,146 +0,0 @@
-use mio;
-
-use crate::protocol::messages::HWProtocolMessage::LoadRoom;
-use crate::server::client::HWClient;
-use crate::server::core::HWServer;
-use crate::{
-    protocol::messages::{HWProtocolMessage, HWServerMessage::*},
-    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},
-    num::NonZeroU16,
-};
-
-pub enum LoginResult {
-    Unchanged,
-    Complete,
-    Exit,
-}
-
-fn completion_result<'a, I>(
-    mut other_clients: I,
-    client: &mut HWAnteClient,
-    response: &mut super::Response,
-) -> LoginResult
-where
-    I: Iterator<Item = (ClientId, &'a HWClient)>,
-{
-    let has_nick_clash =
-        other_clients.any(|(_, c)| !c.is_checker() && 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
-        }
-    } else {
-        #[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(
-    server: &mut HWServer,
-    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.anteroom.clients[client_id];
-
-            if client.nick.is_some() {
-                response.add(Error("Nickname already provided.".to_string()).send_self());
-                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());
-                LoginResult::Exit
-            } else {
-                client.nick = Some(nick.clone());
-                response.add(Nick(nick).send_self());
-
-                if client.protocol_number.is_some() {
-                    completion_result(server.clients.iter(), client, response)
-                } else {
-                    LoginResult::Unchanged
-                }
-            }
-        }
-        HWProtocolMessage::Proto(proto) => {
-            let client = &mut server.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 = NonZeroU16::new(proto);
-                response.add(Proto(proto).send_self());
-
-                if client.nick.is_some() {
-                    completion_result(server.clients.iter(), client, response)
-                } else {
-                    LoginResult::Unchanged
-                }
-            }
-        }
-        #[cfg(feature = "official-server")]
-        HWProtocolMessage::Password(hash, salt) => {
-            let client = &server.anteroom.clients[client_id];
-
-            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 server.anteroom.clients[client_id];
-            if protocol == 0 {
-                response.add(Error("Bad number.".to_string()).send_self());
-                LoginResult::Unchanged
-            } else {
-                client.protocol_number = NonZeroU16::new(protocol);
-                client.nick = Some(nick);
-                client.is_checker = true;
-                LoginResult::Complete
-            }
-        }
-        _ => {
-            warn!("Incorrect command in logging-in state");
-            LoginResult::Unchanged
-        }
-    }
-}
--- a/rust/hedgewars-server/src/server/indexslab.rs	Tue May 28 17:49:04 2019 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,71 +0,0 @@
-use std::{
-    iter,
-    mem::replace,
-    ops::{Index, IndexMut},
-};
-
-pub struct IndexSlab<T> {
-    data: Vec<Option<T>>,
-}
-
-impl<T> IndexSlab<T> {
-    pub fn new() -> Self {
-        Self { data: Vec::new() }
-    }
-
-    pub fn with_capacity(capacity: usize) -> Self {
-        Self {
-            data: Vec::with_capacity(capacity),
-        }
-    }
-
-    pub fn insert(&mut self, index: usize, value: T) {
-        if index >= self.data.len() {
-            self.data.reserve(index - self.data.len() + 1);
-            self.data.extend((self.data.len()..index).map(|_| None));
-            self.data.push(Some(value))
-        } else {
-            self.data[index] = Some(value);
-        }
-    }
-
-    pub fn contains(&self, index: usize) -> bool {
-        self.data.get(index).and_then(|x| x.as_ref()).is_some()
-    }
-
-    pub fn remove(&mut self, index: usize) -> Option<T> {
-        if let Some(x) = self.data.get_mut(index) {
-            replace(x, None)
-        } else {
-            None
-        }
-    }
-
-    pub fn iter(&self) -> impl Iterator<Item = (usize, &T)> {
-        self.data
-            .iter()
-            .enumerate()
-            .filter_map(|(index, opt)| opt.as_ref().and_then(|x| Some((index, x))))
-    }
-
-    pub fn iter_mut(&mut self) -> impl Iterator<Item = (usize, &mut T)> {
-        self.data
-            .iter_mut()
-            .enumerate()
-            .filter_map(|(index, opt)| opt.as_mut().and_then(|x| Some((index, x))))
-    }
-}
-
-impl<T> Index<usize> for IndexSlab<T> {
-    type Output = T;
-
-    fn index(&self, index: usize) -> &Self::Output {
-        self.data[index].as_ref().unwrap()
-    }
-}
-
-impl<T> IndexMut<usize> for IndexSlab<T> {
-    fn index_mut(&mut self, index: usize) -> &mut Self::Output {
-        self.data[index].as_mut().unwrap()
-    }
-}
--- a/rust/hedgewars-server/src/server/io.rs	Tue May 28 17:49:04 2019 +0200
+++ b/rust/hedgewars-server/src/server/io.rs	Tue May 28 19:04:18 2019 +0300
@@ -5,8 +5,8 @@
     thread,
 };
 
-use crate::server::{
-    database::Database,
+use crate::{
+    server::database::Database,
     handlers::{IoResult, IoTask},
 };
 use log::*;
--- a/rust/hedgewars-server/src/server/network.rs	Tue May 28 17:49:04 2019 +0200
+++ b/rust/hedgewars-server/src/server/network.rs	Tue May 28 19:04:18 2019 +0300
@@ -17,8 +17,12 @@
 use netbuf;
 use slab::Slab;
 
-use super::{core::HWServer, coretypes::ClientId, handlers};
 use crate::{
+    core::{
+        server::HWServer,
+        types::ClientId
+    },
+    handlers,
     protocol::{messages::*, ProtocolDecoder},
     utils,
 };
@@ -26,8 +30,11 @@
 #[cfg(feature = "official-server")]
 use super::io::{IOThread, RequestId};
 
-use crate::protocol::messages::HWServerMessage::Redirect;
-use crate::server::handlers::{IoResult, IoTask};
+use crate::{
+    protocol::messages::HWServerMessage::Redirect,
+    handlers::{IoResult, IoTask}
+};
+
 #[cfg(feature = "tls-connections")]
 use openssl::{
     error::ErrorStack,
--- a/rust/hedgewars-server/src/server/room.rs	Tue May 28 17:49:04 2019 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,349 +0,0 @@
-use crate::server::{
-    client::HWClient,
-    coretypes::{
-        ClientId, GameCfg, GameCfg::*, RoomConfig, RoomId, TeamInfo, Voting, MAX_HEDGEHOGS_PER_TEAM,
-    },
-};
-use bitflags::*;
-use serde::{Deserialize, Serialize};
-use serde_derive::{Deserialize, Serialize};
-use serde_yaml;
-use std::{collections::HashMap, iter};
-
-pub const MAX_TEAMS_IN_ROOM: u8 = 8;
-pub const MAX_HEDGEHOGS_IN_ROOM: u8 = MAX_HEDGEHOGS_PER_TEAM * MAX_HEDGEHOGS_PER_TEAM;
-
-fn client_teams_impl(
-    teams: &[(ClientId, TeamInfo)],
-    client_id: ClientId,
-) -> impl Iterator<Item = &TeamInfo> + Clone {
-    teams
-        .iter()
-        .filter(move |(id, _)| *id == client_id)
-        .map(|(_, t)| t)
-}
-
-pub struct GameInfo {
-    pub teams_in_game: u8,
-    pub teams_at_start: Vec<(ClientId, TeamInfo)>,
-    pub left_teams: Vec<String>,
-    pub msg_log: Vec<String>,
-    pub sync_msg: Option<String>,
-    pub is_paused: bool,
-    config: RoomConfig,
-}
-
-impl GameInfo {
-    fn new(teams: Vec<(ClientId, TeamInfo)>, config: RoomConfig) -> GameInfo {
-        GameInfo {
-            left_teams: Vec::new(),
-            msg_log: Vec::new(),
-            sync_msg: None,
-            is_paused: false,
-            teams_in_game: teams.len() as u8,
-            teams_at_start: teams,
-            config,
-        }
-    }
-
-    pub fn client_teams(&self, client_id: ClientId) -> impl Iterator<Item = &TeamInfo> + Clone {
-        client_teams_impl(&self.teams_at_start, client_id)
-    }
-}
-
-#[derive(Serialize, Deserialize)]
-pub struct RoomSave {
-    pub location: String,
-    config: RoomConfig,
-}
-
-bitflags! {
-    pub struct RoomFlags: u8 {
-        const FIXED = 0b0000_0001;
-        const RESTRICTED_JOIN = 0b0000_0010;
-        const RESTRICTED_TEAM_ADD = 0b0000_0100;
-        const RESTRICTED_UNREGISTERED_PLAYERS = 0b0000_1000;
-    }
-}
-
-pub struct HWRoom {
-    pub id: RoomId,
-    pub master_id: Option<ClientId>,
-    pub name: String,
-    pub password: Option<String>,
-    pub greeting: String,
-    pub protocol_number: u16,
-    pub flags: RoomFlags,
-
-    pub players_number: u8,
-    pub default_hedgehog_number: u8,
-    pub max_teams: u8,
-    pub ready_players_number: u8,
-    pub teams: Vec<(ClientId, TeamInfo)>,
-    config: RoomConfig,
-    pub voting: Option<Voting>,
-    pub saves: HashMap<String, RoomSave>,
-    pub game_info: Option<GameInfo>,
-}
-
-impl HWRoom {
-    pub fn new(id: RoomId) -> HWRoom {
-        HWRoom {
-            id,
-            master_id: None,
-            name: String::new(),
-            password: None,
-            greeting: "".to_string(),
-            flags: RoomFlags::empty(),
-            protocol_number: 0,
-            players_number: 0,
-            default_hedgehog_number: 4,
-            max_teams: MAX_TEAMS_IN_ROOM,
-            ready_players_number: 0,
-            teams: Vec::new(),
-            config: RoomConfig::new(),
-            voting: None,
-            saves: HashMap::new(),
-            game_info: None,
-        }
-    }
-
-    pub fn hedgehogs_number(&self) -> u8 {
-        self.teams.iter().map(|(_, t)| t.hedgehogs_number).sum()
-    }
-
-    pub fn addable_hedgehogs(&self) -> u8 {
-        MAX_HEDGEHOGS_IN_ROOM - self.hedgehogs_number()
-    }
-
-    pub fn add_team(
-        &mut self,
-        owner_id: ClientId,
-        mut team: TeamInfo,
-        preserve_color: bool,
-    ) -> &TeamInfo {
-        if !preserve_color {
-            team.color = iter::repeat(())
-                .enumerate()
-                .map(|(i, _)| i as u8)
-                .take(u8::max_value() as usize + 1)
-                .find(|i| self.teams.iter().all(|(_, t)| t.color != *i))
-                .unwrap_or(0u8)
-        };
-        team.hedgehogs_number = if self.teams.is_empty() {
-            self.default_hedgehog_number
-        } else {
-            self.teams[0]
-                .1
-                .hedgehogs_number
-                .min(self.addable_hedgehogs())
-        };
-        self.teams.push((owner_id, team));
-        &self.teams.last().unwrap().1
-    }
-
-    pub fn remove_team(&mut self, name: &str) {
-        if let Some(index) = self.teams.iter().position(|(_, t)| t.name == name) {
-            self.teams.remove(index);
-        }
-    }
-
-    pub fn set_hedgehogs_number(&mut self, n: u8) -> Vec<String> {
-        let mut names = Vec::new();
-        let teams = match self.game_info {
-            Some(ref mut info) => &mut info.teams_at_start,
-            None => &mut self.teams,
-        };
-
-        if teams.len() as u8 * n <= MAX_HEDGEHOGS_IN_ROOM {
-            for (_, team) in teams.iter_mut() {
-                team.hedgehogs_number = n;
-                names.push(team.name.clone())
-            }
-            self.default_hedgehog_number = n;
-        }
-        names
-    }
-
-    pub fn find_team_and_owner_mut<F>(&mut self, f: F) -> Option<(ClientId, &mut TeamInfo)>
-    where
-        F: Fn(&TeamInfo) -> bool,
-    {
-        self.teams
-            .iter_mut()
-            .find(|(_, t)| f(t))
-            .map(|(id, t)| (*id, t))
-    }
-
-    pub fn find_team<F>(&self, f: F) -> Option<&TeamInfo>
-    where
-        F: Fn(&TeamInfo) -> bool,
-    {
-        self.teams
-            .iter()
-            .find_map(|(_, t)| Some(t).filter(|t| f(&t)))
-    }
-
-    pub fn client_teams(&self, client_id: ClientId) -> impl Iterator<Item = &TeamInfo> {
-        client_teams_impl(&self.teams, client_id)
-    }
-
-    pub fn client_team_indices(&self, client_id: ClientId) -> Vec<u8> {
-        self.teams
-            .iter()
-            .enumerate()
-            .filter(move |(_, (id, _))| *id == client_id)
-            .map(|(i, _)| i as u8)
-            .collect()
-    }
-
-    pub fn clan_team_owners(&self, color: u8) -> impl Iterator<Item = ClientId> + '_ {
-        self.teams
-            .iter()
-            .filter(move |(_, t)| t.color == color)
-            .map(|(id, _)| *id)
-    }
-
-    pub fn find_team_owner(&self, team_name: &str) -> Option<(ClientId, &str)> {
-        self.teams
-            .iter()
-            .find(|(_, t)| t.name == team_name)
-            .map(|(id, t)| (*id, &t.name[..]))
-    }
-
-    pub fn find_team_color(&self, owner_id: ClientId) -> Option<u8> {
-        self.client_teams(owner_id).nth(0).map(|t| t.color)
-    }
-
-    pub fn has_multiple_clans(&self) -> bool {
-        self.teams.iter().min_by_key(|(_, t)| t.color)
-            != self.teams.iter().max_by_key(|(_, t)| t.color)
-    }
-
-    pub fn set_config(&mut self, cfg: GameCfg) {
-        self.config.set_config(cfg);
-    }
-
-    pub fn start_round(&mut self) {
-        if self.game_info.is_none() {
-            self.game_info = Some(GameInfo::new(self.teams.clone(), self.config.clone()));
-        }
-    }
-
-    pub fn is_fixed(&self) -> bool {
-        self.flags.contains(RoomFlags::FIXED)
-    }
-    pub fn is_join_restricted(&self) -> bool {
-        self.flags.contains(RoomFlags::RESTRICTED_JOIN)
-    }
-    pub fn is_team_add_restricted(&self) -> bool {
-        self.flags.contains(RoomFlags::RESTRICTED_TEAM_ADD)
-    }
-    pub fn are_unregistered_players_restricted(&self) -> bool {
-        self.flags
-            .contains(RoomFlags::RESTRICTED_UNREGISTERED_PLAYERS)
-    }
-
-    pub fn set_is_fixed(&mut self, value: bool) {
-        self.flags.set(RoomFlags::FIXED, value)
-    }
-    pub fn set_join_restriction(&mut self, value: bool) {
-        self.flags.set(RoomFlags::RESTRICTED_JOIN, value)
-    }
-    pub fn set_team_add_restriction(&mut self, value: bool) {
-        self.flags.set(RoomFlags::RESTRICTED_TEAM_ADD, value)
-    }
-    pub fn set_unregistered_players_restriction(&mut self, value: bool) {
-        self.flags
-            .set(RoomFlags::RESTRICTED_UNREGISTERED_PLAYERS, value)
-    }
-
-    fn flags_string(&self) -> String {
-        let mut result = "-".to_string();
-        if self.game_info.is_some() {
-            result += "g"
-        }
-        if self.password.is_some() {
-            result += "p"
-        }
-        if self.is_join_restricted() {
-            result += "j"
-        }
-        if self.are_unregistered_players_restricted() {
-            result += "r"
-        }
-        result
-    }
-
-    pub fn info(&self, master: Option<&HWClient>) -> Vec<String> {
-        let c = &self.config;
-        vec![
-            self.flags_string(),
-            self.name.clone(),
-            self.players_number.to_string(),
-            self.teams.len().to_string(),
-            master.map_or("[]", |c| &c.nick).to_string(),
-            c.map_type.to_string(),
-            c.script.to_string(),
-            c.scheme.name.to_string(),
-            c.ammo.name.to_string(),
-        ]
-    }
-
-    pub fn active_config(&self) -> &RoomConfig {
-        match self.game_info {
-            Some(ref info) => &info.config,
-            None => &self.config,
-        }
-    }
-
-    pub fn map_config(&self) -> Vec<String> {
-        match self.game_info {
-            Some(ref info) => info.config.to_map_config(),
-            None => self.config.to_map_config(),
-        }
-    }
-
-    pub fn game_config(&self) -> Vec<GameCfg> {
-        match self.game_info {
-            Some(ref info) => info.config.to_game_config(),
-            None => self.config.to_game_config(),
-        }
-    }
-
-    pub fn save_config(&mut self, name: String, location: String) {
-        self.saves.insert(
-            name,
-            RoomSave {
-                location,
-                config: self.config.clone(),
-            },
-        );
-    }
-
-    pub fn load_config(&mut self, name: &str) -> Option<&str> {
-        if let Some(save) = self.saves.get(name) {
-            self.config = save.config.clone();
-            Some(&save.location[..])
-        } else {
-            None
-        }
-    }
-
-    pub fn delete_config(&mut self, name: &str) -> bool {
-        self.saves.remove(name).is_some()
-    }
-
-    pub fn get_saves(&self) -> Result<String, serde_yaml::Error> {
-        serde_yaml::to_string(&(&self.greeting, &self.saves))
-    }
-
-    pub fn set_saves(&mut self, text: &str) -> Result<(), serde_yaml::Error> {
-        serde_yaml::from_str::<(String, HashMap<String, RoomSave>)>(text).map(
-            |(greeting, saves)| {
-                self.greeting = greeting;
-                self.saves = saves;
-            },
-        )
-    }
-}