--- 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