partial room implementation
authoralfadur
Mon, 18 Jun 2018 09:22:53 -0400
changeset 13416 cdf69667593b
parent 13415 0eedc17055a0
child 13417 8d7d48736184
partial room implementation
gameServer2/src/protocol/messages.rs
gameServer2/src/protocol/mod.rs
gameServer2/src/protocol/parser.rs
gameServer2/src/server/actions.rs
gameServer2/src/server/handlers/inroom.rs
gameServer2/src/server/handlers/lobby.rs
gameServer2/src/server/handlers/loggingin.rs
gameServer2/src/server/network.rs
gameServer2/src/server/room.rs
gameServer2/src/server/server.rs
gameServer2/src/utils.rs
--- a/gameServer2/src/protocol/messages.rs	Thu Jun 14 16:44:27 2018 -0400
+++ b/gameServer2/src/protocol/messages.rs	Mon Jun 18 09:22:53 2018 -0400
@@ -24,7 +24,7 @@
     List,
     Chat(String),
     CreateRoom(String, Option<String>),
-    Join(String, Option<String>),
+    JoinRoom(String, Option<String>),
     Follow(String),
     Rnd(Vec<String>),
     Kick(String),
@@ -69,105 +69,103 @@
     Empty,
 }
 
+#[derive(Debug)]
 pub enum HWServerMessage {
     Ping,
     Pong,
     Bye(String),
     Nick(String),
-    LobbyLeft(String),
+    Proto(u32),
+    LobbyLeft(String, String),
     LobbyJoined(Vec<String>),
     ChatMsg(String, String),
     ClientFlags(String, Vec<String>),
+    Rooms(Vec<String>),
+    RoomAdd(Vec<String>),
+    RoomJoined(Vec<String>),
+    RoomLeft(String, String),
+    RoomRemove(String),
+    RoomUpdated(String, Vec<String>),
+    ServerMessage(String),
 
     Warning(String),
+    Error(String),
     Connected(u32),
     Unreachable,
 }
 
-fn construct_message(msg: & [&str]) -> String {
-    let mut m = String::with_capacity(64);
-
-    for s in msg {
-        m.push_str(s);
-        m.push('\n');
-    }
-    m.push('\n');
-
-    m
-}
-
 impl<'a> HWProtocolMessage {
     pub fn to_raw_protocol(&self) -> String {
         use self::HWProtocolMessage::*;
-        match *self {
+        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),
+            Quit(Some(msg)) => format!("QUIT\n{}\n\n", msg),
+            Global(msg) => format!("CMD\nGLOBAL\n{}\n\n", msg),
+            Watch(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),
+            Info(info) => format!("CMD\nINFO\n{}\n\n", info),
+            Nick(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) =>
+            Password(p, s) => format!("PASSWORD\n{}\n{}\n\n", p, s), //?
+            Checker(i, n, 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) =>
+            Chat(msg) => format!("CHAT\n{}\n\n", msg),
+            CreateRoom(name, None) =>
                 format!("CREATE_ROOM\n{}\n\n", name),
-            CreateRoom(ref name, Some(ref password)) =>
+            CreateRoom(name, Some(password)) =>
                 format!("CREATE_ROOM\n{}\n{}\n\n", name, password),
-            Join(ref name, None) =>
+            JoinRoom(name, None) =>
                 format!("JOIN\n{}\n\n", name),
-            Join(ref name, Some(ref arg)) =>
+            JoinRoom(name, Some(arg)) =>
                 format!("JOIN\n{}\n{}\n\n", name, arg),
-            Follow(ref name) =>
+            Follow(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) =>
+            Kick(name) => format!("KICK\n{}\n\n", name),
+            Ban(name, reason, time) =>
                 format!("BAN\n{}\n{}\n{}\n\n", name, reason, time),
-            BanIP(ref ip, ref reason, time) =>
+            BanIP(ip, reason, time) =>
                 format!("BAN_IP\n{}\n{}\n{}\n\n", ip, reason, time),
-            BanNick(ref nick, ref reason, time) =>
+            BanNick(nick, 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),
+            Unban(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),
+            Part(Some(msg)) => format!("CMD\nPART\n{}\n\n", msg),
             //Cfg(GameCfg) ??
             //AddTeam(TeamInfo) ??,
-            RemoveTeam(ref name) => format!("REMOVE_TEAM\n{}\n\n", name),
+            RemoveTeam(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),
+            EngineMessage(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),
+            RoomName(name) => format!("ROOM_NAME\n{}\n\n", name),
+            Delegate(name) => format!("CMD\nDELEGATE\n{}\n\n", name),
+            TeamChat(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),
+            Greeting(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),
+            Vote(msg) => format!("CMD\nVOTE\n{}\n\n", msg),
+            ForceVote(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),
+            Delete(room) => format!("CMD\nDELETE\n{}\n\n", room),
+            SaveRoom(room) => format!("CMD\nSAVEROOM\n{}\n\n", room),
+            LoadRoom(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")
@@ -175,40 +173,74 @@
     }
 }
 
