--- a/gameServer2/src/protocol/messages.rs Thu Jun 21 23:09:20 2018 +0200
+++ b/gameServer2/src/protocol/messages.rs Thu Jun 21 17:23:10 2018 -0400
@@ -86,8 +86,13 @@
RoomLeft(String, String),
RoomRemove(String),
RoomUpdated(String, Vec<String>),
+ TeamAdd(Vec<String>),
+ TeamRemove(String),
+ TeamAccepted(String),
+ TeamColor(String, u8),
+ HedgehogsNumber(String, u8),
+
ServerMessage(String),
-
Warning(String),
Error(String),
Connected(u32),
@@ -183,9 +188,11 @@
};
}
-fn construct_message(mut msg: Vec<&str>) -> String {
- msg.push("\n");
- msg.join("\n")
+fn construct_message(header: &[&str], msg: &Vec<String>) -> String {
+ let mut v: Vec<_> = header.iter().map(|s| *s).collect();
+ v.extend(msg.iter().map(|s| &s[..]));
+ v.push("\n");
+ v.join("\n")
}
impl HWServerMessage {
@@ -202,40 +209,26 @@
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)
- },
- ClientFlags(flags, nicks)
- => {
- let mut v = vec!["CLIENT_FLAGS"];
- v.push(&flags[..]);
- v.extend(nicks.iter().map(|n| { &n[..] }));
- 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)
- },
- RoomJoined(nicks) => {
- let mut v = vec!["JOINED"];
- v.extend(nicks.iter().map(|n| { &n[..] }));
- construct_message(v)
- },
+ LobbyJoined(nicks) =>
+ construct_message(&["LOBBY:JOINED"], &nicks),
+ ClientFlags(flags, nicks) =>
+ construct_message(&["CLIENT_FLAGS", flags], &nicks),
+ Rooms(info) =>
+ construct_message(&["ROOMS"], &info),
+ RoomAdd(info) =>
+ construct_message(&["ROOM", "ADD"], &info),
+ RoomJoined(nicks) =>
+ construct_message(&["JOINED"], &nicks),
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)
- }
+ RoomUpdated(name, info) =>
+ construct_message(&["ROOM", "UPD", name], &info),
+ TeamAdd(info) =>
+ construct_message(&["ADD_TEAM"], &info),
+ TeamRemove(name) => msg!["REMOVE_TEAM", name],
+ TeamAccepted(name) => msg!["TEAM_ACCEPTED", name],
+ TeamColor(name, color) => msg!["TEAM_COLOR", name, color],
+ HedgehogsNumber(name, number) => msg!["HH_NUM", name, number],
ChatMsg(nick, msg) => msg!["CHAT", nick, msg],
ServerMessage(msg) => msg!["SERVER_MESSAGE", msg],
Warning(msg) => msg!["WARNING", msg],
--- a/gameServer2/src/protocol/parser.rs Thu Jun 21 23:09:20 2018 +0200
+++ b/gameServer2/src/protocol/parser.rs Thu Jun 21 17:23:10 2018 -0400
@@ -8,6 +8,9 @@
messages::{HWProtocolMessage, HWProtocolMessage::*},
test::gen_proto_msg
};
+use server::coretypes::{
+ HedgehogInfo, TeamInfo
+};
named!(end_of_message, tag!("\n\n"));
named!(str_line<&[u8], &str>, map_res!(not_line_ending, str::from_utf8));
@@ -15,6 +18,15 @@
named!( u8_line<&[u8], u8>, map_res!(str_line, FromStr::from_str));
named!(u32_line<&[u8], u32>, map_res!(str_line, FromStr::from_str));
named!(opt_param<&[u8], Option<String> >, opt!(map!(flat_map!(preceded!(eol, str_line), non_empty), String::from)));
+named!(hog_line<&[u8], HedgehogInfo>,
+ do_parse!(name: str_line >> eol >> hat: str_line >>
+ (HedgehogInfo{name: name.to_string(), hat: hat.to_string()})));
+named!(_8_hogs<&[u8], [HedgehogInfo; 8]>,
+ do_parse!(h1: hog_line >> eol >> h2: hog_line >> eol >>
+ h3: hog_line >> eol >> h4: hog_line >> eol >>
+ h5: hog_line >> eol >> h6: hog_line >> eol >>
+ h7: hog_line >> eol >> h8: hog_line >>
+ ([h1, h2, h3, h4, h5, h6, h7, h8])));
named!(basic_message<&[u8], HWProtocolMessage>, alt!(
do_parse!(tag!("PING") >> (Ping))
@@ -88,6 +100,28 @@
n: a_line >>
p: opt_param >>
(JoinRoom(n, p)))
+ | do_parse!(tag!("ADD_TEAM") >> eol >>
+ name: a_line >> eol >>
+ color: u8_line >> eol >>
+ grave: a_line >> eol >>
+ fort: a_line >> eol >>
+ voice_pack: a_line >> eol >>
+ flag: a_line >> eol >>
+ difficulty: u8_line >> eol >>
+ hedgehogs: _8_hogs >>
+ (AddTeam(TeamInfo{
+ name, color, grave, fort,
+ voice_pack, flag, difficulty,
+ hedgehogs, hedgehogs_number: 0
+ })))
+ | do_parse!(tag!("HH_NUM") >> eol >>
+ n: a_line >> eol >>
+ c: u8_line >>
+ (SetHedgehogsNumber(n, c)))
+ | do_parse!(tag!("TEAM_COLOR") >> eol >>
+ n: a_line >> eol >>
+ c: u8_line >>
+ (SetTeamColor(n, c)))
| do_parse!(tag!("BAN") >> eol >>
n: a_line >> eol >>
r: a_line >> eol >>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/gameServer2/src/protocol/test.rs Thu Jun 21 17:23:10 2018 -0400
@@ -0,0 +1,134 @@
+use proptest::{
+ test_runner::{TestRunner, Reason},
+ arbitrary::{any, any_with, Arbitrary, StrategyFor},
+ strategy::{Strategy, BoxedStrategy, Just, Filter, ValueTree},
+ string::RegexGeneratorValueTree
+};
+
+use super::messages::{
+ HWProtocolMessage, HWProtocolMessage::*
+};
+
+// 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>>;
+}
+
+pub 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 => JoinRoom(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()
+}
\ No newline at end of file
--- a/gameServer2/src/server/actions.rs Thu Jun 21 23:09:20 2018 +0200
+++ b/gameServer2/src/server/actions.rs Thu Jun 21 17:23:10 2018 -0400
@@ -3,7 +3,8 @@
};
use super::{
server::HWServer,
- client::ClientId,
+ room::RoomId,
+ client::{ClientId, HWClient},
room::HWRoom,
handlers
};
@@ -13,11 +14,69 @@
HWServerMessage::*
};
+pub enum Destination {
+ ToSelf,
+ ToAll {
+ room_id: Option<RoomId>,
+ protocol: Option<u32>,
+ skip_self: bool
+ }
+}
+
+pub struct PendingMessage {
+ pub destination: Destination,
+ pub message: HWServerMessage
+}
+
+impl PendingMessage {
+ pub fn send_self(message: HWServerMessage) -> PendingMessage {
+ PendingMessage{ destination: Destination::ToSelf, message }
+ }
+
+ pub fn send_all(message: HWServerMessage) -> PendingMessage {
+ let destination = Destination::ToAll {
+ room_id: None,
+ protocol: None,
+ skip_self: false,
+ };
+ PendingMessage{ destination, message }
+ }
+
+ pub fn in_room(mut self, clients_room_id: RoomId) -> PendingMessage {
+ if let Destination::ToAll {ref mut room_id, ..} = self.destination {
+ *room_id = Some(clients_room_id)
+ }
+ self
+ }
+
+ pub fn with_protocol(mut self, protocol_number: u32) -> PendingMessage {
+ if let Destination::ToAll {ref mut protocol, ..} = self.destination {
+ *protocol = Some(protocol_number)
+ }
+ self
+ }
+
+ pub fn but_self(mut self) -> PendingMessage {
+ if let Destination::ToAll {ref mut skip_self, ..} = self.destination {
+ *skip_self = true
+ }
+ self
+ }
+
+ pub fn action(self) -> Action { Send(self) }
+}
+
+impl Into<Action> for PendingMessage {
+ fn into(self) -> Action { self.action() }
+}
+
+impl HWServerMessage {
+ pub fn send_self(self) -> PendingMessage { PendingMessage::send_self(self) }
+ pub fn send_all(self) -> PendingMessage { PendingMessage::send_all(self) }
+}
+
pub enum Action {
- SendAll(HWServerMessage),
- SendMe(HWServerMessage),
- SendAllButMe(HWServerMessage),
- SendToSelected(Vec<ClientId>, HWServerMessage),
+ Send(PendingMessage),
RemoveClient,
ByeClient(String),
ReactProtocolMessage(HWProtocolMessage),
@@ -28,65 +87,55 @@
MoveToRoom(RoomId),
MoveToLobby(String),
ChangeMaster(RoomId, Option<ClientId>),
+ RemoveTeam(String),
+ RemoveClientTeams,
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) {
+pub fn run_action(server: &mut HWServer, client_id: 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),
- SendToSelected(client_ids, msg) =>
- server.send_to_selected(client_ids, msg),
+ Send(msg) => server.send(client_id, msg.destination, msg.message),
ByeClient(msg) => {
let room_id;
let nick;
{
- let c = &server.clients[token];
+ let c = &server.clients[client_id];
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()))
+ room_id.map (|id| {
+ if id != server.lobby_id {
+ server.react(client_id, vec![
+ MoveToLobby(format!("quit: {}", msg.clone()))]);
}
});
- if let Some(action) = action {
- server.react(token, vec![action]);
- }
-
- server.react(token, vec![
- SendMe(Bye(msg)),
+ server.react(client_id, vec![
+ LobbyLeft(nick, msg.clone()).send_all().action(),
+ Bye(msg).send_self().action(),
RemoveClient]);
},
RemoveClient => {
- server.removed_clients.push(token);
- if server.clients.contains(token) {
- server.clients.remove(token);
+ server.removed_clients.push(client_id);
+ if server.clients.contains(client_id) {
+ server.clients.remove(client_id);
}
},
ReactProtocolMessage(msg) =>
- handlers::handle(server, token, msg),
+ handlers::handle(server, client_id, msg),
CheckRegistered =>
- if server.clients[token].protocol_number > 0 && server.clients[token].nick != "" {
- server.react(token, vec![
+ if server.clients[client_id].protocol_number > 0 && server.clients[client_id].nick != "" {
+ server.react(client_id, vec![
JoinLobby,
]);
},
JoinLobby => {
- server.clients[token].room_id = Some(server.lobby_id);
+ server.clients[client_id].room_id = Some(server.lobby_id);
let joined_msg;
{
@@ -98,7 +147,7 @@
}
joined_msg = LobbyJoined(lobby_nicks);
}
- let everyone_msg = LobbyJoined(vec![server.clients[token].nick.clone()]);
+ let everyone_msg = LobbyJoined(vec![server.clients[client_id].nick.clone()]);
let flags_msg = ClientFlags(
"+i".to_string(),
server.clients.iter()
@@ -111,53 +160,44 @@
.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),
+ server.react(client_id, vec![
+ everyone_msg.send_all().but_self().action(),
+ joined_msg.send_self().action(),
+ flags_msg.send_self().action(),
+ server_msg.send_self().action(),
+ rooms_msg.send_self().action(),
]);
},
AddRoom(name, password) => {
- let room_protocol;
- let room_info;
let room_id = server.add_room();;
- {
+ let actions = {
let r = &mut server.rooms[room_id];
- let c = &mut server.clients[token];
+ let c = &mut server.clients[client_id];
r.master_id = Some(c.id);
r.name = name;
r.password = password;
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)]);
+ vec![
+ RoomAdd(r.info(Some(&c))).send_all()
+ .with_protocol(r.protocol_number).action(),
+ MoveToRoom(room_id)]
+ };
+ server.react(client_id, actions);
},
RemoveRoom(room_id) => {
- let room_protocol;
- let room_name;
- {
+ let actions = {
let r = &mut server.rooms[room_id];
- room_protocol = r.protocol_number;
- room_name = r.name.clone();
- }
+ vec![RoomRemove(r.name.clone()).send_all()
+ .with_protocol(r.protocol_number).action()]
+ };
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))]);
+ server.react(client_id, actions);
}
MoveToRoom(room_id) => {
- let flags_msg;
- let nick;
- {
+ let actions = {
let r = &mut server.rooms[room_id];
- let c = &mut server.clients[token];
+ let c = &mut server.clients[client_id];
r.players_number += 1;
c.room_id = Some(room_id);
c.is_joined_mid_game = false;
@@ -169,20 +209,18 @@
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)]);
+ let flags_msg = ClientFlags("+i".to_string(), vec![c.nick.clone()]);
+
+ vec![RoomJoined(vec![c.nick.clone()]).send_all().in_room(room_id).action(),
+ flags_msg.send_all().action(),
+ SendRoomUpdate(None)]
+ };
+ server.react(client_id, actions);
},
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) {
+ if let (c, Some(r)) = server.client_and_room(client_id) {
r.players_number -= 1;
if c.is_ready {
r.ready_players_number -= 1;
@@ -190,63 +228,85 @@
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(RemoveClientTeams);
+ actions.push(RoomLeft(c.nick.clone(), msg)
+ .send_all().in_room(r.id).but_self().action());
+ actions.push(ClientFlags("-i".to_string(), vec![c.nick.clone()])
+ .send_all().action());
actions.push(SendRoomUpdate(Some(r.name.clone())));
}
- server.react(token, actions);
+ server.react(client_id, actions);
actions = Vec::new();
- if let (c, Some(r)) = server.client_and_room(token) {
+ if let (c, Some(r)) = server.client_and_room(client_id) {
c.room_id = Some(lobby_id);
if r.players_number == 0 {
actions.push(RemoveRoom(r.id));
}
}
- server.react(token, actions)
+ server.react(client_id, 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));
+ room_client_ids.iter().find(|id| **id != client_id).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()])));
+ if let (c, Some(r)) = server.client_and_room(client_id) {
+ match r.master_id {
+ Some(id) if id == c.id => {
+ c.is_master = false;
+ r.master_id = None;
+ actions.push(ClientFlags("-h".to_string(), vec![c.nick.clone()])
+ .send_all().in_room(r.id).action());
+ }
+ Some(_) => unreachable!(),
+ None => {}
}
r.master_id = new_id;
if let Some(nick) = new_nick {
- actions.push(SendToSelected(room_client_ids, ClientFlags("+h".to_string(), vec![nick])));
+ actions.push(ClientFlags("+h".to_string(), vec![nick])
+ .send_all().in_room(r.id).action());
}
}
new_id.map(|id| server.clients[id].is_master = true);
- server.react(token, actions);
+ server.react(client_id, actions);
+ }
+ RemoveTeam(name) => {
+ let actions = if let (c, Some(r)) = server.client_and_room(client_id) {
+ r.remove_team(&name);
+ vec![TeamRemove(name).send_all().in_room(r.id).action(),
+ SendRoomUpdate(None)]
+ } else {
+ Vec::new()
+ };
+ server.react(client_id, actions);
+ },
+ RemoveClientTeams => {
+ let actions = if let (c, Some(r)) = server.client_and_room(client_id) {
+ r.client_teams(c.id).map(|t| RemoveTeam(t.name.clone())).collect()
+ } else {
+ Vec::new()
+ };
+ server.react(client_id, 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))]);
- }
+ let actions = if let (c, Some(r)) = server.client_and_room(client_id) {
+ let name = old_name.unwrap_or_else(|| r.name.clone());
+ vec![RoomUpdated(name, r.info(Some(&c)))
+ .send_all().with_protocol(r.protocol_number).action()]
+ } else {
+ Vec::new()
+ };
+ server.react(client_id, actions);
}
Warn(msg) => {
- run_action(server, token,SendMe(Warning(msg)));
+ run_action(server, client_id, Warning(msg).send_self().action());
}
ProtocolError(msg) => {
- run_action(server, token, SendMe(Error(msg)))
+ run_action(server, client_id, Error(msg).send_self().action())
}
}
}
--- a/gameServer2/src/server/client.rs Thu Jun 21 23:09:20 2018 +0200
+++ b/gameServer2/src/server/client.rs Thu Jun 21 17:23:10 2018 -0400
@@ -7,6 +7,8 @@
pub protocol_number: u32,
pub is_master: bool,
pub is_ready: bool,
+ pub teams_in_game: u8,
+ pub clan: Option<u8>,
pub is_joined_mid_game: bool,
}
@@ -19,6 +21,8 @@
protocol_number: 0,
is_master: false,
is_ready: false,
+ teams_in_game: 0,
+ clan: None,
is_joined_mid_game: false,
}
}
--- a/gameServer2/src/server/coretypes.rs Thu Jun 21 23:09:20 2018 +0200
+++ b/gameServer2/src/server/coretypes.rs Thu Jun 21 17:23:10 2018 -0400
@@ -12,19 +12,19 @@
#[derive(PartialEq, Eq, Clone, Debug)]
pub struct TeamInfo {
- name: String,
- color: u8,
- grave: String,
- fort: String,
- voice_pack: String,
- flag: String,
- difficulty: u8,
- hedgehogs_number: u8,
- hedgehogs: [HedgehogInfo; 8],
+ pub name: String,
+ pub color: u8,
+ pub grave: String,
+ pub fort: String,
+ pub voice_pack: String,
+ pub flag: String,
+ pub difficulty: u8,
+ pub hedgehogs_number: u8,
+ pub hedgehogs: [HedgehogInfo; 8],
}
#[derive(PartialEq, Eq, Clone, Debug)]
pub struct HedgehogInfo {
- name: String,
- hat: String,
+ pub name: String,
+ pub hat: String,
}
--- a/gameServer2/src/server/handlers/inroom.rs Thu Jun 21 23:09:20 2018 +0200
+++ b/gameServer2/src/server/handlers/inroom.rs Thu Jun 21 17:23:10 2018 -0400
@@ -1,34 +1,36 @@
use mio;
-use server::{
- server::HWServer,
- actions::{Action, Action::*}
-};
use protocol::messages::{
HWProtocolMessage,
HWServerMessage::*
};
+use server::{
+ server::HWServer,
+ client::ClientId,
+ room::HWRoom,
+ actions::{Action, Action::*}
+};
use utils::is_name_illegal;
use std::mem::swap;
-pub fn handle(server: &mut HWServer, token: usize, message: HWProtocolMessage) {
+pub fn handle(server: &mut HWServer, client_id: ClientId, message: HWProtocolMessage) {
use protocol::messages::HWProtocolMessage::*;
match message {
- Part(None) => server.react(token, vec![
+ Part(None) => server.react(client_id, vec![
MoveToLobby("part".to_string())]),
- Part(Some(msg)) => server.react(token, vec![
+ Part(Some(msg)) => server.react(client_id, 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)]);
+ let actions = {
+ let c = &mut server.clients[client_id];
+ let chat_msg = ChatMsg(c.nick.clone(), msg);
+ if let Some(room_id) = c.room_id {
+ vec![chat_msg.send_all().in_room(room_id).but_self().action()]
+ } else {
+ Vec::new()
+ }
+ };
+ server.react(client_id, actions);
},
RoomName(new_name) => {
let actions =
@@ -38,15 +40,130 @@
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) {
+ if let (c, Some(r)) = server.client_and_room(client_id) {
swap(&mut r.name, &mut old_name);
vec![SendRoomUpdate(Some(old_name))]
} else {
Vec::new()
}
};
- server.react(token, actions);
+ server.react(client_id, actions);
+ },
+ ToggleReady => {
+ let actions = if let (c, Some(r)) = server.client_and_room(client_id) {
+ let flags = if c.is_ready {
+ r.ready_players_number -= 1;
+ "-r"
+ } else {
+ r.ready_players_number += 1;
+ "+r"
+ };
+ c.is_ready = !c.is_ready;
+ vec![ClientFlags(flags.to_string(), vec![c.nick.clone()])
+ .send_all().in_room(r.id).action()]
+ } else {
+ Vec::new()
+ };
+ server.react(client_id, actions);
}
- _ => warn!("Unimplemented!"),
+ AddTeam(mut info) => {
+ let mut actions = Vec::new();
+ if let (c, Some(r)) = server.client_and_room(client_id) {
+ let room_id = r.id;
+ if r.teams.len() >= r.team_limit as usize {
+ actions.push(Warn("Too many teams!".to_string()))
+ } else if r.addable_hedgehogs() == 0 {
+ actions.push(Warn("Too many hedgehogs!".to_string()))
+ } else if r.find_team(|t| t.name == info.name) != None {
+ actions.push(Warn("There's already a team with same name in the list.".to_string()))
+ } else if r.game_info != None {
+ actions.push(Warn("Joining not possible: Round is in progress.".to_string()))
+ } else {
+ let team = r.add_team(c.id, info);
+ c.teams_in_game += 1;
+ c.clan = Some(team.color);
+ actions.push(TeamAccepted(team.name.clone())
+ .send_self().action());
+ actions.push(TeamAdd(HWRoom::team_info(&c, team))
+ .send_all().in_room(room_id).but_self().action());
+ actions.push(TeamColor(team.name.clone(), team.color)
+ .send_all().in_room(room_id).action());
+ actions.push(HedgehogsNumber(team.name.clone(), team.hedgehogs_number)
+ .send_all().in_room(room_id).action());
+ actions.push(SendRoomUpdate(None));
+ }
+ }
+ server.react(client_id, actions);
+ },
+ RemoveTeam(name) => {
+ let mut actions = Vec::new();
+ if let (c, Some(r)) = server.client_and_room(client_id) {
+ match r.find_team_owner(&name) {
+ None =>
+ actions.push(Warn("Error: The team you tried to remove does not exist.".to_string())),
+ Some((id, _)) if id != client_id =>
+ actions.push(Warn("You can't remove a team you don't own.".to_string())),
+ Some((_, name)) => {
+ c.teams_in_game -= 1;
+ c.clan = r.find_team_color(c.id);
+ actions.push(Action::RemoveTeam(name.to_string()));
+ }
+ }
+ };
+ server.react(client_id, actions);
+ },
+ SetHedgehogsNumber(team_name, number) => {
+ let actions = if let (c, Some(r)) = server.client_and_room(client_id) {
+ let room_id = r.id;
+ let addable_hedgehogs = r.addable_hedgehogs();
+ if let Some((_, mut team)) = r.find_team_and_owner_mut(|t| t.name == team_name) {
+ if !c.is_master {
+ vec![ProtocolError("You're not the room master!".to_string())]
+ } else if number < 1 || number > 8
+ || number > addable_hedgehogs + team.hedgehogs_number {
+ vec![HedgehogsNumber(team.name.clone(), team.hedgehogs_number)
+ .send_self().action()]
+ } else {
+ team.hedgehogs_number = number;
+ vec![HedgehogsNumber(team.name.clone(), number)
+ .send_all().in_room(room_id).but_self().action()]
+ }
+ } else {
+ vec![(Warn("No such team.".to_string()))]
+ }
+ } else {
+ Vec::new()
+ };
+ server.react(client_id, actions);
+ },
+ SetTeamColor(team_name, color) => {
+ let mut owner_id = None;
+ let actions = if let (c, Some(r)) = server.client_and_room(client_id) {
+ let room_id = r.id;
+ if let Some((owner, mut team)) = r.find_team_and_owner_mut(|t| t.name == team_name) {
+ if !c.is_master {
+ vec![ProtocolError("You're not the room master!".to_string())]
+ } else if false {
+ Vec::new()
+ } else {
+ owner_id = Some(owner);
+ team.color = color;
+ vec![TeamColor(team.name.clone(), color)
+ .send_all().in_room(room_id).but_self().action()]
+ }
+ } else {
+ vec![(Warn("No such team.".to_string()))]
+ }
+ } else {
+ Vec::new()
+ };
+
+ if let Some(id) = owner_id {
+ server.clients[id].clan = Some(color);
+ }
+
+ server.react(client_id, actions);
+ }
+ _ => warn!("Unimplemented!")
}
}
--- a/gameServer2/src/server/handlers/lobby.rs Thu Jun 21 23:09:20 2018 +0200
+++ b/gameServer2/src/server/handlers/lobby.rs Thu Jun 21 17:23:10 2018 -0400
@@ -2,6 +2,7 @@
use server::{
server::HWServer,
+ client::ClientId,
actions::{Action, Action::*}
};
use protocol::messages::{
@@ -10,7 +11,7 @@
};
use utils::is_name_illegal;
-pub fn handle(server: &mut HWServer, token: usize, message: HWProtocolMessage) {
+pub fn handle(server: &mut HWServer, client_id: ClientId, message: HWProtocolMessage) {
use protocol::messages::HWProtocolMessage::*;
match message {
CreateRoom(name, password) => {
@@ -22,15 +23,15 @@
} else {
let flags_msg = ClientFlags(
"+hr".to_string(),
- vec![server.clients[token].nick.clone()]);
+ vec![server.clients[client_id].nick.clone()]);
vec![AddRoom(name, password),
- SendMe(flags_msg)]
+ flags_msg.send_self().action()]
};
- server.react(token, actions);
+ server.react(client_id, actions);
},
Chat(msg) => {
- let chat_msg = ChatMsg(server.clients[token].nick.clone(), msg);
- server.react(token, vec![SendAllButMe(chat_msg)]);
+ let chat_msg = ChatMsg(server.clients[client_id].nick.clone(), msg);
+ server.react(client_id, vec![chat_msg.send_all().but_self().action()]);
},
JoinRoom(name, password) => {
let actions;
@@ -41,7 +42,7 @@
.filter(|(_, c)| c.room_id == room_id)
.map(|(_, c)| c.nick.clone())
.collect();
- let c = &mut server.clients[token];
+ let c = &mut server.clients[client_id];
actions = match room {
None => vec![Warn("No such room.".to_string())],
Some((_, r)) => {
@@ -49,12 +50,12 @@
vec![Warn("Room version incompatible to your Hedgewars version!".to_string())]
} else {
vec![MoveToRoom(r.id),
- SendMe(RoomJoined(nicks))]
+ RoomJoined(nicks).send_self().action()]
}
}
};
}
- server.react(token, actions);
+ server.react(client_id, actions);
},
List => warn!("Deprecated LIST message received"),
_ => warn!("Incorrect command in lobby state"),
--- a/gameServer2/src/server/handlers/loggingin.rs Thu Jun 21 23:09:20 2018 +0200
+++ b/gameServer2/src/server/handlers/loggingin.rs Thu Jun 21 17:23:10 2018 -0400
@@ -1,18 +1,21 @@
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,
+ client::ClientId,
+ actions::{Action, Action::*}
+};
+use protocol::messages::{
+ HWProtocolMessage, HWServerMessage::*
+};
use utils::is_name_illegal;
-pub fn handle(server: & mut HWServer, token: usize, message: HWProtocolMessage) {
+pub fn handle(server: & mut HWServer, client_id: ClientId, message: HWProtocolMessage) {
match message {
HWProtocolMessage::Nick(nick) => {
let actions;
{
- let client = &mut server.clients[token];
+ let client = &mut server.clients[client_id];
debug!("{} {}", nick, is_name_illegal(&nick));
actions = if client.room_id != None {
unreachable!()
@@ -25,15 +28,16 @@
}
else {
client.nick = nick.clone();
- vec![SendMe(Nick(nick)), CheckRegistered]
+ vec![Nick(nick).send_self().action(),
+ CheckRegistered]
};
}
- server.react(token, actions);
+ server.react(client_id, actions);
},
HWProtocolMessage::Proto(proto) => {
let actions;
{
- let client = &mut server.clients[token];
+ let client = &mut server.clients[client_id];
actions = if client.protocol_number != 0 {
vec![ProtocolError("Protocol already known.".to_string())]
}
@@ -42,10 +46,11 @@
}
else {
client.protocol_number = proto;
- vec![SendMe(Proto(proto)), CheckRegistered]
+ vec![Proto(proto).send_self().action(),
+ CheckRegistered]
};
}
- server.react(token, actions);
+ server.react(client_id, actions);
},
_ => warn!("Incorrect command in logging-in state"),
}
--- a/gameServer2/src/server/handlers/mod.rs Thu Jun 21 23:09:20 2018 +0200
+++ b/gameServer2/src/server/handlers/mod.rs Thu Jun 21 17:23:10 2018 -0400
@@ -15,7 +15,7 @@
pub fn handle(server: &mut HWServer, token: usize, message: HWProtocolMessage) {
match message {
HWProtocolMessage::Ping =>
- server.react(token, vec![SendMe(Pong)]),
+ server.react(token, vec![Pong.send_self().action()]),
HWProtocolMessage::Quit(Some(msg)) =>
server.react(token, vec![ByeClient("User quit: ".to_string() + &msg)]),
HWProtocolMessage::Quit(None) =>
--- a/gameServer2/src/server/network.rs Thu Jun 21 23:09:20 2018 +0200
+++ b/gameServer2/src/server/network.rs Thu Jun 21 17:23:10 2018 -0400
@@ -17,7 +17,7 @@
use utils;
use protocol::{ProtocolDecoder, messages::*};
use super::{
- server::{HWServer, PendingMessage, Destination},
+ server::{HWServer},
client::ClientId
};
@@ -166,39 +166,13 @@
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(client) = self.clients.get_mut(id) {
- client.send_msg(msg);
- self.pending.insert((id, NetworkClientState::NeedsWrite));
- }
- }
- Destination::ToOthers(id) => {
- let msg_string = msg.to_raw_protocol();
- for (client_id, client) in self.clients.iter_mut() {
- if client_id != id {
- client.send_string(&msg_string);
- 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));
- }
- }
+ for (clients, message) in self.server.output.drain(..) {
+ debug!("Message {:?} to {:?}", message, clients);
+ let msg_string = message.to_raw_protocol();
+ for client_id in clients {
+ if let Some(client) = self.clients.get_mut(client_id) {
+ client.send_string(&msg_string);
+ self.pending.insert((client_id, NetworkClientState::NeedsWrite));
}
}
}
--- a/gameServer2/src/server/room.rs Thu Jun 21 23:09:20 2018 +0200
+++ b/gameServer2/src/server/room.rs Thu Jun 21 17:23:10 2018 -0400
@@ -1,8 +1,10 @@
+use std::iter;
use server::{
- coretypes::TeamInfo,
+ coretypes::{TeamInfo, GameCfg},
client::{ClientId, HWClient}
};
+const MAX_HEDGEHOGS_IN_ROOM: u8 = 48;
pub type RoomId = usize;
pub struct HWRoom {
@@ -13,8 +15,11 @@
pub protocol_number: u32,
pub players_number: u32,
+ pub default_hedgehog_number: u8,
+ pub team_limit: u8,
pub ready_players_number: u8,
- pub teams: Vec<TeamInfo>,
+ pub teams: Vec<(ClientId, TeamInfo)>,
+ pub game_info: Option<()>
}
impl HWRoom {
@@ -26,11 +31,65 @@
password: None,
protocol_number: 0,
players_number: 0,
+ default_hedgehog_number: 4,
+ team_limit: 8,
ready_players_number: 0,
- teams: Vec::new()
+ teams: Vec::new(),
+ game_info: None
}
}
+ pub fn hedgehogs_number(&self) -> u8 {
+ self.teams.iter().map(|(_, t)| t.hedgehogs_number).sum()
+ }
+
+ pub fn addable_hedgehogs(&self) -> u8 {
+ MAX_HEDGEHOGS_IN_ROOM - self.hedgehogs_number()
+ }
+
+ pub fn add_team(&mut self, owner_id: ClientId, mut team: TeamInfo) -> &TeamInfo {
+ team.color = iter::repeat(()).enumerate()
+ .map(|(i, _)| i as u8).take(u8::max_value() as usize + 1)
+ .find(|i| self.teams.iter().all(|(_, t)| t.color != *i ))
+ .unwrap_or(0u8);
+ team.hedgehogs_number = if self.teams.is_empty() {
+ self.default_hedgehog_number
+ } else {
+ self.teams[0].1.hedgehogs_number.min(self.addable_hedgehogs())
+ };
+ self.teams.push((owner_id, team));
+ &self.teams.last().unwrap().1
+ }
+
+ pub fn remove_team(&mut self, name: &str) {
+ if let Some(index) = self.teams.iter().position(|(_, t)| t.name == name) {
+ self.teams.remove(index);
+ }
+ }
+
+ pub fn find_team_and_owner_mut<F>(&mut self, f: F) -> Option<(ClientId, &mut TeamInfo)>
+ where F: Fn(&TeamInfo) -> bool {
+ self.teams.iter_mut().find(|(_, t)| f(t)).map(|(id, t)| (*id, t))
+ }
+
+ pub fn find_team<F>(&self, f: F) -> Option<&TeamInfo>
+ where F: Fn(&TeamInfo) -> bool {
+ self.teams.iter().map(|(_, t)| t).find(|t| f(*t))
+ }
+
+ pub fn client_teams(&self, client_id: ClientId) -> impl Iterator<Item = &TeamInfo> {
+ self.teams.iter().filter(move |(id, _)| *id == client_id).map(|(_, t)| t)
+ }
+
+ pub fn find_team_owner(&self, team_name: &str) -> Option<(ClientId, &str)> {
+ self.teams.iter().find(|(_, t)| t.name == team_name)
+ .map(|(id, t)| (*id, &t.name[..]))
+ }
+
+ pub fn find_team_color(&self, owner_id: ClientId) -> Option<u8> {
+ self.client_teams(owner_id).nth(0).map(|t| t.color)
+ }
+
pub fn info(&self, master: Option<&HWClient>) -> Vec<String> {
let flags = "-".to_string();
vec![
@@ -39,10 +98,25 @@
self.players_number.to_string(),
self.teams.len().to_string(),
master.map_or("?", |c| &c.nick).to_string(),
- "Default".to_string(),
+ "Normal".to_string(),
"Default".to_string(),
"Default".to_string(),
"Default".to_string(),
]
}
+
+ pub fn team_info(owner: &HWClient, team: &TeamInfo) -> Vec<String> {
+ let mut info = vec![
+ team.name.clone(),
+ team.grave.clone(),
+ team.fort.clone(),
+ team.voice_pack.clone(),
+ team.flag.clone(),
+ owner.nick.clone(),
+ team.difficulty.to_string()];
+ let hogs = team.hedgehogs.iter().flat_map(|h|
+ iter::once(h.name.clone()).chain(iter::once(h.hat.clone())));
+ info.extend(hogs);
+ info
+ }
}
\ No newline at end of file
--- a/gameServer2/src/server/server.rs Thu Jun 21 23:09:20 2018 +0200
+++ b/gameServer2/src/server/server.rs Thu Jun 21 17:23:10 2018 -0400
@@ -1,27 +1,19 @@
use slab;
use utils;
use super::{
- client::*, room::*, actions, handlers
+ client::*, room::*, actions, handlers,
+ actions::{Destination, PendingMessage}
};
use protocol::messages::*;
type Slab<T> = slab::Slab<T>;
-#[derive(Debug)]
-pub enum Destination {
- ToAll,
- ToSelf(ClientId),
- ToOthers(ClientId),
- ToSelected(Vec<ClientId>)
-}
-
-pub struct PendingMessage(pub Destination, pub HWServerMessage);
pub struct HWServer {
pub clients: Slab<HWClient>,
pub rooms: Slab<HWRoom>,
pub lobby_id: RoomId,
- pub output: Vec<PendingMessage>,
+ pub output: Vec<(Vec<ClientId>, HWServerMessage)>,
pub removed_clients: Vec<ClientId>,
}
@@ -47,7 +39,7 @@
let client = HWClient::new(entry.key());
entry.insert(client);
}
- self.send_self(key, HWServerMessage::Connected(utils::PROTOCOL_VERSION));
+ self.send(key, Destination::ToSelf, HWServerMessage::Connected(utils::PROTOCOL_VERSION));
key
}
@@ -69,24 +61,28 @@
handlers::handle(self, client_id, msg);
}
- pub fn send_all(&mut self, msg: HWServerMessage) {
- self.output.push(PendingMessage(
- Destination::ToAll, msg));
+ fn get_recipients(&self, client_id: ClientId, destination: Destination) -> Vec<ClientId> {
+ let mut ids = match destination {
+ Destination::ToSelf => vec![client_id],
+ Destination::ToAll {room_id: Some(id), ..} =>
+ self.room_clients(id),
+ Destination::ToAll {protocol: Some(proto), ..} =>
+ self.protocol_clients(proto),
+ Destination::ToAll {..} =>
+ self.clients.iter().map(|(id, _)| id).collect::<Vec<_>>(),
+ _ => Vec::new()
+ };
+ if let Destination::ToAll {skip_self: true, ..} = destination {
+ if let Some(index) = ids.iter().position(|id| *id == client_id) {
+ ids.remove(index);
+ }
+ }
+ ids
}
- pub fn send_self(&mut self, client_id: ClientId, msg: HWServerMessage) {
- self.output.push(PendingMessage(
- Destination::ToSelf(client_id), msg));
- }
-
- pub fn send_others(&mut self, client_id: ClientId, msg: HWServerMessage) {
- self.output.push(PendingMessage(
- 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 send(&mut self, client_id: ClientId, destination: Destination, message: HWServerMessage) {
+ let ids = self.get_recipients(client_id, destination);
+ self.output.push((ids, message));
}
pub fn react(&mut self, client_id: ClientId, actions: Vec<actions::Action>) {