# HG changeset patch # User alfadur # Date 1530984151 -10800 # Node ID d79795acaa73466688105cb1e5ff0a1a5d534b18 # Parent f748a72432f2f2e9dbc3555ee0a8460df80f14a6 Mostly implement voting diff -r f748a72432f2 -r d79795acaa73 gameServer2/src/protocol/messages.rs --- a/gameServer2/src/protocol/messages.rs Fri Jul 06 21:03:03 2018 +0300 +++ b/gameServer2/src/protocol/messages.rs Sat Jul 07 20:22:31 2018 +0300 @@ -1,4 +1,7 @@ -use server::coretypes::{ServerVar, GameCfg, TeamInfo, HedgehogInfo}; +use server::coretypes::{ + ServerVar, GameCfg, TeamInfo, + HedgehogInfo, VoteType +}; use std::{ops, convert::From, iter::once}; #[derive(PartialEq, Eq, Clone, Debug)] @@ -56,9 +59,9 @@ Fix, Unfix, Greeting(String), - CallVote(Option<(String, Option)>), - Vote(String), - ForceVote(String), + CallVote(Option), + Vote(bool), + ForceVote(bool), Save(String, String), Delete(String), SaveRoom(String), @@ -90,6 +93,7 @@ TeamColor(String, u8), HedgehogsNumber(String, u8), ConfigEntry(String, Vec), + Kicked, RunGame, ForwardEngineMessage(Vec), RoundFinished, @@ -101,6 +105,10 @@ Unreachable, } +pub fn server_chat(msg: &str) -> HWServerMessage { + HWServerMessage::ChatMsg{ nick: "[server]".to_string(), msg: msg.to_string() } +} + impl GameCfg { pub fn to_protocol(&self) -> (String, Vec) { use server::coretypes::GameCfg::*; @@ -280,6 +288,7 @@ HedgehogsNumber(name, number) => msg!["HH_NUM", name, number], ConfigEntry(name, values) => construct_message(&["CFG", name], &values), + Kicked => msg!["KICKED"], RunGame => msg!["RUN_GAME"], ForwardEngineMessage(em) => construct_message(&["EM"], &em), diff -r f748a72432f2 -r d79795acaa73 gameServer2/src/protocol/parser.rs --- a/gameServer2/src/protocol/parser.rs Fri Jul 06 21:03:03 2018 +0300 +++ b/gameServer2/src/protocol/parser.rs Sat Jul 07 20:22:31 2018 +0300 @@ -18,7 +18,7 @@ test::gen_proto_msg }; use server::coretypes::{ - HedgehogInfo, TeamInfo, GameCfg + HedgehogInfo, TeamInfo, GameCfg, VoteType }; named!(end_of_message, tag!("\n\n")); @@ -26,6 +26,9 @@ named!( a_line<&[u8], String>, map!(str_line, String::from)); 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!(yes_no_line<&[u8], bool>, alt!( + do_parse!(tag_no_case!("YES") >> (true)) + | do_parse!(tag_no_case!("NO") >> (false)))); named!(opt_param<&[u8], Option >, alt!( do_parse!(peek!(tag!("\n\n")) >> (None)) | do_parse!(tag!("\n") >> s: str_line >> (Some(s.to_string()))))); @@ -42,6 +45,18 @@ h5: hog_line >> eol >> h6: hog_line >> eol >> h7: hog_line >> eol >> h8: hog_line >> ([h1, h2, h3, h4, h5, h6, h7, h8]))); +named!(voting<&[u8], VoteType>, alt!( + do_parse!(tag_no_case!("KICK") >> spaces >> n: a_line >> + (VoteType::Kick(n))) + | do_parse!(tag_no_case!("MAP") >> + n: opt!(preceded!(spaces, a_line)) >> + (VoteType::Map(n))) + | do_parse!(tag_no_case!("PAUSE") >> + (VoteType::Pause)) + | do_parse!(tag_no_case!("NEWSEED") >> + (VoteType::NewSeed)) + | do_parse!(tag_no_case!("HEDGEHOGS") >> spaces >> n: u8_line >> + (VoteType::HedgehogsPerTeam(n))))); /** Recognizes messages which do not take any parameters */ named!(basic_message<&[u8], HWProtocolMessage>, alt!( @@ -94,10 +109,12 @@ | do_parse!(tag_no_case!("GLOBAL") >> spaces >> m: a_line >> (Global(m))) | do_parse!(tag_no_case!("WATCH") >> spaces >> i: a_line >> (Watch(i))) | do_parse!(tag_no_case!("GREETING") >> spaces >> m: a_line >> (Greeting(m))) - | do_parse!(tag_no_case!("VOTE") >> spaces >> m: a_line >> (Vote(m))) - | do_parse!(tag_no_case!("FORCE") >> spaces >> m: a_line >> (ForceVote(m))) + | do_parse!(tag_no_case!("VOTE") >> spaces >> m: yes_no_line >> (Vote(m))) + | do_parse!(tag_no_case!("FORCE") >> spaces >> m: yes_no_line >> (ForceVote(m))) | do_parse!(tag_no_case!("INFO") >> spaces >> n: a_line >> (Info(n))) | do_parse!(tag_no_case!("MAXTEAMS") >> spaces >> n: u8_line >> (MaxTeams(n))) + | do_parse!(tag_no_case!("CALLVOTE") >> + v: opt!(preceded!(spaces, voting)) >> (CallVote(v))) | do_parse!( tag_no_case!("RND") >> alt!(spaces | peek!(end_of_message)) >> v: str_line >> diff -r f748a72432f2 -r d79795acaa73 gameServer2/src/protocol/test.rs --- a/gameServer2/src/protocol/test.rs Fri Jul 06 21:03:03 2018 +0300 +++ b/gameServer2/src/protocol/test.rs Sat Jul 07 20:22:31 2018 +0300 @@ -166,8 +166,8 @@ 47 => Unfix(), 48 => Greeting(Ascii), //49 => CallVote(Option<(String, Option)>), - 50 => Vote(Ascii), - 51 => ForceVote(Ascii), + 50 => Vote(bool), + 51 => ForceVote(bool), //52 => Save(String, String), 53 => Delete(Ascii), 54 => SaveRoom(Ascii), diff -r f748a72432f2 -r d79795acaa73 gameServer2/src/server/actions.rs --- a/gameServer2/src/server/actions.rs Fri Jul 06 21:03:03 2018 +0300 +++ b/gameServer2/src/server/actions.rs Sat Jul 07 20:22:31 2018 +0300 @@ -1,19 +1,21 @@ use std::{ io, io::Write, iter::once, - mem::swap + mem::replace }; use super::{ server::HWServer, - room::{RoomId, GameInfo}, - client::{ClientId, HWClient}, + room::{GameInfo}, + client::HWClient, + coretypes::{ClientId, RoomId, VoteType}, room::HWRoom, handlers }; use protocol::messages::{ HWProtocolMessage, HWServerMessage, - HWServerMessage::* + HWServerMessage::*, + server_chat }; use utils::to_engine_msg; @@ -103,6 +105,8 @@ SendTeamRemovalMessage(String), FinishRoomGame(RoomId), SendRoomData{to: ClientId, teams: bool, config: bool, flags: bool}, + AddVote{vote: bool, is_forced: bool}, + ApplyVoting(VoteType, RoomId), Warn(String), ProtocolError(String) } @@ -324,6 +328,80 @@ } server.react(client_id, actions); } + AddVote{vote, is_forced} => { + let mut actions = Vec::new(); + if let (c, Some(r)) = server.client_and_room(client_id) { + let mut result = None; + if let Some(ref mut voting) = r.voting { + if is_forced || voting.votes.iter().find(|(id, _)| client_id == *id).is_none() { + actions.push(server_chat("Your vote has been counted.").send_self().action()); + voting.votes.push((client_id, vote)); + let i = voting.votes.iter(); + let pro = i.clone().filter(|(_, v)| *v).count(); + let contra = i.filter(|(_, v)| !*v).count(); + let success_quota = voting.voters.len() / 2 + 1; + if is_forced && vote || pro >= success_quota { + result = Some(true); + } else if is_forced && !vote || contra > voting.voters.len() - success_quota { + result = Some(false); + } + } else { + actions.push(server_chat("You already have voted.").send_self().action()); + } + } else { + actions.push(server_chat("There's no voting going on.").send_self().action()); + } + + if let Some(res) = result { + actions.push(server_chat("Voting closed.") + .send_all().in_room(r.id).action()); + let voting = replace(&mut r.voting, None).unwrap(); + if res { + actions.push(ApplyVoting(voting.kind, r.id)); + } + } + } + + server.react(client_id, actions); + } + ApplyVoting(kind, room_id) => { + let mut actions = Vec::new(); + let mut id = client_id; + match kind { + VoteType::Kick(nick) => { + if let Some(c) = server.find_client(&nick) { + if c.room_id == Some(room_id) { + id = c.id; + actions.push(Kicked.send_self().action()); + actions.push(MoveToLobby("kicked".to_string())); + } + } + }, + VoteType::Map(_) => { + unimplemented!(); + }, + VoteType::Pause => { + if let Some(ref mut info) = server.room(client_id).unwrap().game_info { + info.is_paused = !info.is_paused; + actions.push(server_chat("Pause toggled.") + .send_all().in_room(room_id).action()); + actions.push(ForwardEngineMessage(vec![to_engine_msg(once(b'I'))]) + .send_all().in_room(room_id).action()); + } + }, + VoteType::NewSeed => { + unimplemented!(); + }, + VoteType::HedgehogsPerTeam(number) => { + let r = &mut server.rooms[room_id]; + let nicks = r.set_hedgehogs_number(number); + actions.extend(nicks.into_iter().map(|n| + HedgehogsNumber(n, number).send_all().in_room(room_id).action() + )); + }, + } + server.react(id, actions); + } MoveToLobby(msg) => { let mut actions = Vec::new(); let lobby_id = server.lobby_id; @@ -470,10 +548,10 @@ } FinishRoomGame(room_id) => { let mut actions = Vec::new(); - let mut old_info = None; + let old_info; { let r = &mut server.rooms[room_id]; - swap(&mut old_info, &mut r.game_info); + old_info = replace(&mut r.game_info, None); r.game_info = None; r.ready_players_number = 1; actions.push(SendRoomUpdate(None)); diff -r f748a72432f2 -r d79795acaa73 gameServer2/src/server/client.rs --- a/gameServer2/src/server/client.rs Fri Jul 06 21:03:03 2018 +0300 +++ b/gameServer2/src/server/client.rs Sat Jul 07 20:22:31 2018 +0300 @@ -1,4 +1,4 @@ -pub type ClientId = usize; +use super::coretypes::ClientId; pub struct HWClient { pub id: ClientId, diff -r f748a72432f2 -r d79795acaa73 gameServer2/src/server/coretypes.rs --- a/gameServer2/src/server/coretypes.rs Fri Jul 06 21:03:03 2018 +0300 +++ b/gameServer2/src/server/coretypes.rs Sat Jul 07 20:22:31 2018 +0300 @@ -1,3 +1,6 @@ +pub type ClientId = usize; +pub type RoomId = usize; + #[derive(PartialEq, Eq, Clone, Debug)] pub enum ServerVar { MOTDNew(String), @@ -39,3 +42,29 @@ pub name: String, pub hat: String, } + +#[derive(PartialEq, Eq, Clone, Debug)] +pub enum VoteType { + Kick(String), + Map(Option), + Pause, + NewSeed, + HedgehogsPerTeam(u8) +} + +#[derive(Clone, Debug)] +pub struct Voting { + pub ttl: u32, + pub voters: Vec, + pub votes: Vec<(ClientId, bool)>, + pub kind: VoteType +} + +impl Voting { + pub fn new(kind: VoteType, voters: Vec) -> Voting { + Voting { + kind, voters, ttl: 2, + votes: Vec::new() + } + } +} \ No newline at end of file diff -r f748a72432f2 -r d79795acaa73 gameServer2/src/server/handlers/inroom.rs --- a/gameServer2/src/server/handlers/inroom.rs Fri Jul 06 21:03:03 2018 +0300 +++ b/gameServer2/src/server/handlers/inroom.rs Sat Jul 07 20:22:31 2018 +0300 @@ -2,11 +2,12 @@ use protocol::messages::{ HWProtocolMessage, - HWServerMessage::* + HWServerMessage::*, + server_chat }; use server::{ + coretypes::{ClientId, Voting, VoteType}, server::HWServer, - client::ClientId, room::HWRoom, actions::{Action, Action::*} }; @@ -262,6 +263,76 @@ }; server.react(client_id, actions); } + CallVote(None) => { + server.react(client_id, vec![ + server_chat("Available callvote commands: kick , map , pause, newseed, hedgehogs ") + .send_self().action()]) + } + CallVote(Some(kind)) => { + let (room_id, is_in_game) = server.room(client_id) + .map(|r| (r.id, r.game_info.is_some())).unwrap(); + let error = match &kind { + VoteType::Kick(nick) => { + if server.find_client(&nick).filter(|c| c.room_id == Some(room_id)).is_some() { + None + } else { + Some("/callvote kick: No such user!") + } + }, + VoteType::Map(None) => { + Some("/callvote map: Not implemented") + }, + VoteType::Map(Some(name)) => { + Some("/callvote map: Not implemented") + }, + VoteType::Pause => { + if is_in_game { + None + } else { + Some("/callvote pause: No game in progress!") + } + }, + VoteType::NewSeed => { + None + }, + VoteType::HedgehogsPerTeam(number) => { + match number { + 1...8 => None, + _ => Some("/callvote hedgehogs: Specify number from 1 to 8.") + } + }, + }; + match error { + None => { + let voting = Voting::new(kind, server.room_clients(client_id)); + server.room(client_id).unwrap().voting = Some(voting); + server.react(client_id, vec![ + server_chat("New voting started: ") + .send_all().in_room(room_id).action(), + AddVote{ vote: true, is_forced: false}]); + } + Some(msg) => { + server.react(client_id, vec![ + server_chat(msg).send_self().action()]) + } + } + } + Vote(vote) => { + let actions = if let (c, Some(r)) = server.client_and_room(client_id) { + vec![AddVote{ vote, is_forced: false }] + } else { + Vec::new() + }; + server.react(client_id, actions); + } + ForceVote(vote) => { + let actions = if let (c, Some(r)) = server.client_and_room(client_id) { + vec![AddVote{ vote, is_forced: c.is_admin} ] + } else { + Vec::new() + }; + server.react(client_id, actions); + } StartGame => { let actions = if let (_, Some(r)) = server.client_and_room(client_id) { vec![StartRoomGame(r.id)] diff -r f748a72432f2 -r d79795acaa73 gameServer2/src/server/handlers/lobby.rs --- a/gameServer2/src/server/handlers/lobby.rs Fri Jul 06 21:03:03 2018 +0300 +++ b/gameServer2/src/server/handlers/lobby.rs Sat Jul 07 20:22:31 2018 +0300 @@ -2,7 +2,7 @@ use server::{ server::HWServer, - client::ClientId, + coretypes::ClientId, actions::{Action, Action::*} }; use protocol::messages::{ diff -r f748a72432f2 -r d79795acaa73 gameServer2/src/server/handlers/loggingin.rs --- a/gameServer2/src/server/handlers/loggingin.rs Fri Jul 06 21:03:03 2018 +0300 +++ b/gameServer2/src/server/handlers/loggingin.rs Sat Jul 07 20:22:31 2018 +0300 @@ -2,7 +2,7 @@ use server::{ server::HWServer, - client::ClientId, + coretypes::ClientId, actions::{Action, Action::*} }; use protocol::messages::{ diff -r f748a72432f2 -r d79795acaa73 gameServer2/src/server/network.rs --- a/gameServer2/src/server/network.rs Fri Jul 06 21:03:03 2018 +0300 +++ b/gameServer2/src/server/network.rs Sat Jul 07 20:22:31 2018 +0300 @@ -4,7 +4,7 @@ io, io::{Error, ErrorKind, Write}, net::{SocketAddr, IpAddr, Ipv4Addr}, collections::HashSet, - mem::swap + mem::{swap, replace} }; use mio::{ @@ -18,7 +18,7 @@ use protocol::{ProtocolDecoder, messages::*}; use super::{ server::{HWServer}, - client::ClientId + coretypes::ClientId }; const MAX_BYTES_PER_READ: usize = 2048; @@ -277,8 +277,7 @@ pub fn on_idle(&mut self, poll: &Poll) -> io::Result<()> { if self.has_pending_operations() { - let mut cache = Vec::new(); - swap(&mut cache, &mut self.pending_cache); + let mut cache = replace(&mut self.pending_cache, Vec::new()); cache.extend(self.pending.drain()); for (id, state) in cache.drain(..) { match state { diff -r f748a72432f2 -r d79795acaa73 gameServer2/src/server/room.rs --- a/gameServer2/src/server/room.rs Fri Jul 06 21:03:03 2018 +0300 +++ b/gameServer2/src/server/room.rs Sat Jul 07 20:22:31 2018 +0300 @@ -1,11 +1,10 @@ use std::{iter}; use server::{ - coretypes::{TeamInfo, GameCfg, GameCfg::*}, - client::{ClientId, HWClient} + coretypes::{ClientId, RoomId, TeamInfo, GameCfg, GameCfg::*, Voting}, + client::{HWClient} }; const MAX_HEDGEHOGS_IN_ROOM: u8 = 48; -pub type RoomId = usize; #[derive(Clone)] struct Ammo { @@ -122,6 +121,7 @@ pub ready_players_number: u8, pub teams: Vec<(ClientId, TeamInfo)>, config: RoomConfig, + pub voting: Option, pub game_info: Option } @@ -141,6 +141,7 @@ ready_players_number: 0, teams: Vec::new(), config: RoomConfig::new(), + voting: None, game_info: None } } @@ -173,6 +174,23 @@ } } + pub fn set_hedgehogs_number(&mut self, n: u8) -> Vec { + let mut names = Vec::new(); + let teams = match self.game_info { + Some(ref mut info) => &mut info.teams_at_start, + None => &mut self.teams + }; + + if teams.len() as u8 * n <= MAX_HEDGEHOGS_IN_ROOM { + for (_, team) in teams.iter_mut() { + team.hedgehogs_number = n; + names.push(team.name.clone()) + }; + self.default_hedgehog_number = n; + } + names + } + 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)) diff -r f748a72432f2 -r d79795acaa73 gameServer2/src/server/server.rs --- a/gameServer2/src/server/server.rs Fri Jul 06 21:03:03 2018 +0300 +++ b/gameServer2/src/server/server.rs Sat Jul 07 20:22:31 2018 +0300 @@ -1,7 +1,8 @@ use slab; use utils; use super::{ - client::*, room::*, actions, handlers, + client::HWClient, room::HWRoom, actions, handlers, + coretypes::{ClientId, RoomId}, actions::{Destination, PendingMessage} }; use protocol::messages::*; @@ -105,6 +106,14 @@ self.rooms.iter_mut().find(|(_, r)| r.name == name).map(|(_, r)| r) } + pub fn find_client(&self, nick: &str) -> Option<&HWClient> { + self.clients.iter().find(|(_, c)| c.nick == nick).map(|(_, c)| c) + } + + pub fn find_client_mut(&mut self, nick: &str) -> Option<&mut HWClient> { + self.clients.iter_mut().find(|(_, c)| c.nick == nick).map(|(_, c)| c) + } + pub fn select_clients(&self, f: F) -> Vec where F: Fn(&(usize, &HWClient)) -> bool { self.clients.iter().filter(f)