+macro_rules! const_braces {
+    ($e: expr) => { "{}\n" }
+}
+
+macro_rules! msg {
+    [$($part: expr),*] => {
+        format!(concat!($(const_braces!($part)),*, "\n"), $($part),*);
+    };
+}
+
+fn construct_message(mut msg: Vec<&str>) -> String {
+    msg.push("\n");
+    msg.join("\n")
+}
+
 impl HWServerMessage {
     pub fn to_raw_protocol(&self) -> String {
         use self::HWServerMessage::*;
         match self {
-            &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()
-                ]),
-            &Bye(ref msg) => construct_message(&["BYE", &msg]),
-            &Nick(ref nick) => construct_message(&["NICK", &nick]),
-            &LobbyLeft(ref nick)
-                => construct_message(&["LOBBY_LEFT", &nick]),
-            &LobbyJoined(ref nicks)
-                => {
+            Ping => msg!["PING"],
+            Pong => msg!["PONG"],
+            Connected(protocol_version) => msg![
+                "CONNECTED",
+                "Hedgewars server http://www.hedgewars.org/",
+                protocol_version],
+            Bye(msg) => msg!["BYE", msg],
+            Nick(nick) => msg!["NICK", nick],
+            Proto(proto) => msg!["PROTO", proto],
+            LobbyLeft(nick, msg) => msg!["LOBBY:LEFT", nick, msg],
+            LobbyJoined(nicks) => {
                 let mut v = vec!["LOBBY:JOINED"];
                 v.extend(nicks.iter().map(|n| { &n[..] }));
-                construct_message(&v)
+                construct_message(v)
             },
-            &ClientFlags(ref flags, ref nicks)
+            ClientFlags(flags, nicks)
                 => {
                 let mut v = vec!["CLIENT_FLAGS"];
                 v.push(&flags[..]);
                 v.extend(nicks.iter().map(|n| { &n[..] }));
-                construct_message(&v)
+                construct_message(v)
+            },
+            Rooms(info) => {
+                let mut v = vec!["ROOMS"];
+                v.extend(info.iter().map(|n| { &n[..] }));
+                construct_message(v)
+            },
+            RoomAdd(info) => {
+                let mut v = vec!["ROOM", "ADD"];
+                v.extend(info.iter().map(|n| { &n[..] }));
+                construct_message(v)
             },
-            &ChatMsg(ref nick, ref msg)
-                => construct_message(&["CHAT", &nick, &msg]),
-            &Warning(ref msg)
-                => construct_message(&["WARNING", &msg]),
-            _ => construct_message(&["ERROR", "UNIMPLEMENTED"]),
+            RoomJoined(nicks) => {
+                let mut v = vec!["JOINED"];
+                v.extend(nicks.iter().map(|n| { &n[..] }));
+                construct_message(v)
+            },
+            RoomLeft(nick, msg) => msg!["LEFT", nick, msg],
+            RoomRemove(name) => msg!["ROOM", "DEL", name],
+            RoomUpdated(name, info) => {
+                let mut v = vec!["ROOM", "UPD", name];
+                v.extend(info.iter().map(|n| { &n[..] }));
+                construct_message(v)
+            }
+            ChatMsg(nick, msg) => msg!["CHAT", nick, msg],
+            ServerMessage(msg) => msg!["SERVER_MESSAGE", msg],
+            Warning(msg) => msg!["WARNING", msg],
+            Error(msg) => msg!["ERROR", msg],
+            _ => msg!["ERROR", "UNIMPLEMENTED"],
         }
     }
 }
