restore voting
authoralfadur <mail@none>
Tue, 24 Dec 2019 20:57:58 +0300
changeset 15547 16d3c9acd715
parent 15546 fbcee515b946
child 15548 24f692e791d3
restore voting
rust/hedgewars-server/src/core/room.rs
rust/hedgewars-server/src/core/server.rs
rust/hedgewars-server/src/handlers/common.rs
rust/hedgewars-server/src/handlers/inroom.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)],
--- 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();