separated the server logic from all the async io mess.
authoralfadur
Thu, 08 Mar 2018 15:01:18 -0500
changeset 13119 1e39b8749072
parent 13118 1ddb8aac5e30
child 13128 2f7c8e2ebe8e
separated the server logic from all the async io mess.
gameServer2/Cargo.toml
gameServer2/src/main.rs
gameServer2/src/protocol/messages.rs
gameServer2/src/protocol/parser.rs
gameServer2/src/server/actions.rs
gameServer2/src/server/client.rs
gameServer2/src/server/coretypes.rs
gameServer2/src/server/handlers/inroom.rs
gameServer2/src/server/handlers/lobby.rs
gameServer2/src/server/handlers/loggingin.rs
gameServer2/src/server/handlers/mod.rs
gameServer2/src/server/mod.rs
gameServer2/src/server/network.rs
gameServer2/src/server/room.rs
gameServer2/src/server/server.rs
--- a/gameServer2/Cargo.toml	Thu Mar 08 16:49:49 2018 +0100
+++ b/gameServer2/Cargo.toml	Thu Mar 08 15:01:18 2018 -0500
@@ -11,3 +11,4 @@
 nom = "3.2"
 env_logger = "0.4"
 log = "0.3.8"
+proptest = "0.5.1"
--- a/gameServer2/src/main.rs	Thu Mar 08 16:49:49 2018 +0100
+++ b/gameServer2/src/main.rs	Thu Mar 08 15:01:18 2018 -0500
@@ -1,3 +1,5 @@
+#![allow(unused_imports)]
+
 extern crate rand;
 extern crate mio;
 extern crate slab;
@@ -7,6 +9,7 @@
 #[macro_use]
 extern crate log;
 extern crate env_logger;
+#[macro_use] extern crate proptest;
 
 //use std::io::*;
 //use rand::Rng;
@@ -18,6 +21,8 @@
 mod server;
 mod protocol;
 