--- a/gameServer2/src/protocol/mod.rs	Thu Jun 14 16:44:27 2018 -0400
+++ b/gameServer2/src/protocol/mod.rs	Mon Jun 18 09:22:53 2018 -0400
@@ -4,6 +4,7 @@
 use nom::IResult;
 
 pub mod messages;
+pub mod test;
 mod parser;
 
 pub struct ProtocolDecoder {
--- a/gameServer2/src/protocol/parser.rs	Thu Jun 14 16:44:27 2018 -0400
+++ b/gameServer2/src/protocol/parser.rs	Mon Jun 18 09:22:53 2018 -0400
@@ -1,15 +1,13 @@
 use nom::*;
 
-use std::str;
-use std::str::FromStr;
-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;
+use std::{
+    str, str::FromStr,
+    ops::Range
+};
+use super::{
+    messages::{HWProtocolMessage, HWProtocolMessage::*},
+    test::gen_proto_msg
+};
 
 named!(end_of_message, tag!("\n\n"));
 named!(str_line<&[u8],   &str>, map_res!(not_line_ending, str::from_utf8));
@@ -36,6 +34,7 @@
       do_parse!(tag!("NICK")    >> eol >> n: a_line >> (Nick(n)))
     | do_parse!(tag!("INFO")    >> eol >> n: a_line >> (Info(n)))
     | do_parse!(tag!("CHAT")    >> eol >> m: a_line >> (Chat(m)))
+    | do_parse!(tag!("PART")    >> msg: opt_param   >> (Part(msg)))
     | do_parse!(tag!("FOLLOW")  >> eol >> n: a_line >> (Follow(n)))
     | do_parse!(tag!("KICK")    >> eol >> n: a_line >> (Kick(n)))
     | do_parse!(tag!("UNBAN")   >> eol >> n: a_line >> (Unban(n)))
@@ -85,10 +84,10 @@
                     n: a_line       >>
                     p: opt_param    >>
                     (CreateRoom(n, p)))
