# HG changeset patch # User alfadur # Date 1577210278 -10800 # Node ID 16d3c9acd7151e5fce5b3c634a58ed96489c4793 # Parent fbcee515b946325fffdb8f6a058025b7b14536af restore voting diff -r fbcee515b946 -r 16d3c9acd715 rust/hedgewars-server/src/core/room.rs --- a/rust/hedgewars-server/src/core/room.rs Tue Dec 24 12:46:23 2019 -0500 +++ b/rust/hedgewars-server/src/core/room.rs Tue Dec 24 20:57:58 2019 +0300 @@ -11,7 +11,7 @@ use std::{collections::HashMap, iter}; pub const MAX_TEAMS_IN_ROOM: u8 = 8; -pub const MAX_HEDGEHOGS_IN_ROOM: u8 = MAX_HEDGEHOGS_PER_TEAM * MAX_HEDGEHOGS_PER_TEAM; +pub const MAX_HEDGEHOGS_IN_ROOM: u8 = MAX_TEAMS_IN_ROOM * MAX_HEDGEHOGS_PER_TEAM; fn client_teams_impl( teams: &[(ClientId, TeamInfo)], diff -r fbcee515b946 -r 16d3c9acd715 rust/hedgewars-server/src/core/server.rs --- a/rust/hedgewars-server/src/core/server.rs Tue Dec 24 12:46:23 2019 -0500 +++ b/rust/hedgewars-server/src/core/server.rs Tue Dec 24 20:57:58 2019 +0300 @@ -3,7 +3,7 @@ client::HwClient, indexslab::IndexSlab, room::HwRoom, - types::{ClientId, GameCfg, RoomId, ServerVar, TeamInfo}, + types::{ClientId, GameCfg, RoomId, ServerVar, TeamInfo, Vote, VoteType, Voting}, }; use crate::{protocol::messages::HwProtocolMessage::Greeting, utils}; @@ -101,6 +101,24 @@ } #[derive(Debug)] +pub enum StartVoteError { + VotingInProgress, +} + +#[derive(Debug)] +pub enum VoteResult { + Submitted, + Succeeded(VoteType), + Failed, +} + +#[derive(Debug)] +pub enum VoteError { + NoVoting, + AlreadyVoted, +} + +#[derive(Debug)] pub enum StartGameError { NotEnoughClans, NotEnoughTeams, @@ -167,11 +185,6 @@ } #[inline] - fn client_mut(&mut self, client_id: ClientId) -> &mut HwClient { - &mut self.clients[client_id] - } - - #[inline] pub fn has_client(&self, client_id: ClientId) -> bool { self.clients.contains(client_id) } @@ -187,11 +200,6 @@ } #[inline] - pub fn room_mut(&mut self, room_id: RoomId) -> &mut HwRoom { - &mut self.rooms[room_id] - } - - #[inline] pub fn get_room(&self, room_id: RoomId) -> Option<&HwRoom> { self.rooms.get(room_id) } @@ -538,6 +546,11 @@ ) } + pub fn change_client<'b: 'a>(self, client_id: ClientId) -> Option> { + let room_id = self.room_id; + HwRoomControl::new(self.server, client_id).filter(|c| c.room_id == room_id) + } + pub fn leave_room(&mut self) -> LeaveRoomResult { let (client, room) = self.get_mut(); room.players_number -= 1; @@ -586,7 +599,6 @@ new_master.set_is_master(true); if protocol_number < 42 { - todo!(); let nick = new_master.nick.clone(); self.room_mut().name = nick; } @@ -655,8 +667,44 @@ } } - pub fn vote(&mut self) { - todo!("port from the room handler") + pub fn start_vote(&mut self, kind: VoteType) -> Result<(), StartVoteError> { + use StartVoteError::*; + match self.room().voting { + Some(_) => Err(VotingInProgress), + None => { + let voting = Voting::new(kind, self.server.room_clients(self.room_id).collect()); + self.room_mut().voting = Some(voting); + Ok(()) + } + } + } + + pub fn vote(&mut self, vote: Vote) -> Result { + use self::{VoteError::*, VoteResult::*}; + let client_id = self.client_id; + if let Some(ref mut voting) = self.room_mut().voting { + if vote.is_forced || voting.votes.iter().all(|(id, _)| client_id != *id) { + voting.votes.push((client_id, vote.is_pro)); + 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 vote.is_forced && vote.is_pro || pro >= success_quota { + let voting = self.room_mut().voting.take().unwrap(); + Ok(Succeeded(voting.kind)) + } else if vote.is_forced && !vote.is_pro + || contra > voting.voters.len() - success_quota + { + Ok(Failed) + } else { + Ok(Submitted) + } + } else { + Err(AlreadyVoted) + } + } else { + Err(NoVoting) + } } pub fn add_engine_message(&mut self) { @@ -759,6 +807,10 @@ } } + pub fn set_hedgehogs_number(&mut self, number: u8) -> Vec { + self.room_mut().set_hedgehogs_number(number) + } + pub fn add_team(&mut self, mut info: Box) -> Result<&TeamInfo, AddTeamError> { use AddTeamError::*; let (client, room) = self.get_mut(); @@ -841,6 +893,10 @@ self.room_mut().save_config(name, location); } + pub fn load_config(&mut self, name: &str) -> Option<&str> { + self.room_mut().load_config(name) + } + pub fn delete_config(&mut self, name: &str) -> bool { self.room_mut().delete_config(name) } @@ -885,6 +941,13 @@ } } + pub fn toggle_pause(&mut self) -> bool { + if let Some(ref mut info) = self.room_mut().game_info { + info.is_paused = !info.is_paused; + } + self.room_mut().game_info.is_some() + } + pub fn leave_game(&mut self) -> Option> { let (client, room) = self.get_mut(); let client_left = client.is_in_game(); diff -r fbcee515b946 -r 16d3c9acd715 rust/hedgewars-server/src/handlers/common.rs --- a/rust/hedgewars-server/src/handlers/common.rs Tue Dec 24 12:46:23 2019 -0500 +++ b/rust/hedgewars-server/src/handlers/common.rs Tue Dec 24 20:57:58 2019 +0300 @@ -2,8 +2,11 @@ core::{ client::HwClient, room::HwRoom, - server::{EndGameResult, HwServer, JoinRoomError, LeaveRoomResult, StartGameError}, - types::{ClientId, GameCfg, RoomId, TeamInfo, Vote, VoteType}, + server::{ + EndGameResult, HwRoomControl, HwServer, JoinRoomError, LeaveRoomResult, StartGameError, + VoteError, VoteResult, + }, + types::{ClientId, GameCfg, RoomId, TeamInfo, Vote, VoteType, MAX_HEDGEHOGS_PER_TEAM}, }, protocol::messages::{ add_flags, remove_flags, server_chat, @@ -341,21 +344,102 @@ } } -pub fn apply_voting_result( - server: &mut HwServer, +pub fn check_vote( + server: &HwServer, + room: &HwRoom, + kind: &VoteType, + response: &mut Response, +) -> bool { + 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!".to_string()) + } + } + VoteType::Map(None) => { + let names: Vec<_> = room.saves.keys().cloned().collect(); + if names.is_empty() { + Some("/callvote map: No maps saved in this room!".to_string()) + } else { + Some(format!("Available maps: {}", names.join(", "))) + } + } + VoteType::Map(Some(name)) => { + if room.saves.get(&name[..]).is_some() { + None + } else { + Some("/callvote map: No such map!".to_string()) + } + } + VoteType::Pause => { + if room.game_info.is_some() { + None + } else { + Some("/callvote pause: No game in progress!".to_string()) + } + } + VoteType::NewSeed => None, + VoteType::HedgehogsPerTeam(number) => match number { + 1..=MAX_HEDGEHOGS_PER_TEAM => None, + _ => Some("/callvote hedgehogs: Specify number from 1 to 8.".to_string()), + }, + }; + + match error { + None => true, + Some(msg) => { + response.add(server_chat(msg).send_self()); + false + } + } +} + +pub fn get_vote_data( room_id: RoomId, + result: &Result, response: &mut Response, - kind: VoteType, ) { - match kind { - VoteType::Kick(nick) => { - if let Some(kicked_client) = server.find_client(&nick) { - let kicked_id = kicked_client.id; - if kicked_client.room_id == Some(room_id) { - if let Some(mut room_control) = server.get_room_control(kicked_client.id) { + match result { + Ok(VoteResult::Submitted) => { + response.add(server_chat("Your vote has been counted.".to_string()).send_self()) + } + Ok(VoteResult::Succeeded(_)) | Ok(VoteResult::Failed) => response.add( + server_chat("Voting closed.".to_string()) + .send_all() + .in_room(room_id), + ), + Err(VoteError::NoVoting) => { + response.add(server_chat("There's no voting going on.".to_string()).send_self()) + } + Err(VoteError::AlreadyVoted) => { + response.add(server_chat("You already have voted.".to_string()).send_self()) + } + } +} + +pub fn handle_vote( + mut room_control: HwRoomControl, + result: Result, + response: &mut super::Response, +) { + let room_id = room_control.room().id; + super::common::get_vote_data(room_control.room().id, &result, response); + + if let Ok(VoteResult::Succeeded(kind)) = result { + match kind { + VoteType::Kick(nick) => { + if let Some(kicked_client) = room_control.server().find_client(&nick) { + let kicked_id = kicked_client.id; + if let Some(mut room_control) = room_control.change_client(kicked_id) { response.add(Kicked.send(kicked_id)); let result = room_control.leave_room(); - get_room_leave_result( + super::common::get_room_leave_result( room_control.server(), room_control.room(), "kicked", @@ -365,108 +449,49 @@ } } } - } - VoteType::Map(None) => (), - VoteType::Map(Some(name)) => { - if let Some(location) = server.room_mut(room_id).load_config(&name) { - response.add( - server_chat(location.to_string()) - .send_all() - .in_room(room_id), - ); - let room = &server.room(room_id); - let room_master = if let Some(id) = room.master_id { - Some(server.client(id)) - } else { - None - }; - get_room_update(None, room, room_master, response); + VoteType::Map(None) => (), + VoteType::Map(Some(name)) => { + if let Some(location) = room_control.load_config(&name) { + let msg = server_chat(location.to_string()); + let room = room_control.room(); + response.add(msg.send_all().in_room(room.id)); - for client in server.iter_clients() { - if client.room_id == Some(room_id) { - super::common::get_room_config(server.room(room_id), client.id, response); + let room_master = room.master_id.map(|id| room_control.server().client(id)); + + super::common::get_room_update(None, room, room_master, response); + + for client_id in room_control.server().room_clients(room.id) { + super::common::get_room_config(room, client_id, response); } } } - } - VoteType::Pause => { - if let Some(ref mut info) = server.room_mut(room_id).game_info { - info.is_paused = !info.is_paused; - response.add( - server_chat("Pause toggled.".to_string()) - .send_all() - .in_room(room_id), - ); - response.add( - ForwardEngineMessage(vec![to_engine_msg(once(b'I'))]) - .send_all() - .in_room(room_id), - ); + VoteType::Pause => { + if room_control.toggle_pause() { + response.add( + server_chat("Pause toggled.".to_string()) + .send_all() + .in_room(room_id), + ); + response.add( + ForwardEngineMessage(vec![to_engine_msg(once(b'I'))]) + .send_all() + .in_room(room_id), + ); + } } - } - VoteType::NewSeed => { - let seed = thread_rng().gen_range(0, 1_000_000_000).to_string(); - let cfg = GameCfg::Seed(seed); - response.add(cfg.to_server_msg().send_all().in_room(room_id)); - server.room_mut(room_id).set_config(cfg); - } - VoteType::HedgehogsPerTeam(number) => { - let r = server.room_mut(room_id); - let nicks = r.set_hedgehogs_number(number); - - response.extend( - nicks - .into_iter() - .map(|n| HedgehogsNumber(n, number).send_all().in_room(room_id)), - ); - } - } -} - -fn add_vote(room: &mut HwRoom, response: &mut Response, vote: Vote) -> Option { - let client_id = response.client_id; - let mut result = None; - - if let Some(ref mut voting) = room.voting { - if vote.is_forced || voting.votes.iter().all(|(id, _)| client_id != *id) { - response.add(server_chat("Your vote has been counted.".to_string()).send_self()); - voting.votes.push((client_id, vote.is_pro)); - 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 vote.is_forced && vote.is_pro || pro >= success_quota { - result = Some(true); - } else if vote.is_forced && !vote.is_pro || contra > voting.voters.len() - success_quota - { - result = Some(false); + VoteType::NewSeed => { + let seed = thread_rng().gen_range(0, 1_000_000_000).to_string(); + let cfg = GameCfg::Seed(seed); + response.add(cfg.to_server_msg().send_all().in_room(room_id)); + room_control.set_config(cfg); } - } else { - response.add(server_chat("You already have voted.".to_string()).send_self()); - } - } else { - response.add(server_chat("There's no voting going on.".to_string()).send_self()); - } - - result -} - -pub fn submit_vote(server: &mut HwServer, vote: Vote, response: &mut Response) { - let client_id = response.client_id; - let client = server.client(client_id); - - if let Some(room_id) = client.room_id { - let room = server.room_mut(room_id); - - if let Some(res) = add_vote(room, response, vote) { - response.add( - server_chat("Voting closed.".to_string()) - .send_all() - .in_room(room.id), - ); - let voting = replace(&mut room.voting, None).unwrap(); - if res { - apply_voting_result(server, room_id, response, voting.kind); + VoteType::HedgehogsPerTeam(number) => { + let nicks = room_control.set_hedgehogs_number(number); + response.extend( + nicks + .into_iter() + .map(|n| HedgehogsNumber(n, number).send_all().in_room(room_id)), + ); } } } @@ -507,11 +532,7 @@ response: &mut Response, ) { let room = server.room(room_id); - let room_master = if let Some(id) = room.master_id { - Some(server.client(id)) - } else { - None - }; + let room_master = room.master_id.map(|id| server.client(id)); get_room_update(None, room, room_master, response); response.add(RoundFinished.send_all().in_room(room_id)); diff -r fbcee515b946 -r 16d3c9acd715 rust/hedgewars-server/src/handlers/inroom.rs --- a/rust/hedgewars-server/src/handlers/inroom.rs Tue Dec 24 12:46:23 2019 -0500 +++ b/rust/hedgewars-server/src/handlers/inroom.rs Tue Dec 24 20:57:58 2019 +0300 @@ -5,8 +5,8 @@ core::{ room::{HwRoom, RoomFlags, MAX_TEAMS_IN_ROOM}, server::{ - ChangeMasterError, ChangeMasterResult, HwRoomControl, LeaveRoomResult, ModifyTeamError, - StartGameError, + ChangeMasterError, ChangeMasterResult, HwRoomControl, HwServer, LeaveRoomResult, + ModifyTeamError, StartGameError, }, types, types::{ClientId, GameCfg, RoomId, VoteType, Voting, MAX_HEDGEHOGS_PER_TEAM}, @@ -235,11 +235,7 @@ ); let room = room_control.room(); - let room_master = if let Some(id) = room.master_id { - Some(room_control.server().client(id)) - } else { - None - }; + let room_master = room.master_id.map(|id| room_control.server().client(id)); super::common::get_room_update(None, room, room_master, response); } Err(AddTeamError::TooManyTeams) => response.warn(TOO_MANY_TEAMS), @@ -365,91 +361,49 @@ response.add(server_chat("Available callvote commands: kick , map , pause, newseed, hedgehogs ".to_string()) .send_self()); } - /*CallVote(Some(kind)) => { - let is_in_game = room.game_info.is_some(); - let error = match &kind { - VoteType::Kick(nick) => { - if room_control.server() - .find_client(&nick) - .filter(|c| c.room_id == Some(room_id)) - .is_some() - { - None - } else { - Some("/callvote kick: No such user!".to_string()) + CallVote(Some(kind)) => { + use crate::core::server::StartVoteError; + let room_id = room_control.room().id; + if super::common::check_vote( + room_control.server(), + room_control.room(), + &kind, + response, + ) { + match room_control.start_vote(kind.clone()) { + Ok(()) => { + let msg = voting_description(&kind); + response.add(server_chat(msg).send_all().in_room(room_id)); + let vote_result = room_control.vote(types::Vote { + is_pro: true, + is_forced: false, + }); + super::common::handle_vote(room_control, vote_result, response); } - } - VoteType::Map(None) => { - let names: Vec<_> = room.saves.keys().cloned().collect(); - if names.is_empty() { - Some("/callvote map: No maps saved in this room!".to_string()) - } else { - Some(format!("Available maps: {}", names.join(", "))) - } - } - VoteType::Map(Some(name)) => { - if room.saves.get(&name[..]).is_some() { - None - } else { - Some("/callvote map: No such map!".to_string()) - } - } - VoteType::Pause => { - if is_in_game { - None - } else { - Some("/callvote pause: No game in progress!".to_string()) + Err(StartVoteError::VotingInProgress) => { + response.add( + server_chat("There is already voting in progress".to_string()) + .send_self(), + ); } } - VoteType::NewSeed => None, - VoteType::HedgehogsPerTeam(number) => match number { - 1..=MAX_HEDGEHOGS_PER_TEAM => None, - _ => Some("/callvote hedgehogs: Specify number from 1 to 8.".to_string()), - }, - }; - - match error { - None => { - let msg = voting_description(&kind); - let voting = Voting::new(kind, room_control.server().room_clients(client_id).collect()); - let room = room_control.server().room_mut(room_id); - room.voting = Some(voting); - response.add(server_chat(msg).send_all().in_room(room_id)); - super::common::submit_vote( - room_control.server(), - types::Vote { - is_pro: true, - is_forced: false, - }, - response, - ); - } - Some(msg) => { - response.add(server_chat(msg).send_self()); - } } - }*/ - /*Vote(vote) => { - super::common::submit_vote( - room_control.server(), - types::Vote { - is_pro: vote, - is_forced: false, - }, - response, - ); - }*/ - /*ForceVote(vote) => { + } + Vote(vote) => { + let vote_result = room_control.vote(types::Vote { + is_pro: vote, + is_forced: false, + }); + super::common::handle_vote(room_control, vote_result, response); + } + ForceVote(vote) => { let is_forced = client.is_admin(); - super::common::submit_vote( - room_control.server(), - types::Vote { - is_pro: vote, - is_forced, - }, - response, - ); - }*/ + let vote_result = room_control.vote(types::Vote { + is_pro: vote, + is_forced, + }); + super::common::handle_vote(room_control, vote_result, response); + } ToggleRestrictJoin | ToggleRestrictTeams | ToggleRegisteredOnly => { if room_control.toggle_flag(room_message_flag(&message)) { let (client, room) = room_control.get();