+use server::network::NetworkLayer;
+
 fn main() {
     env_logger::init().unwrap();
 
@@ -25,10 +30,10 @@
 
     let address = "0.0.0.0:46631".parse().unwrap();
     let listener = TcpListener::bind(&address).unwrap();
-    let mut server = server::server::HWServer::new(listener, 1024, 512);
 
     let poll = Poll::new().unwrap();
-    server.register(&poll).unwrap();
+    let mut hw_network = NetworkLayer::new(listener, 1024, 512);
+    hw_network.register_server(&poll).unwrap();
 
     let mut events = Events::with_capacity(1024);
 
@@ -38,14 +43,14 @@
         for event in events.iter() {
             if event.readiness() & Ready::readable() == Ready::readable() {
                 match event.token() {
-                    utils::SERVER => server.accept(&poll).unwrap(),
-                    Token(tok) => server.client_readable(&poll, tok).unwrap(),
+                    utils::SERVER => hw_network.accept_client(&poll).unwrap(),
+                    Token(tok) => hw_network.client_readable(&poll, tok).unwrap(),
                 }
             }
             if event.readiness() & Ready::writable() == Ready::writable() {
                 match event.token() {
                     utils::SERVER => unreachable!(),
-                    Token(tok) => server.client_writable(&poll, tok).unwrap(),
+                    Token(tok) => hw_network.client_writable(&poll, tok).unwrap(),
                 }
             }
 //            if event.kind().is_hup() || event.kind().is_error() {
--- a/gameServer2/src/protocol/messages.rs	Thu Mar 08 16:49:49 2018 +0100
+++ b/gameServer2/src/protocol/messages.rs	Thu Mar 08 15:01:18 2018 -0500
@@ -3,7 +3,7 @@
 use std::ops;
 use std::convert::From;
 
-#[derive(PartialEq, Debug)]
+#[derive(PartialEq, Eq, Clone, Debug)]
 pub enum HWProtocolMessage {
     // core
     Ping,
@@ -69,17 +69,17 @@
     Empty,
 }
 
-pub enum HWServerMessage<'a> {
+pub enum HWServerMessage {
     Ping,
     Pong,
-    Bye(&'a str),
-    Nick(&'a str),
-    LobbyLeft(&'a str),
-    LobbyJoined(&'a [&'a str]),
-    ChatMsg(&'a str, &'a str),
-    ClientFlags(&'a str, &'a [&'a str]),
+    Bye(String),
+    Nick(String),
+    LobbyLeft(String),
+    LobbyJoined(Vec<String>),
+    ChatMsg(String, String),
+    ClientFlags(String, Vec<String>),
 
-    Warning(&'a str),
+    Warning(String),
     Connected(u32),
     Unreachable,
 }
@@ -96,41 +96,117 @@
     m
 }
 
-impl<'a> HWServerMessage<'a> {
+impl<'a> HWProtocolMessage {
     pub fn to_raw_protocol(&self) -> String {
+        use self::HWProtocolMessage::*;
+        match *self {
+            Ping => "PING\n\n".to_string(),
+            Pong => "PONG\n\n".to_string(),
+            Quit(None) => format!("QUIT\n\n"),
+            Quit(Some(ref msg)) => format!("QUIT\n{}\n\n", msg),
+            Global(ref msg) => format!("CMD\nGLOBAL\n{}\n\n", msg),
+            Watch(ref name) => format!("CMD\nWATCH\n{}\n\n", name),
+            ToggleServerRegisteredOnly => "CMD\nREGISTERED_ONLY\n\n".to_string(),
+            SuperPower => "CMD\nSUPER_POWER\n\n".to_string(),
+            Info(ref info) => format!("CMD\nINFO\n{}\n\n", info),
+            Nick(ref nick) => format!("NICK\n{}\n\n", nick),
+            Proto(version) => format!("PROTO\n{}\n\n", version),
+            Password(ref p, ref s) => format!("PASSWORD\n{}\n{}\n\n", p, s), //?
+            Checker(i, ref n, ref p) =>
+                format!("CHECKER\n{}\n{}\n{}\n\n", i, n, p), //?,
+            List => "LIST\n\n".to_string(),
+            Chat(ref msg) => format!("CHAT\n{}\n\n", msg),
+            CreateRoom(ref name, None) =>
+                format!("CREATE_ROOM\n{}\n\n", name),
+            CreateRoom(ref name, Some(ref password)) =>
+                format!("CREATE_ROOM\n{}\n{}\n\n", name, password),
+            Join(ref name, None) =>
+                format!("JOIN\n{}\n\n", name),
+            Join(ref name, Some(ref arg)) =>
+                format!("JOIN\n{}\n{}\n\n", name, arg),
+            Follow(ref name) =>
+                format!("FOLLOW\n{}\n\n", name),
+            //Rnd(Vec<String>), ???
+            Kick(ref name) => format!("KICK\n{}\n\n", name),
+            Ban(ref name, ref reason, time) =>
+                format!("BAN\n{}\n{}\n{}\n\n", name, reason, time),
+            BanIP(ref ip, ref reason, time) =>
+                format!("BAN_IP\n{}\n{}\n{}\n\n", ip, reason, time),
+            BanNick(ref nick, ref reason, time) =>
+                format!("BAN_NICK\n{}\n{}\n{}\n\n", nick, reason, time),
+            BanList => "BANLIST\n\n".to_string(),
+            Unban(ref name) => format!("UNBAN\n{}\n\n", name),
+            //SetServerVar(ServerVar), ???
+            GetServerVar => "GET_SERVER_VAR\n\n".to_string(),
+            RestartServer => "CMD\nRESTART_SERVER\nYES\n\n".to_string(),
+            Stats => "CMD\nSTATS\n\n".to_string(),
+            Part(None) => "CMD\nPART\n\n".to_string(),
+            Part(Some(ref msg)) => format!("CMD\nPART\n{}\n\n", msg),
+            //Cfg(GameCfg) ??
+            //AddTeam(TeamInfo) ??,
+            RemoveTeam(ref name) => format!("REMOVE_TEAM\n{}\n\n", name),
+            //SetHedgehogsNumber(String, u8), ??
+            //SetTeamColor(String, u8), ??
+            ToggleReady => "TOGGLE_READY\n\n".to_string(),
+            StartGame => "START_GAME\n\n".to_string(),
+            EngineMessage(ref msg) => format!("EM\n{}\n\n", msg),
+            RoundFinished => "ROUNDFINISHED\n\n".to_string(),
+            ToggleRestrictJoin => "TOGGLE_RESTRICT_JOINS\n\n".to_string(),
+            ToggleRestrictTeams => "TOGGLE_RESTRICT_TEAMS\n\n".to_string(),
+            ToggleRegisteredOnly => "TOGGLE_REGISTERED_ONLY\n\n".to_string(),
+            RoomName(ref name) => format!("ROOM_NAME\n{}\n\n", name),
+            Delegate(ref name) => format!("CMD\nDELEGATE\n{}\n\n", name),
+            TeamChat(ref msg) => format!("TEAMCHAT\n{}\n\n", msg),
+            MaxTeams(count) => format!("CMD\nMAXTEAMS\n{}\n\n", count) ,
+            Fix => "CMD\nFIX\n\n".to_string(),
+            Unfix => "CMD\nUNFIX\n\n".to_string(),
+            Greeting(ref msg) => format!("CMD\nGREETING\n{}\n\n", msg),
+            //CallVote(Option<(String, Option<String>)>) =>, ??
+            Vote(ref msg) => format!("CMD\nVOTE\n{}\n\n", msg),
+            ForceVote(ref msg) => format!("CMD\nFORCE\n{}\n\n", msg),
+            //Save(String, String), ??
+            Delete(ref room) => format!("CMD\nDELETE\n{}\n\n", room),
+            SaveRoom(ref room) => format!("CMD\nSAVEROOM\n{}\n\n", room),
+            LoadRoom(ref room) => format!("CMD\nLOADROOM\n{}\n\n", room),
+            Malformed => "A\nQUICK\nBROWN\nHOG\nJUMPS\nOVER\nTHE\nLAZY\nDOG\n\n".to_string(),
+            Empty => "\n\n".to_string(),
+            _ => panic!("Protocol message not yet implemented")
+        }
+    }
+}
+
+impl HWServerMessage {
+    pub fn to_raw_protocol(&self) -> String {
+        use self::HWServerMessage::*;
         match self {
-            &HWServerMessage::Ping
-                => "PING\n\n".to_string(),
-            &HWServerMessage::Pong
-                => "PONG\n\n".to_string(),
-            &HWServerMessage::Connected(protocol_version)
+            &Ping => "PING\n\n".to_string(),
+            &Pong => "PONG\n\n".to_string(),
+            &Connected(protocol_version)
                 => construct_message(&[
                     "CONNECTED",
                     "Hedgewars server http://www.hedgewars.org/",
                     &protocol_version.to_string()
                 ]),
-            &HWServerMessage::Bye(msg)
-                => construct_message(&["BYE", &msg]),
-            &HWServerMessage::Nick(nick)
-                => construct_message(&["NICK", &nick]),
-            &HWServerMessage::LobbyLeft(nick)
+            &Bye(ref msg) => construct_message(&["BYE", &msg]),
+            &Nick(ref nick) => construct_message(&["NICK", &nick]),
+            &LobbyLeft(ref nick)
                 => construct_message(&["LOBBY_LEFT", &nick]),
-            &HWServerMessage::LobbyJoined(nicks)
+            &LobbyJoined(ref nicks)
                 => {
                 let mut v = vec!["LOBBY:JOINED"];
-                v.extend_from_slice(nicks);
+                v.extend(nicks.iter().map(|n| { &n[..] }));
                 construct_message(&v)
             },
-            &HWServerMessage::ClientFlags(flags, nicks)
-            => {
+            &ClientFlags(ref flags, ref nicks)
+                => {
                 let mut v = vec!["CLIENT_FLAGS"];
-                v.push(flags);
-                v.extend_from_slice(nicks);
+                v.push(&flags[..]);
+                v.extend(nicks.iter().map(|n| { &n[..] }));
                 construct_message(&v)
             },
-            &HWServerMessage::ChatMsg(nick, msg)
+            &ChatMsg(ref nick, ref msg)
                 => construct_message(&["CHAT", &nick, &msg]),
-            &HWServerMessage::Warning(msg)
+            &Warning(ref msg)
                 => construct_message(&["WARNING", &msg]),
             _ => construct_message(&["ERROR", "UNIMPLEMENTED"]),
         }
--- a/gameServer2/src/protocol/parser.rs	Thu Mar 08 16:49:49 2018 +0100
+++ b/gameServer2/src/protocol/parser.rs	Thu Mar 08 15:01:18 2018 -0500
@@ -5,6 +5,12 @@
 use super::messages::HWProtocolMessage;
 use super::messages::HWProtocolMessage::*;
 
+use proptest::test_runner::{TestRunner, Reason};
+use proptest::arbitrary::{any, any_with, Arbitrary, StrategyFor};
+use proptest::strategy::{Strategy, BoxedStrategy, Just, Filter, ValueTree};
+use proptest::string::RegexGeneratorValueTree;
+use std::ops::Range;
+
 named!(end_of_message, tag!("\n\n"));
 named!(str_line<&[u8],   &str>, map_res!(not_line_ending, str::from_utf8));
 named!(  a_line<&[u8], String>, map!(str_line, String::from));
@@ -50,8 +56,8 @@
     | do_parse!(tag_no_case!("RESTART_SERVER") >> eol >> tag!("YES") >> (RestartServer))
     | do_parse!(tag_no_case!("REGISTERED_ONLY") >> (ToggleServerRegisteredOnly))
     | do_parse!(tag_no_case!("SUPER_POWER")     >> (SuperPower))
-    | do_parse!(tag_no_case!("PART")     >> eol >> m: opt_param >> (Quit(m)))
-    | do_parse!(tag_no_case!("QUIT")     >> eol >> m: opt_param >> (Part(m)))
+    | do_parse!(tag_no_case!("PART")     >> m: opt_param >> (Part(m)))
+    | do_parse!(tag_no_case!("QUIT")     >> m: opt_param >> (Quit(m)))
     | do_parse!(tag_no_case!("DELEGATE") >> eol >> n: a_line  >> (Delegate(n)))
     | do_parse!(tag_no_case!("SAVEROOM") >> eol >> r: a_line  >> (SaveRoom(r)))
     | do_parse!(tag_no_case!("LOADROOM") >> eol >> r: a_line  >> (LoadRoom(r)))
@@ -121,6 +127,138 @@
 
 named!(pub extract_messages<&[u8], Vec<HWProtocolMessage> >, many0!(complete!(message)));
 
+// Due to inability to define From between Options
+trait Into2<T>: Sized { fn into2(self) -> T; }
+impl <T> Into2<T> for T { fn into2(self) -> T { self } }
+impl Into2<String> for Ascii { fn into2(self) -> String { self.0 } }
+impl Into2<Option<String>> for Option<Ascii>{
+    fn into2(self) -> Option<String> { self.map(|x| {x.0}) }
+}
+
+macro_rules! proto_msg_case {
+    ($val: ident()) =>
+        (Just($val));
+    ($val: ident($arg: ty)) =>
+        (any::<$arg>().prop_map(|v| {$val(v.into2())}));
+    ($val: ident($arg1: ty, $arg2: ty)) =>
+        (any::<($arg1, $arg2)>().prop_map(|v| {$val(v.0.into2(), v.1.into2())}));
+    ($val: ident($arg1: ty, $arg2: ty, $arg3: ty)) =>
+        (any::<($arg1, $arg2, $arg3)>().prop_map(|v| {$val(v.0.into2(), v.1.into2(), v.2.into2())}));
+}
+
+macro_rules! proto_msg_match {
+    ($var: expr, def = $default: ident, $($num: expr => $constr: ident $res: tt),*) => (
+        match $var {
+            $($num => (proto_msg_case!($constr $res)).boxed()),*,
+            _ => Just($default).boxed()
+        }
+    )
+}
+
+#[derive(Debug)]
+struct Ascii(String);
+
+struct AsciiValueTree(RegexGeneratorValueTree<String>);
+
+impl ValueTree for AsciiValueTree {
+    type Value = Ascii;
+
+    fn current(&self) -> Self::Value { Ascii(self.0.current()) }
+    fn simplify(&mut self) -> bool { self.0.simplify() }
+    fn complicate(&mut self) -> bool { self.0.complicate() }
+}
+
+impl Arbitrary for Ascii {
+    type Parameters = <String as Arbitrary>::Parameters;
+
+    fn arbitrary_with(args: Self::Parameters) -> Self::Strategy {
+        any_with::<String>(args)
+            .prop_filter("not ascii", |s| {
+                s.len() > 0 && s.is_ascii() &&
+                s.find(|c| {
+                    ['\0', '\n', '\x20'].contains(&c)
+                }).is_none()})
+            .prop_map(Ascii)
+            .boxed()
+    }
+
+    type Strategy = BoxedStrategy<Ascii>;
+    type ValueTree = Box<ValueTree<Value = Ascii>>;
+}
+
+fn gen_proto_msg() -> BoxedStrategy<HWProtocolMessage> where {
+    let res = (0..58).no_shrink().prop_flat_map(|i| {
+        proto_msg_match!(i, def = Malformed,
+        0 => Ping(),
+        1 => Pong(),
+        2 => Quit(Option<Ascii>),
+        //3 => Cmd
+        4 => Global(Ascii),
+        5 => Watch(Ascii),
+        6 => ToggleServerRegisteredOnly(),
+        7 => SuperPower(),
+        8 => Info(Ascii),
+        9 => Nick(Ascii),
+        10 => Proto(u32),
+        11 => Password(Ascii, Ascii),
+        12 => Checker(u32, Ascii, Ascii),
+        13 => List(),
+        14 => Chat(Ascii),
+        15 => CreateRoom(Ascii, Option<Ascii>),
+        16 => Join(Ascii, Option<Ascii>),
+        17 => Follow(Ascii),
+        //18 => Rnd(Vec<String>),
+        19 => Kick(Ascii),
+        20 => Ban(Ascii, Ascii, u32),
+        21 => BanIP(Ascii, Ascii, u32),
+        22 => BanNick(Ascii, Ascii, u32),
+        23 => BanList(),
+        24 => Unban(Ascii),
+        //25 => SetServerVar(ServerVar),
+        26 => GetServerVar(),
+        27 => RestartServer(),
+        28 => Stats(),
+        29 => Part(Option<Ascii>),
+        //30 => Cfg(GameCfg),
+        //31 => AddTeam(TeamInfo),
+        32 => RemoveTeam(Ascii),
+        //33 => SetHedgehogsNumber(String, u8),
+        //34 => SetTeamColor(String, u8),
+        35 => ToggleReady(),
+        36 => StartGame(),
+        37 => EngineMessage(Ascii),
+        38 => RoundFinished(),
+        39 => ToggleRestrictJoin(),
+        40 => ToggleRestrictTeams(),
+        41 => ToggleRegisteredOnly(),
+        42 => RoomName(Ascii),
+        43 => Delegate(Ascii),
+        44 => TeamChat(Ascii),
+        45 => MaxTeams(u8),
+        46 => Fix(),
+        47 => Unfix(),
+        48 => Greeting(Ascii),
+        //49 => CallVote(Option<(String, Option<String>)>),
+        50 => Vote(String),
+        51 => ForceVote(Ascii),
+        //52 => Save(String, String),
+        53 => Delete(Ascii),
+        54 => SaveRoom(Ascii),
+        55 => LoadRoom(Ascii),
+        56 => Malformed(),
+        57 => Empty()
+    )});
+    res.boxed()
+}
+
+proptest! {
+    #[test]
+    fn is_parser_composition_idempotent(ref msg in gen_proto_msg()) {
+        println!("!! Msg: {:?}, Bytes: {:?} !!", msg, msg.to_raw_protocol().as_bytes());
+        assert_eq!(message(msg.to_raw_protocol().as_bytes()), IResult::Done(&b""[..], msg.clone()))
+    }
+}
+
 #[test]
 fn parse_test() {
     assert_eq!(message(b"PING\n\n"),          IResult::Done(&b""[..], Ping));
@@ -132,10 +270,13 @@
     assert_eq!(message(b"CMD\nwatch\ndemo\n\n"), IResult::Done(&b""[..], Watch("demo".to_string())));
     assert_eq!(message(b"BAN\nme\nbad\n77\n\n"), IResult::Done(&b""[..], Ban("me".to_string(), "bad".to_string(), 77)));
 
+    assert_eq!(message(b"CMD\nPART\n\n"),      IResult::Done(&b""[..], Part(None)));
+    assert_eq!(message(b"CMD\nPART\n_msg_\n\n"), IResult::Done(&b""[..], Part(Some("_msg_".to_string()))));
+
     assert_eq!(extract_messages(b"QUIT\n1\n2\n\n"),    IResult::Done(&b""[..], vec![Malformed]));
 
     assert_eq!(extract_messages(b"PING\n\nPING\n\nP"), IResult::Done(&b"P"[..], vec![Ping, Ping]));
     assert_eq!(extract_messages(b"SING\n\nPING\n\n"),  IResult::Done(&b""[..],  vec![Malformed, Ping]));
     assert_eq!(extract_messages(b"\n\n\n\nPING\n\n"),  IResult::Done(&b""[..],  vec![Empty, Empty, Ping]));
     assert_eq!(extract_messages(b"\n\n\nPING\n\n"),    IResult::Done(&b""[..],  vec![Empty, Empty, Ping]));
-}
+}
\ No newline at end of file
--- a/gameServer2/src/server/actions.rs	Thu Mar 08 16:49:49 2018 +0100
+++ b/gameServer2/src/server/actions.rs	Thu Mar 08 15:01:18 2018 -0500
@@ -3,14 +3,15 @@
 use std::io;
 
 use super::server::HWServer;
-use super::server::HWRoom;
+use super::room::HWRoom;
 use protocol::messages::HWProtocolMessage;
+use protocol::messages::HWServerMessage;
 use protocol::messages::HWServerMessage::*;
 use super::handlers;
 
 pub enum Action {
-    SendMe(String),
-    SendAllButMe(String),
+    SendMe(HWServerMessage),
+    SendAllButMe(HWServerMessage),
     RemoveClient,
     ByeClient(String),
     ReactProtocolMessage(HWProtocolMessage),
@@ -22,32 +23,30 @@
 
 use self::Action::*;
 
-pub fn run_action(server: &mut HWServer, token: usize, poll: &mio::Poll, action: Action) {
+pub fn run_action(server: &mut HWServer, token: usize, action: Action) {
     match action {
         SendMe(msg) =>
-            server.send(token, &msg),
+            server.send_self(token, msg),
         SendAllButMe(msg) => {
-            for (_i, c) in server.clients.iter_mut() {
-                if c.id != token {
-                    c.send_string(&msg)
-                }
-            }
+            server.send_others(token, msg)
         },
         ByeClient(msg) => {
-            server.react(token, poll, vec![
-                SendMe(Bye(&msg).to_raw_protocol()),
+            server.react(token, vec![
+                SendMe(Bye(msg)),
                 RemoveClient,
                 ]);
         },
         RemoveClient => {
-            server.clients[token].deregister(poll);
-            server.clients.remove(token);
+            server.removed_clients.push(token);
+            if server.clients.contains(token) {
+                server.clients.remove(token);
+            }
         },
         ReactProtocolMessage(msg) =>
-            handlers::handle(server, token, poll, msg),
+            handlers::handle(server, token, msg),
         CheckRegistered =>
             if server.clients[token].protocol_number > 0 && server.clients[token].nick != "" {
-                server.react(token, poll, vec![
+                server.react(token, vec![
                     JoinLobby,
                     ]);
             },
@@ -56,36 +55,34 @@
 
             let joined_msg;
             {
-                let mut lobby_nicks: Vec<&str> = Vec::new();
+                let mut lobby_nicks = Vec::new();
                 for (_, c) in server.clients.iter() {
                     if c.room_id.is_some() {
-                        lobby_nicks.push(&c.nick);
+                        lobby_nicks.push(c.nick.clone());
                     }
                 }
-                joined_msg = LobbyJoined(&lobby_nicks).to_raw_protocol();
+                joined_msg = LobbyJoined(lobby_nicks);
             }
-            let everyone_msg = LobbyJoined(&[&server.clients[token].nick]).to_raw_protocol();
-            server.react(token, poll, vec![
+            let everyone_msg = LobbyJoined(vec![server.clients[token].nick.clone()]);
+            server.react(token, vec![
                 SendAllButMe(everyone_msg),
                 SendMe(joined_msg),
                 ]);
         },
         AddRoom(name, password) => {
-            let room_id = server.rooms.insert(HWRoom::new());
+            let room_id = server.add_room();;
             {
                 let r = &mut server.rooms[room_id];
                 let c = &mut server.clients[token];
                 r.name = name;
                 r.password = password;
-                r.id = room_id.clone();
                 r.ready_players_number = 1;
                 r.protocol_number = c.protocol_number;
                 c.room_id = Some(room_id);
             }
-
         },
         Warn(msg) => {
-            run_action(server, token, poll, SendMe(Warning(&msg).to_raw_protocol()));
+            run_action(server, token,SendMe(Warning(msg)));
         }
         //_ => unimplemented!(),
     }
--- a/gameServer2/src/server/client.rs	Thu Mar 08 16:49:49 2018 +0100
+++ b/gameServer2/src/server/client.rs	Thu Mar 08 15:01:18 2018 -0500
@@ -1,21 +1,7 @@
-use mio::net::TcpStream;
-use mio::*;
-use std::io::Write;
-use std::io;
-use netbuf;
-
-use utils;
-use protocol::ProtocolDecoder;
-use protocol::messages::*;
-use super::actions::Action::*;
-use super::actions::Action;
+pub type ClientId = usize;
 
 pub struct HWClient {
-    sock: TcpStream,
-    decoder: ProtocolDecoder,
-    buf_out: netbuf::Buf,
-
-    pub id: usize,
+    pub id: ClientId,
     pub room_id: Option<usize>,
     pub nick: String,
     pub protocol_number: u32,
@@ -25,14 +11,10 @@
 }
 
 impl HWClient {
-    pub fn new(sock: TcpStream) -> HWClient {
+    pub fn new(id: ClientId) -> HWClient {
         HWClient {
-            sock: sock,
-            decoder: ProtocolDecoder::new(),
-            buf_out: netbuf::Buf::new(),
+            id,
             room_id: None,
-            id: 0,
-
             nick: String::new(),
             protocol_number: 0,
             is_master: false,
@@ -40,58 +22,4 @@
             is_joined_mid_game: false,
         }
     }
-
-    pub fn register(&mut self, poll: &Poll, token: Token) {
-        poll.register(&self.sock, token, Ready::readable() | Ready::writable(),
-                      PollOpt::edge())
-            .ok().expect("could not register socket with event loop");
-
-        self.send_msg(HWServerMessage::Connected(utils::PROTOCOL_VERSION));
-    }
-
-    pub fn deregister(&mut self, poll: &Poll) {
-        poll.deregister(&self.sock)
-            .ok().expect("could not deregister socket");
-    }
-
-    pub fn send_raw_msg(&mut self, msg: &[u8]) {
-        self.buf_out.write(msg).unwrap();
-        self.flush();
-    }
-
-    pub fn send_string(&mut self, msg: &String) {
-        self.send_raw_msg(&msg.as_bytes());
-    }
-
-    pub fn send_msg(&mut self, msg: HWServerMessage) {
-        self.send_string(&msg.to_raw_protocol());
-    }
-
-    fn flush(&mut self) {
-        self.buf_out.write_to(&mut self.sock).unwrap();
-        self.sock.flush();
-    }
-
-    pub fn readable(&mut self, _poll: &Poll) -> Vec<Action> {
-        let v = self.decoder.read_from(&mut self.sock).unwrap();
-        debug!("Read {} bytes", v);
-        let mut response = Vec::new();
-        {
-            for msg in self.decoder.extract_messages() {
-                response.push(ReactProtocolMessage(msg));
-            }
-        }
-        self.decoder.sweep();
-        response
-    }
-
-    pub fn writable(&mut self, _poll: &Poll) -> io::Result<()> {
-        self.buf_out.write_to(&mut self.sock)?;
-
-        Ok(())
-    }
-
-    pub fn error(&mut self, _poll: &Poll) -> Vec<Action> {
-        return vec![ByeClient("Connection reset".to_string())]
-    }
-}
+}
\ No newline at end of file
--- a/gameServer2/src/server/coretypes.rs	Thu Mar 08 16:49:49 2018 +0100
+++ b/gameServer2/src/server/coretypes.rs	Thu Mar 08 15:01:18 2018 -0500
@@ -1,16 +1,16 @@
-#[derive(PartialEq, Debug)]
+#[derive(PartialEq, Eq, Clone, Debug)]
 pub enum ServerVar {
     MOTDNew(String),
     MOTDOld(String),
     LatestProto(u32),
 }
 
-#[derive(PartialEq, Debug)]
+#[derive(PartialEq, Eq, Clone, Debug)]
 pub enum GameCfg {
 
 }
 
-#[derive(PartialEq, Debug)]
+#[derive(PartialEq, Eq, Clone, Debug)]
 pub struct TeamInfo {
     name: String,
     color: u8,
@@ -23,7 +23,7 @@
     hedgehogs: [HedgehogInfo; 8],
 }
 
-#[derive(PartialEq, Debug)]
+#[derive(PartialEq, Eq, Clone, Debug)]
 pub struct HedgehogInfo {
     name: String,
     hat: String,
--- a/gameServer2/src/server/handlers/inroom.rs	Thu Mar 08 16:49:49 2018 +0100
+++ b/gameServer2/src/server/handlers/inroom.rs	Thu Mar 08 15:01:18 2018 -0500
@@ -6,7 +6,7 @@
 use protocol::messages::HWProtocolMessage;
 use protocol::messages::HWServerMessage::*;
 
-pub fn handle(server: &mut HWServer, token: usize, _poll: &mio::Poll, message: HWProtocolMessage) {
+pub fn handle(server: &mut HWServer, token: usize, message: HWProtocolMessage) {
     match message {
         _ => warn!("Unimplemented!"),
     }
--- a/gameServer2/src/server/handlers/lobby.rs	Thu Mar 08 16:49:49 2018 +0100
+++ b/gameServer2/src/server/handlers/lobby.rs	Thu Mar 08 15:01:18 2018 -0500
@@ -6,34 +6,35 @@
 use protocol::messages::HWProtocolMessage;
 use protocol::messages::HWServerMessage::*;
 
-pub fn handle(server: &mut HWServer, token: usize, poll: &mio::Poll, message: HWProtocolMessage) {
+pub fn handle(server: &mut HWServer, token: usize, message: HWProtocolMessage) {
+    use protocol::messages::HWProtocolMessage::*;
     match message {
-        HWProtocolMessage::Chat(msg) => {
-            let chat_msg = ChatMsg(&server.clients[token].nick, &msg).to_raw_protocol();
-            server.react(token, poll, vec![SendAllButMe(chat_msg)]);
+        Chat(msg) => {
+            let chat_msg = ChatMsg(server.clients[token].nick.clone(), msg);
+            server.react(token, vec![SendAllButMe(chat_msg)]);
         },
-        HWProtocolMessage::CreateRoom(name, password) => {
+        CreateRoom(name, password) => {
             let room_exists = server.rooms.iter().find(|&(_, r)| r.name == name).is_some();
             if room_exists {
-                server.react(token, poll, vec![Warn("Room exists".to_string())]);
+                server.react(token, vec![Warn("Room exists".to_string())]);
             } else {
-                let flags_msg = ClientFlags("+hr", &[&server.clients[token].nick]).to_raw_protocol();
+                let flags_msg = ClientFlags("+hr".to_string(), vec![server.clients[token].nick.clone()]);
                 {
                     let c = &mut server.clients[token];
                     c.is_master = true;
                     c.is_ready = true;
                     c.is_joined_mid_game = false;
                 }
-                server.react(token, poll, vec![
+                server.react(token, vec![
                     AddRoom(name, password)
                     , SendMe(flags_msg)
                     ]);
             }
         },
-        HWProtocolMessage::Join(name, password) => {
+        Join(name, password) => {
 
         },
-        HWProtocolMessage::List => warn!("Deprecated LIST message received"),
+        List => warn!("Deprecated LIST message received"),
         _ => warn!("Incorrect command in lobby state"),
     }
 }
--- a/gameServer2/src/server/handlers/loggingin.rs	Thu Mar 08 16:49:49 2018 +0100
+++ b/gameServer2/src/server/handlers/loggingin.rs	Thu Mar 08 15:01:18 2018 -0500
@@ -6,17 +6,17 @@
 use protocol::messages::HWProtocolMessage;
 use protocol::messages::HWServerMessage::*;
 
-pub fn handle(server: &mut HWServer, token: usize, poll: &mio::Poll, message: HWProtocolMessage) {
+pub fn handle(server: & mut HWServer, token: usize, message: HWProtocolMessage) {
     match message {
         HWProtocolMessage::Nick(nick) =>
             if server.clients[token].room_id == None {
-                server.react(token, poll, vec![SendMe(Nick(&nick).to_raw_protocol())]);
+                server.react(token, vec![SendMe(Nick(nick.clone()))]);
                 server.clients[token].nick = nick;
-                server.react(token, poll, vec![CheckRegistered]);
+                server.react(token, vec![CheckRegistered]);
             },
         HWProtocolMessage::Proto(proto) => {
             server.clients[token].protocol_number = proto;
-            server.react(token, poll, vec![CheckRegistered]);
+            server.react(token, vec![CheckRegistered]);
         },
         _ => warn!("Incorrect command in logging-in state"),
     }
--- a/gameServer2/src/server/handlers/mod.rs	Thu Mar 08 16:49:49 2018 +0100
+++ b/gameServer2/src/server/handlers/mod.rs	Thu Mar 08 15:01:18 2018 -0500
@@ -12,23 +12,24 @@
 mod lobby;
 mod inroom;
 
-pub fn handle(server: &mut HWServer, token: usize, poll: &mio::Poll, message: HWProtocolMessage) {
+pub fn handle(server: &mut HWServer, token: usize, message: HWProtocolMessage) {
     match message {
         HWProtocolMessage::Ping =>
-            server.react(token, poll, vec![SendMe(Pong.to_raw_protocol())]),
+            server.react(token, vec![SendMe(Pong)]),
         HWProtocolMessage::Quit(Some(msg)) =>
-            server.react(token, poll, vec![ByeClient("User quit: ".to_string() + &msg)]),
+            server.react(token, vec![ByeClient("User quit: ".to_string() + &msg)]),
         HWProtocolMessage::Quit(None) =>
-            server.react(token, poll, vec![ByeClient("User quit".to_string())]),
+            server.react(token, vec![ByeClient("User quit".to_string())]),
         HWProtocolMessage::Malformed => warn!("Malformed/unknown message"),
         HWProtocolMessage::Empty => warn!("Empty message"),
         _ => {
-            if !server.clients[token].room_id.is_some() {
-                loggingin::handle(server, token, poll, message);
-            } else if server.clients[token].room_id == Some(server.lobby_id) {
-                lobby::handle(server, token, poll, message);
-            } else {
-                inroom::handle(server, token, poll, message);
+            match server.clients[token].room_id {
+                None =>
+                    loggingin::handle(server, token, message),
+                Some(id) if id == server.lobby_id =>
+                    lobby::handle(server, token, message),
+                _ =>
+                    inroom::handle(server, token, message)
             }
         },
     }
--- a/gameServer2/src/server/mod.rs	Thu Mar 08 16:49:49 2018 +0100
+++ b/gameServer2/src/server/mod.rs	Thu Mar 08 15:01:18 2018 -0500
@@ -1,5 +1,7 @@
 pub mod server;
 pub mod client;
+pub mod room;
+pub mod network;
 pub mod coretypes;
 mod actions;
 mod handlers;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gameServer2/src/server/network.rs	Thu Mar 08 15:01:18 2018 -0500
@@ -0,0 +1,211 @@
+extern crate slab;
+
+use std::io::ErrorKind;
+use mio::net::*;
+use super::server::{HWServer, PendingMessage, Destination};
+use super::client::ClientId;
+use slab::Slab;
+
+use mio::net::TcpStream;
+use mio::*;
+use std::io::Write;
+use std::io;
+use netbuf;
+
+use utils;
+use protocol::ProtocolDecoder;
+use protocol::messages::*;
+use std::net::SocketAddr;
+
+pub struct NetworkClient {
+    id: ClientId,
+    socket: TcpStream,
+    peer_addr: SocketAddr,
+    decoder: ProtocolDecoder,
+    buf_out: netbuf::Buf,
+    closed: bool
+}
+
+impl NetworkClient {
+    pub fn new(id: ClientId, socket: TcpStream, peer_addr: SocketAddr) -> NetworkClient {
+        NetworkClient {
+            id, socket, peer_addr,
+            decoder: ProtocolDecoder::new(),
+            buf_out: netbuf::Buf::new(),
+            closed: false
+        }
+    }
+
+    pub fn send_raw_msg(&mut self, msg: &[u8]) {
+        self.buf_out.write(msg).unwrap();
+        self.flush();
+    }
+
+    pub fn send_string(&mut self, msg: &String) {
+        self.send_raw_msg(&msg.as_bytes());
+    }
+
+    pub fn send_msg(&mut self, msg: HWServerMessage) {
+        self.send_string(&msg.to_raw_protocol());
+    }
+
+    fn flush(&mut self) {
+        self.buf_out.write_to(&mut self.socket).unwrap();
+        self.socket.flush().unwrap();
+    }
+
+    pub fn read_messages(&mut self) -> io::Result<Vec<HWProtocolMessage>> {
+        let bytes_read = self.decoder.read_from(&mut self.socket)?;
+        debug!("Read {} bytes", bytes_read);
+
+        if bytes_read == 0 {
+            self.closed = true;
+            info!("EOF for client {} ({})", self.id, self.peer_addr);
+        }
+
+        Ok(self.decoder.extract_messages())
+    }
+
+    pub fn write_messages(&mut self) -> io::Result<()> {
+        self.buf_out.write_to(&mut self.socket)?;
+        Ok(())
+    }
+}
+
+pub struct NetworkLayer {
+    listener: TcpListener,
+    server: HWServer,
+
+    clients: Slab<NetworkClient>
+}
+
+impl NetworkLayer {
+    pub fn new(listener: TcpListener, clients_limit: usize, rooms_limit: usize) -> NetworkLayer {
+        let server = HWServer::new(clients_limit, rooms_limit);
+        let clients = Slab::with_capacity(clients_limit);
+        NetworkLayer {listener, server, clients}
+    }
+
+    pub fn register_server(&self, poll: &Poll) -> io::Result<()> {
+        poll.register(&self.listener, utils::SERVER, Ready::readable(),
+                      PollOpt::edge())
+    }
+
+    fn deregister_client(&mut self, poll: &Poll, id: ClientId) {
+        let mut client_exists = false;
+        if let Some(ref client) = self.clients.get_mut(id) {
+            poll.deregister(&client.socket)
+                .ok().expect("could not deregister socket");
+            info!("client {} ({}) removed", client.id, client.peer_addr);
+            client_exists = true;
+        }
+        if client_exists {
+            self.clients.remove(id);
+        }
+    }
+
+    fn register_client(&mut self, poll: &Poll, id: ClientId, client_socket: TcpStream, addr: SocketAddr) {
+        poll.register(&client_socket, Token(id),
+                      Ready::readable() | Ready::writable(),
+                      PollOpt::edge())
+            .ok().expect("could not register socket with event loop");
+
+        let entry = self.clients.vacant_entry();
+        let client = NetworkClient::new(id, client_socket, addr);
+        info!("client {} ({}) added", client.id, client.peer_addr);
+        entry.insert(client);
+    }
+
+    pub fn accept_client(&mut self, poll: &Poll) -> io::Result<()> {
+        let (client_socket, addr) = self.listener.accept()?;
+        info!("Connected: {}", addr);
+
+        let client_id = self.server.add_client();
+        self.register_client(poll, client_id, client_socket, addr);
+        self.flush_server_messages();
+
+        Ok(())
+    }
+
+    fn flush_server_messages(&mut self) {
+        for PendingMessage(destination, msg) in self.server.output.drain(..) {
+            match destination {
+                Destination::ToSelf(id)  => {
+                    if let Some(ref mut client) = self.clients.get_mut(id) {
+                        client.send_msg(msg)
+                    }
+                }
+                Destination::ToOthers(id) => {
+                    let msg_string = msg.to_raw_protocol();
+                    for item in self.clients.iter_mut() {
+                        if item.0 != id {
+                            item.1.send_string(&msg_string)
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    pub fn client_readable(&mut self, poll: &Poll,
+                           client_id: ClientId) -> io::Result<()> {
+        let mut client_lost = false;
+        let messages;
+        if let Some(ref mut client) = self.clients.get_mut(client_id) {
+            messages = match client.read_messages() {
+                Ok(messages) => Some(messages),
+                Err(ref error) if error.kind() == ErrorKind::WouldBlock => None,
+                Err(error) => return Err(error)
+            };
+            if client.closed {
+                client_lost = true;
+            }
+        } else {
+            warn!("invalid readable client: {}", client_id);
+            messages = None;
+        };
+
+        if client_lost {
+            self.client_error(&poll, client_id)?;
+        } else if let Some(msg) = messages {
+            for message in msg {
+                self.server.handle_msg(client_id, message);
+            }
+            self.flush_server_messages();
+        }
+
+        if !self.server.removed_clients.is_empty() {
+            let ids = self.server.removed_clients.to_vec();
+            self.server.removed_clients.clear();
+            for client_id in ids {
+                self.deregister_client(poll, client_id);
+            }
+        }
+
+        Ok(())
+    }
+
+    pub fn client_writable(&mut self, poll: &Poll,
+                           client_id: ClientId) -> io::Result<()> {
+        if let Some(ref mut client) = self.clients.get_mut(client_id) {
+            match client.write_messages() {
+                Ok(_) => (),
+                Err(ref error) if error.kind() == ErrorKind::WouldBlock => (),
+                Err(error) => return Err(error)
+            }
+        } else {
+            warn!("invalid writable client: {}", client_id);
+        }
+
+        Ok(())
+    }
+
+    pub fn client_error(&mut self, poll: &Poll,
+                        client_id: ClientId) -> io::Result<()> {
+        self.deregister_client(poll, client_id);
+        self.server.client_lost(client_id);
+
+        Ok(())
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gameServer2/src/server/room.rs	Thu Mar 08 15:01:18 2018 -0500
@@ -0,0 +1,21 @@
+pub type RoomId = usize;
+
+pub struct HWRoom {
+    pub id: RoomId,
+    pub name: String,
+    pub password: Option<String>,
+    pub protocol_number: u32,
+    pub ready_players_number: u8,
+}
+
+impl HWRoom {
+    pub fn new(id: RoomId) -> HWRoom {
+        HWRoom {
+            id,
+            name: String::new(),
+            password: None,
+            protocol_number: 0,
+            ready_players_number: 0,
+        }
+    }
+}
\ No newline at end of file
--- a/gameServer2/src/server/server.rs	Thu Mar 08 16:49:49 2018 +0100
+++ b/gameServer2/src/server/server.rs	Thu Mar 08 15:01:18 2018 -0500
@@ -4,107 +4,85 @@
 use std::io;
 
 use utils;
-use super::client::HWClient;
+use super::client::*;
+use super::room::*;
 use super::actions;
+use protocol::messages::*;
+use super::handlers;
 
 type Slab<T> = slab::Slab<T>;
 
+pub enum Destination {
+    ToSelf(ClientId),
+    ToOthers(ClientId)
+}
+
+pub struct PendingMessage(pub Destination, pub HWServerMessage);
+
 pub struct HWServer {
-    listener: TcpListener,
     pub clients: Slab<HWClient>,
     pub rooms: Slab<HWRoom>,
-    pub lobby_id: usize,
+    pub lobby_id: RoomId,
+    pub output: Vec<PendingMessage>,
+    pub removed_clients: Vec<ClientId>,
 }
 
 impl HWServer {
-    pub fn new(listener: TcpListener, clients_limit: usize, rooms_limit: usize) -> HWServer {
-        let mut rooms = Slab::with_capacity(rooms_limit);
-        let token = rooms.insert(HWRoom::new());
-        HWServer {
-            listener: listener,
-            clients: Slab::with_capacity(clients_limit),
-            rooms: rooms,
-            lobby_id: token,
-        }
+    pub fn new(clients_limit: usize, rooms_limit: usize) -> HWServer {
+        let rooms = Slab::with_capacity(rooms_limit);
+        let clients = Slab::with_capacity(clients_limit);
+        let mut server = HWServer {
+            clients, rooms,
+            lobby_id: 0,
+            output: vec![],
+            removed_clients: vec![]
+        };
+        server.lobby_id = server.add_room();
+        server
     }
 
-    pub fn register(&self, poll: &Poll) -> io::Result<()> {
-        poll.register(&self.listener, utils::SERVER, Ready::readable(),
-                      PollOpt::edge())
-    }
-
-    pub fn accept(&mut self, poll: &Poll) -> io::Result<()> {
-        let (sock, addr) = self.listener.accept()?;
-        info!("Connected: {}", addr);
-
-        let client = HWClient::new(sock);
-        let token = self.clients.insert(client);
-
-        self.clients[token].id = token;
-        self.clients[token].register(poll, Token(token));
-
-        Ok(())
+    pub fn add_client(&mut self) -> ClientId {
+        let key: ClientId;
+        {
+            let entry = self.clients.vacant_entry();
+            key = entry.key();
+            let client = HWClient::new(entry.key());
+            entry.insert(client);
+        }
+        self.send_self(key, HWServerMessage::Connected(utils::PROTOCOL_VERSION));
+        key
     }
 
-    pub fn client_readable(&mut self, poll: &Poll,
-                           token: usize) -> io::Result<()> {
-        let actions;
-        {
-            actions = self.clients[token].readable(poll);
-        }
-
-        self.react(token, poll, actions);
-
-        Ok(())
+    pub fn client_lost(&mut self, client_id: ClientId) {
+        actions::run_action(self, client_id,
+                            actions::Action::ByeClient("Connection reset".to_string()));
     }
 
-    pub fn client_writable(&mut self, poll: &Poll,
-                           token: usize) -> io::Result<()> {
-        self.clients[token].writable(poll)?;
-
-        Ok(())
+    pub fn add_room(&mut self) -> RoomId {
+        let entry = self.rooms.vacant_entry();
+        let key = entry.key();
+        let room = HWRoom::new(entry.key());
+        entry.insert(room);
+        key
     }
 
-    pub fn client_error(&mut self, poll: &Poll,
-                           token: usize) -> io::Result<()> {
-        let actions;
-        {
-            actions = self.clients[token].error(poll);
-        }
+    pub fn handle_msg(&mut self, client_id: ClientId, msg: HWProtocolMessage) {
+        handlers::handle(self, client_id, msg);
+    }
 
-        self.react(token, poll, actions);
-
-        Ok(())
+    pub fn send_self(&mut self, client_id: ClientId, msg: HWServerMessage) {
+        self.output.push(PendingMessage(
+            Destination::ToSelf(client_id), msg));
     }
 
-    pub fn send(&mut self, token: usize, msg: &String) {
-        self.clients[token].send_string(msg);
+    pub fn send_others(&mut self, client_id: ClientId, msg: HWServerMessage) {
+        self.output.push(PendingMessage(
+            Destination::ToOthers(client_id), msg));
     }
 
-    pub fn react(&mut self, token: usize, poll: &Poll, actions: Vec<actions::Action>) {
+    pub fn react(&mut self, client_id: ClientId, actions: Vec<actions::Action>) {
         for action in actions {
-            actions::run_action(self, token, poll, action);
+            actions::run_action(self, client_id, action);
         }
     }
 }
-
-
-pub struct HWRoom {
-    pub id: usize,
-    pub name: String,
-    pub password: Option<String>,
-    pub protocol_number: u32,
-    pub ready_players_number: u8,
-}
-
-impl HWRoom {
-    pub fn new() -> HWRoom {
-        HWRoom {
-            id: 0,
-            name: String::new(),
-            password: None,
-            protocol_number: 0,
-            ready_players_number: 0,
-        }
-    }
-}