-    | do_parse!(tag!("JOIN")        >> eol >>
+    | do_parse!(tag!("JOIN_ROOM")   >> eol >>
                     n: a_line       >>
                     p: opt_param    >>
-                    (Join(n, p)))
+                    (JoinRoom(n, p)))
     | do_parse!(tag!("BAN")    >> eol >>
                     n: a_line     >> eol >>
                     r: a_line     >> eol >>
@@ -127,130 +126,6 @@
 
 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()) {
--- a/gameServer2/src/server/actions.rs	Thu Jun 14 16:44:27 2018 -0400
+++ b/gameServer2/src/server/actions.rs	Mon Jun 18 09:22:53 2018 -0400
@@ -1,40 +1,75 @@
-use mio;
-use std::io::Write;
-use std::io;
-
-use super::server::HWServer;
-use super::room::HWRoom;
-use protocol::messages::HWProtocolMessage;
-use protocol::messages::HWServerMessage;
-use protocol::messages::HWServerMessage::*;
-use super::handlers;
+use std::{
+    io, io::Write
+};
+use super::{
+    server::HWServer,
+    client::ClientId,
+    room::HWRoom,
+    handlers
+};
+use protocol::messages::{
+    HWProtocolMessage,
+    HWServerMessage,
+    HWServerMessage::*
+};
 
 pub enum Action {
+    SendAll(HWServerMessage),
     SendMe(HWServerMessage),
     SendAllButMe(HWServerMessage),
+    SendToSelected(Vec<ClientId>, HWServerMessage),
     RemoveClient,
     ByeClient(String),
     ReactProtocolMessage(HWProtocolMessage),
     CheckRegistered,
     JoinLobby,
     AddRoom(String, Option<String>),
+    RemoveRoom(RoomId),
+    MoveToRoom(RoomId),
+    MoveToLobby(String),
+    ChangeMaster(RoomId, Option<ClientId>),
+    SendRoomUpdate(Option<String>),
     Warn(String),
+    ProtocolError(String)
 }
 
 use self::Action::*;
+use server::room::RoomId;
 
 pub fn run_action(server: &mut HWServer, token: usize, action: Action) {
     match action {
+        SendAll(msg) =>
+            server.send_all(msg),
         SendMe(msg) =>
             server.send_self(token, msg),
-        SendAllButMe(msg) => {
-            server.send_others(token, msg)
-        },
+        SendAllButMe(msg) =>
+            server.send_others(token, msg),
+        SendToSelected(client_ids, msg) =>
+            server.send_to_selected(client_ids, msg),
         ByeClient(msg) => {
+            let room_id;
+            let nick;
+            {
+                let c = &server.clients[token];
+                room_id = c.room_id;
+                nick = c.nick.clone();
+            }
+
+            let action = room_id.map (|id| {
+                if id == server.lobby_id {
+                    SendAll(LobbyLeft(nick, msg.clone()))
+                } else {
+                    MoveToLobby(format!("quit: {}", msg.clone()))
+                }
+            });
+
+            if let Some(action) = action {
+                server.react(token, vec![action]);
+            }
+
             server.react(token, vec![
                 SendMe(Bye(msg)),
-                RemoveClient,
-                ]);
+                RemoveClient]);
         },
         RemoveClient => {
             server.removed_clients.push(token);
@@ -64,26 +99,154 @@
                 joined_msg = LobbyJoined(lobby_nicks);
             }
             let everyone_msg = LobbyJoined(vec![server.clients[token].nick.clone()]);
+            let flags_msg = ClientFlags(
+                "+i".to_string(),
+                server.clients.iter()
+                    .filter(|(_, c)| c.room_id.is_some())
+                    .map(|(_, c)| c.nick.clone())
+                    .collect());
+            let server_msg = ServerMessage("\u{1f994} is watching".to_string());
+            let rooms_msg = Rooms(server.rooms.iter()
+                .filter(|(id, _)| *id != server.lobby_id)
+                .flat_map(|(_, r)|
+                    r.info(r.master_id.map(|id| &server.clients[id])))
+                .collect());
             server.react(token, vec![
                 SendAllButMe(everyone_msg),
                 SendMe(joined_msg),
+                SendMe(flags_msg),
+                SendMe(server_msg),
+                SendMe(rooms_msg),
                 ]);
         },
         AddRoom(name, password) => {
+            let room_protocol;
+            let room_info;
             let room_id = server.add_room();;
             {
                 let r = &mut server.rooms[room_id];
                 let c = &mut server.clients[token];
+                r.master_id = Some(c.id);
                 r.name = name;
                 r.password = password;
-                r.ready_players_number = 1;
                 r.protocol_number = c.protocol_number;
+
+                room_protocol = r.protocol_number;
+                room_info = r.info(Some(&c));
+            }
+            let protocol_client_ids = server.protocol_clients(room_protocol);
+            server.react(token, vec![
+                SendToSelected(protocol_client_ids, RoomAdd(room_info)),
+                MoveToRoom(room_id)]);
+        },
+        RemoveRoom(room_id) => {
+            let room_protocol;
+            let room_name;
+            {
+                let r = &mut server.rooms[room_id];
+                room_protocol = r.protocol_number;
+                room_name = r.name.clone();
+            }
+            server.rooms.remove(room_id);
+            let protocol_client_ids = server.protocol_clients(room_protocol);
+            server.react(token, vec![
+                SendToSelected(protocol_client_ids, RoomRemove(room_name))]);
+        }
+        MoveToRoom(room_id) => {
+            let flags_msg;
+            let nick;
+            {
+                let r = &mut server.rooms[room_id];
+                let c = &mut server.clients[token];
+                r.players_number += 1;
                 c.room_id = Some(room_id);
+                c.is_joined_mid_game = false;
+                if r.master_id == Some(c.id) {
+                    r.ready_players_number += 1;
+                    c.is_master = true;
+                    c.is_ready = true;
+                } else {
+                    c.is_ready = false;
+                    c.is_master = false;
+                }
+                flags_msg = ClientFlags("+i".to_string(), vec![c.nick.clone()]);
+                nick = c.nick.clone();
             }
+            let rooms_client_ids = server.room_clients(room_id);
+            server.react(token, vec![
+                SendToSelected(rooms_client_ids, RoomJoined(vec![nick])),
+                SendAll(flags_msg),
+                SendRoomUpdate(None)]);
         },
