separated the server logic from all the async io mess.
--- 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,
- }
- }
-}