--- 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)],
--- 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<HwRoomControl<'a>> {
+ 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<VoteResult, VoteError> {
+ 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<String> {
+ self.room_mut().set_hedgehogs_number(number)
+ }
+
pub fn add_team(&mut self, mut info: Box<TeamInfo>) -> 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<Vec<String>> {
let (client, room) = self.get_mut();
let client_left = client.is_in_game();
--- 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<VoteResult, VoteError>,
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<VoteResult, VoteError>,
+ 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<bool> {
- 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));
--- 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 <nickname>, map <name>, pause, newseed, hedgehogs <number>".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();