+        MoveToLobby(msg) => {
+            let mut actions = Vec::new();
+            let other_client_ids = server.other_clients_in_room(token);
+            let lobby_id = server.lobby_id;
+            if let (c, Some(r)) = server.client_and_room(token) {
+                r.players_number -= 1;
+                if c.is_ready {
+                    r.ready_players_number -= 1;
+                }
+                if r.players_number > 0 && c.is_master {
+                    actions.push(ChangeMaster(r.id, None));
+                }
+                actions.push(SendToSelected(other_client_ids, RoomLeft(c.nick.clone(), msg)));
+                actions.push(SendAll(ClientFlags("-i".to_string(), vec![c.nick.clone()])));
+                actions.push(SendRoomUpdate(Some(r.name.clone())));
+            }
+            server.react(token, actions);
+            actions = Vec::new();
+
+            if let (c, Some(r)) = server.client_and_room(token) {
+                c.room_id = Some(lobby_id);
+                if r.players_number == 0 {
+                    actions.push(RemoveRoom(r.id));
+                }
+            }
+            server.react(token, actions)
+        }
+        ChangeMaster(room_id, new_id) => {
+            let mut actions = Vec::new();
+            let room_client_ids = server.room_clients(room_id);
+            let new_id = new_id.or_else(||
+                room_client_ids.iter().find(|id| **id != token).map(|id| *id));
+            let new_nick = new_id.map(|id| server.clients[id].nick.clone());
+
+            if let (c, Some(r)) = server.client_and_room(token) {
+                if let Some(id) = r.master_id {
+                    c.is_master = false;
+                    r.master_id = None;
+                    actions.push(SendToSelected(room_client_ids.clone(), ClientFlags("-h".to_string(), vec![c.nick.clone()])));
+                }
+                r.master_id = new_id;
+                if let Some(nick) = new_nick {
+                    actions.push(SendToSelected(room_client_ids, ClientFlags("+h".to_string(), vec![nick])));
+                }
+            }
+            new_id.map(|id| server.clients[id].is_master = true);
+            server.react(token, actions);
+        }
+        SendRoomUpdate(old_name) => {
+            let room_data =
+                if let (c, Some(r)) = server.client_and_room(token) {
+                    let name = old_name.unwrap_or_else(|| r.name.clone());
+                    Some((name, r.protocol_number, r.info(Some(&c))))
+                } else {
+                    None
+                };
+
+            if let Some((room_name, protocol, room_info)) = room_data {
+                let protocol_clients = server.protocol_clients(protocol);
+                server.react(token,
+                             vec![SendToSelected(protocol_clients, RoomUpdated(room_name, room_info))]);
+            }
+        }
+
         Warn(msg) => {
             run_action(server, token,SendMe(Warning(msg)));
         }
-        //_ => unimplemented!(),
+        ProtocolError(msg) => {
+            run_action(server, token, SendMe(Error(msg)))
+        }
     }
 }
--- a/gameServer2/src/server/handlers/inroom.rs	Thu Jun 14 16:44:27 2018 -0400
+++ b/gameServer2/src/server/handlers/inroom.rs	Mon Jun 18 09:22:53 2018 -0400
@@ -1,13 +1,52 @@
 use mio;
 
