# HG changeset patch # User alfadur # Date 1529616190 14400 # Node ID 81e0ed105f5d4841adce907272ec469925f7181b # Parent bb24c3414b0d553cc368cd3f7823969b902747fb implementation of team related messages diff -r bb24c3414b0d -r 81e0ed105f5d gameServer2/src/protocol/messages.rs --- 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), + TeamAdd(Vec), + 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 { + 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], diff -r bb24c3414b0d -r 81e0ed105f5d gameServer2/src/protocol/parser.rs --- 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 >, 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 >> diff -r bb24c3414b0d -r 81e0ed105f5d gameServer2/src/protocol/test.rs --- /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: Sized { fn into2(self) -> T; } +impl Into2 for T { fn into2(self) -> T { self } } +impl Into2 for Ascii { fn into2(self) -> String { self.0 } } +impl Into2> for Option{ + fn into2(self) -> Option { 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); + +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 = ::Parameters; + + fn arbitrary_with(args: Self::Parameters) -> Self::Strategy { + any_with::(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; + type ValueTree = Box>; +} + +pub fn gen_proto_msg() -> BoxedStrategy where { + let res = (0..58).no_shrink().prop_flat_map(|i| { + proto_msg_match!(i, def = Malformed, + 0 => Ping(), + 1 => Pong(), + 2 => Quit(Option), + //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), + 16 => JoinRoom(Ascii, Option), + 17 => Follow(Ascii), + //18 => Rnd(Vec), + 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), + //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)>), + 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 diff -r bb24c3414b0d -r 81e0ed105f5d gameServer2/src/server/actions.rs --- 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, + protocol: Option, + 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 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, HWServerMessage), + Send(PendingMessage), RemoveClient, ByeClient(String), ReactProtocolMessage(HWProtocolMessage), @@ -28,65 +87,55 @@ MoveToRoom(RoomId), MoveToLobby(String), ChangeMaster(RoomId, Option), + RemoveTeam(String), + RemoveClientTeams, SendRoomUpdate(Option), 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()) } } } diff -r bb24c3414b0d -r 81e0ed105f5d gameServer2/src/server/client.rs --- 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, 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, } } diff -r bb24c3414b0d -r 81e0ed105f5d gameServer2/src/server/coretypes.rs --- 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, } diff -r bb24c3414b0d -r 81e0ed105f5d gameServer2/src/server/handlers/inroom.rs --- 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!") } } diff -r bb24c3414b0d -r 81e0ed105f5d gameServer2/src/server/handlers/lobby.rs --- 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"), diff -r bb24c3414b0d -r 81e0ed105f5d gameServer2/src/server/handlers/loggingin.rs --- 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"), } diff -r bb24c3414b0d -r 81e0ed105f5d gameServer2/src/server/handlers/mod.rs --- 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) => diff -r bb24c3414b0d -r 81e0ed105f5d gameServer2/src/server/network.rs --- 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)); } } } diff -r bb24c3414b0d -r 81e0ed105f5d gameServer2/src/server/room.rs --- 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, + 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(&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(&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 { + 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 { + self.client_teams(owner_id).nth(0).map(|t| t.color) + } + pub fn info(&self, master: Option<&HWClient>) -> Vec { 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 { + 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 diff -r bb24c3414b0d -r 81e0ed105f5d gameServer2/src/server/server.rs --- 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 = slab::Slab; -#[derive(Debug)] -pub enum Destination { - ToAll, - ToSelf(ClientId), - ToOthers(ClientId), - ToSelected(Vec) -} - -pub struct PendingMessage(pub Destination, pub HWServerMessage); pub struct HWServer { pub clients: Slab, pub rooms: Slab, pub lobby_id: RoomId, - pub output: Vec, + pub output: Vec<(Vec, HWServerMessage)>, pub removed_clients: Vec, } @@ -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 { + 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::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, 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) {