-use server::server::HWServer;
-use server::actions::Action;
-use server::actions::Action::*;
-use protocol::messages::HWProtocolMessage;
-use protocol::messages::HWServerMessage::*;
+use server::{
+    server::HWServer,
+    actions::{Action, Action::*}
+};
+use protocol::messages::{
+    HWProtocolMessage,
+    HWServerMessage::*
+};
+use utils::is_name_illegal;
+use std::mem::swap;
 
 pub fn handle(server: &mut HWServer, token: usize, message: HWProtocolMessage) {
+    use protocol::messages::HWProtocolMessage::*;
     match message {
+        Part(None) => server.react(token, vec![
+            MoveToLobby("part".to_string())]),
+        Part(Some(msg)) => server.react(token, vec![
+            MoveToLobby(format!("part: {}", msg))]),
+        Chat(msg) => {
+            let chat_msg;
+            let room_id;
+            {
+                let c = &mut server.clients[token];
+                chat_msg = ChatMsg(c.nick.clone(), msg);
+                room_id = c.room_id;
+            }
+            let client_ids = server.other_clients_in_room(token);
+            server.react(token, vec![
+                SendToSelected(client_ids, chat_msg)]);
+        },
+        RoomName(new_name) => {
+            let actions =
+                if is_name_illegal(&new_name) {
+                    vec![Warn("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())]
+                } else if server.has_room(&new_name) {
+                    vec![Warn("A room with the same name already exists.".to_string())]
+                } else {
+                    let mut old_name = new_name.clone();
+                    if let (c, Some(r)) = server.client_and_room(token) {
+                        swap(&mut r.name, &mut old_name);
+                        vec![SendRoomUpdate(Some(old_name))]
+                    } else {
+                        Vec::new()
+                    }
+                };
+            server.react(token, actions);
+        }
         _ => warn!("Unimplemented!"),
     }
 }
--- a/gameServer2/src/server/handlers/lobby.rs	Thu Jun 14 16:44:27 2018 -0400
+++ b/gameServer2/src/server/handlers/lobby.rs	Mon Jun 18 09:22:53 2018 -0400
@@ -1,38 +1,60 @@
 use mio;
 
-use server::server::HWServer;
-use server::actions::Action;
-use server::actions::Action::*;
-use protocol::messages::HWProtocolMessage;
-use protocol::messages::HWServerMessage::*;
+use server::{
+    server::HWServer,
+    actions::{Action, Action::*}
+};
+use protocol::messages::{
+    HWProtocolMessage,
+    HWServerMessage::*
+};
+use utils::is_name_illegal;
 
 pub fn handle(server: &mut HWServer, token: usize, message: HWProtocolMessage) {
     use protocol::messages::HWProtocolMessage::*;
     match message {
+        CreateRoom(name, password) => {
+            let actions =
+                if is_name_illegal(&name) {
+                    vec![Warn("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())]
+                } else if server.has_room(&name) {
+                    vec![Warn("A room with the same name already exists.".to_string())]
+                } else {
+                    let flags_msg = ClientFlags(
+                        "+hr".to_string(),
+                        vec![server.clients[token].nick.clone()]);
+                    vec![AddRoom(name, password),
+                         SendMe(flags_msg)]
+                };
+            server.react(token, actions);
+        },
         Chat(msg) => {
             let chat_msg = ChatMsg(server.clients[token].nick.clone(), msg);
             server.react(token, vec![SendAllButMe(chat_msg)]);
         },
-        CreateRoom(name, password) => {
-            let room_exists = server.rooms.iter().find(|&(_, r)| r.name == name).is_some();
-            if room_exists {
-                server.react(token, vec![Warn("Room exists".to_string())]);
-            } else {
-                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, vec![
-                    AddRoom(name, password)
-                    , SendMe(flags_msg)
-                    ]);
+        JoinRoom(name, password) => {
+            let actions;
+            {
+                let room = server.rooms.iter().find(|(_, r)| r.name == name);
+                let room_id = room.map(|(_, r)| r.id);
+                let nicks = server.clients.iter()
+                    .filter(|(_, c)| c.room_id == room_id)
+                    .map(|(_, c)| c.nick.clone())
+                    .collect();
+                let c = &mut server.clients[token];
+                actions = match room {
+                    None => vec![Warn("No such room.".to_string())],
+                    Some((_, r)) => {
+                        if c.protocol_number != r.protocol_number {
+                            vec![Warn("Room version incompatible to your Hedgewars version!".to_string())]
+                        } else {
+                            vec![MoveToRoom(r.id),
+                                 SendMe(RoomJoined(nicks))]
+                        }
+                    }
+                };
             }
-        },
-        Join(name, password) => {
-
+            server.react(token, actions);
         },
         List => warn!("Deprecated LIST message received"),
         _ => warn!("Incorrect command in lobby state"),
--- a/gameServer2/src/server/handlers/loggingin.rs	Thu Jun 14 16:44:27 2018 -0400
+++ b/gameServer2/src/server/handlers/loggingin.rs	Mon Jun 18 09:22:53 2018 -0400
@@ -5,18 +5,47 @@
 use server::actions::Action::*;
 use protocol::messages::HWProtocolMessage;
 use protocol::messages::HWServerMessage::*;
+use utils::is_name_illegal;
 
 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, vec![SendMe(Nick(nick.clone()))]);
-                server.clients[token].nick = nick;
-                server.react(token, vec![CheckRegistered]);
-            },
+        HWProtocolMessage::Nick(nick) => {
+            let actions;
+            {
+                let client = &mut server.clients[token];
+                debug!("{} {}", nick, is_name_illegal(&nick));
+                actions = if client.room_id != None {
+                    unreachable!()
+                }
+                else if !client.nick.is_empty() {
+                    vec![ProtocolError("Nickname already provided.".to_string())]
+                }
+                else if     is_name_illegal(&nick) {
+                    vec![ByeClient("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())]
+                }
+                else {
+                    client.nick = nick.clone();
+                    vec![SendMe(Nick(nick)), CheckRegistered]
+                };
+            }
+            server.react(token, actions);
+        },
         HWProtocolMessage::Proto(proto) => {
-            server.clients[token].protocol_number = proto;
-            server.react(token, vec![CheckRegistered]);
+            let actions;
+            {
+                let client = &mut server.clients[token];
+                actions = if client.protocol_number != 0 {
+                    vec![ProtocolError("Protocol already known.".to_string())]
+                }
+                else if proto == 0 {
+                    vec![ProtocolError("Bad number.".to_string())]
+                }
+                else {
+                    client.protocol_number = proto;
+                    vec![SendMe(Proto(proto)), CheckRegistered]
+                };
+            }
+            server.react(token, actions);
         },
         _ => warn!("Incorrect command in logging-in state"),
     }
--- a/gameServer2/src/server/network.rs	Thu Jun 14 16:44:27 2018 -0400
+++ b/gameServer2/src/server/network.rs	Mon Jun 18 09:22:53 2018 -0400
@@ -55,7 +55,7 @@
         let result = loop {
             match self.decoder.read_from(&mut self.socket) {
                 Ok(bytes) => {
-                    debug!("Read {} bytes", bytes);
+                    debug!("Client {}: read {} bytes", self.id, bytes);
                     bytes_read += bytes;
                     if bytes == 0 {
                         let result = if bytes_read == 0 {
@@ -167,9 +167,17 @@
     fn flush_server_messages(&mut self) {
         debug!("{} pending server messages", self.server.output.len());
         for PendingMessage(destination, msg) in self.server.output.drain(..) {
+            debug!("Message {:?} to {:?}", msg, destination);
             match destination {
+                Destination::ToAll => {
+                    let msg_string = msg.to_raw_protocol();
+                    for (client_id, client) in self.clients.iter_mut() {
+                        client.send_string(&msg_string);
+                        self.pending.insert((client_id, NetworkClientState::NeedsWrite));
+                    }
+                },
                 Destination::ToSelf(id)  => {
-                    if let Some(ref mut client) = self.clients.get_mut(id) {
+                    if let Some(client) = self.clients.get_mut(id) {
                         client.send_msg(msg);
                         self.pending.insert((id, NetworkClientState::NeedsWrite));
                     }
@@ -182,6 +190,15 @@
                             self.pending.insert((client_id, NetworkClientState::NeedsWrite));
                         }
                     }
+                },
+                Destination::ToSelected(client_ids) => {
+                    let msg_string = msg.to_raw_protocol();
+                    for id in client_ids {
+                        if let Some(client) = self.clients.get_mut(id) {
+                            client.send_string(&msg_string);
+                            self.pending.insert((id, NetworkClientState::NeedsWrite));
+                        }
+                    }
                 }
             }
         }
--- a/gameServer2/src/server/room.rs	Thu Jun 14 16:44:27 2018 -0400
+++ b/gameServer2/src/server/room.rs	Mon Jun 18 09:22:53 2018 -0400
@@ -1,21 +1,48 @@
+use server::{
+    coretypes::TeamInfo,
+    client::{ClientId, HWClient}
+};
+
 pub type RoomId = usize;
 
 pub struct HWRoom {
     pub id: RoomId,
+    pub master_id: Option<ClientId>,
     pub name: String,
     pub password: Option<String>,
     pub protocol_number: u32,
+
+    pub players_number: u32,
     pub ready_players_number: u8,
+    pub teams: Vec<TeamInfo>,
 }
 
 impl HWRoom {
     pub fn new(id: RoomId) -> HWRoom {
         HWRoom {
             id,
+            master_id: None,
             name: String::new(),
             password: None,
             protocol_number: 0,
+            players_number: 0,
             ready_players_number: 0,
+            teams: Vec::new()
         }
     }
+
+    pub fn info(&self, master: Option<&HWClient>) -> Vec<String> {
+        let flags = "-".to_string();
+        vec![
+            flags,
+            self.name.clone(),
+            self.players_number.to_string(),
+            self.teams.len().to_string(),
+            master.map_or("?", |c| &c.nick).to_string(),
+            "Default".to_string(),
+            "Default".to_string(),
+            "Default".to_string(),
+            "Default".to_string(),
+        ]
+    }
 }
\ No newline at end of file
--- a/gameServer2/src/server/server.rs	Thu Jun 14 16:44:27 2018 -0400
+++ b/gameServer2/src/server/server.rs	Mon Jun 18 09:22:53 2018 -0400
@@ -1,20 +1,18 @@
 use slab;
-use mio::net::*;
-use mio::*;
-use std::io;
-
 use utils;
-use super::client::*;
-use super::room::*;
-use super::actions;
+use super::{
+    client::*, room::*, actions, handlers
+};
 use protocol::messages::*;
-use super::handlers;
 
 type Slab<T> = slab::Slab<T>;
 
+#[derive(Debug)]
 pub enum Destination {
+    ToAll,
     ToSelf(ClientId),
-    ToOthers(ClientId)
+    ToOthers(ClientId),
+    ToSelected(Vec<ClientId>)
 }
 
 pub struct PendingMessage(pub Destination, pub HWServerMessage);
@@ -67,9 +65,15 @@
     }
 
     pub fn handle_msg(&mut self, client_id: ClientId, msg: HWProtocolMessage) {
+        debug!("Handling message {:?} for client {}", msg, client_id);
         handlers::handle(self, client_id, msg);
     }
 
+    pub fn send_all(&mut self, msg: HWServerMessage) {
+        self.output.push(PendingMessage(
+            Destination::ToAll, msg));
+    }
+
     pub fn send_self(&mut self, client_id: ClientId, msg: HWServerMessage) {
         self.output.push(PendingMessage(
             Destination::ToSelf(client_id), msg));
@@ -80,9 +84,54 @@
             Destination::ToOthers(client_id), msg));
     }
 
+    pub fn send_to_selected(&mut self, client_ids: Vec<ClientId>, msg: HWServerMessage) {
+        self.output.push(PendingMessage(
+            Destination::ToSelected(client_ids), msg));
+    }
+
     pub fn react(&mut self, client_id: ClientId, actions: Vec<actions::Action>) {
         for action in actions {
             actions::run_action(self, client_id, action);
         }
     }
-}
+
+    pub fn has_room(&self, name: &str) -> bool {
+        self.rooms.iter().any(|(_, r)| r.name == name)
+    }
+
+    pub fn find_room(&self, name: &str) -> Option<&HWRoom> {
+        self.rooms.iter().find(|(_, r)| r.name == name).map(|(_, r)| r)
+    }
+
+    pub fn find_room_mut(&mut self, name: &str) -> Option<&mut HWRoom> {
+        self.rooms.iter_mut().find(|(_, r)| r.name == name).map(|(_, r)| r)
+    }
+
+    pub fn select_clients<F>(&self, f: F) -> Vec<ClientId>
+        where F: Fn(&(usize, &HWClient)) -> bool {
+        self.clients.iter().filter(f)
+            .map(|(_, c)| c.id).collect()
+    }
+
+    pub fn room_clients(&self, room_id: RoomId) -> Vec<ClientId> {
+        self.select_clients(|(_, c)| c.room_id == Some(room_id))
+    }
+
+    pub fn protocol_clients(&self, protocol: u32) -> Vec<ClientId> {
+        self.select_clients(|(_, c)| c.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.select_clients(|(id, c)| *id != self_id && c.room_id == room_id )
+    }
+
+    pub fn client_and_room(&mut self, client_id: ClientId) -> (&mut HWClient, Option<&mut HWRoom>) {
+        let c = &mut self.clients[client_id];
+        if let Some(room_id) = c.room_id {
+            (c, Some(&mut self.rooms[room_id]))
+        } else {
+            (c, None)
+        }
+    }
+}
\ No newline at end of file
--- a/gameServer2/src/utils.rs	Thu Jun 14 16:44:27 2018 -0400
+++ b/gameServer2/src/utils.rs	Mon Jun 18 09:22:53 2018 -0400
@@ -2,3 +2,11 @@
 
 pub const PROTOCOL_VERSION : u32 = 3;
 pub const SERVER: mio::Token = mio::Token(1000000000 + 0);
+
+pub fn is_name_illegal(name: &str ) -> bool{
+    name.len() > 40 ||
+        name.trim().is_empty() ||
+        name.chars().any(|c|
+            "$()*+?[]^{|}\x7F".contains(c) ||
+                '\x00' <= c && c <= '\x1F')
+}
\ No newline at end of file