# HG changeset patch # User alfadur # Date 1577134026 -10800 # Node ID f4f6060b536c11a85ae09c44a7b03edf3be6aa9b # Parent 4a0b06b031993ba94e42094b286548ebdb75a55e add a separate interface for modifying room state diff -r 4a0b06b03199 -r f4f6060b536c rust/hedgewars-server/src/core/server.rs --- a/rust/hedgewars-server/src/core/server.rs Mon Dec 23 18:55:25 2019 +0300 +++ b/rust/hedgewars-server/src/core/server.rs Mon Dec 23 23:47:06 2019 +0300 @@ -3,7 +3,7 @@ client::HwClient, indexslab::IndexSlab, room::HwRoom, - types::{ClientId, RoomId, ServerVar, TeamInfo}, + types::{ClientId, GameCfg, RoomId, ServerVar, TeamInfo}, }; use crate::{protocol::messages::HwProtocolMessage::Greeting, utils}; @@ -11,7 +11,7 @@ use bitflags::*; use log::*; use slab::Slab; -use std::{borrow::BorrowMut, collections::HashSet, iter, mem::replace}; +use std::{borrow::BorrowMut, cmp::min, collections::HashSet, iter, mem::replace}; #[derive(Debug)] pub enum CreateRoomError { @@ -40,11 +40,6 @@ } #[derive(Debug)] -pub enum LeaveRoomError { - NoRoom, -} - -#[derive(Debug)] pub struct ChangeMasterResult { pub old_master_id: Option, pub new_master_id: ClientId, @@ -80,6 +75,25 @@ } #[derive(Debug)] +pub enum SetTeamCountError { + InvalidNumber, + NotMaster, +} + +#[derive(Debug)] +pub enum SetHedgehogsError { + NoTeam, + InvalidNumber(u8), + NotMaster, +} + +#[derive(Debug)] +pub enum SetConfigError { + NotMaster, + RoomFixed, +} + +#[derive(Debug)] pub enum ModifyRoomNameError { AccessDenied, InvalidName, @@ -198,12 +212,18 @@ } #[inline] - pub fn client_and_room_mut( - &mut self, - client_id: ClientId, - room_id: RoomId, - ) -> (&HwClient, &HwRoom) { - (&self.clients[client_id], &mut self.rooms[room_id]) + fn client_and_room_mut(&mut self, client_id: ClientId) -> Option<(&mut HwClient, &mut HwRoom)> { + let client = &mut self.clients[client_id]; + if let Some(room_id) = client.room_id { + Some((client, &mut self.rooms[room_id])) + } else { + None + } + } + + #[inline] + pub fn get_room_control(&mut self, client_id: ClientId) -> Option { + HwRoomControl::new(self, client_id) } #[inline] @@ -313,226 +333,6 @@ } } - pub fn leave_room(&mut self, client_id: ClientId) -> Result { - let client = &mut self.clients[client_id]; - if let Some(room_id) = client.room_id { - let room = &mut self.rooms[room_id]; - - room.players_number -= 1; - client.room_id = None; - - let is_empty = room.players_number == 0; - let is_fixed = room.is_fixed(); - let was_master = room.master_id == Some(client_id); - let was_in_game = client.is_in_game(); - let mut removed_teams = vec![]; - - if is_empty && !is_fixed { - if client.is_ready() && room.ready_players_number > 0 { - room.ready_players_number -= 1; - } - - removed_teams = room - .client_teams(client.id) - .map(|t| t.name.clone()) - .collect(); - - for team_name in &removed_teams { - room.remove_team(team_name); - } - - if client.is_master() && !is_fixed { - client.set_is_master(false); - room.master_id = None; - } - } - - client.set_is_ready(false); - client.set_is_in_game(false); - - if !is_fixed { - if room.players_number == 0 { - self.rooms.remove(room_id); - } else if room.master_id == None { - let new_master_id = self.room_clients(room_id).next(); - if let Some(new_master_id) = new_master_id { - let room = &mut self.rooms[room_id]; - room.master_id = Some(new_master_id); - let new_master = &mut self.clients[new_master_id]; - new_master.set_is_master(true); - - if room.protocol_number < 42 { - room.name = new_master.nick.clone(); - } - - room.set_join_restriction(false); - room.set_team_add_restriction(false); - room.set_unregistered_players_restriction(true); - } - } - } - - if is_empty && !is_fixed { - Ok(LeaveRoomResult::RoomRemoved) - } else { - Ok(LeaveRoomResult::RoomRemains { - is_empty, - was_master, - was_in_game, - new_master: self.rooms[room_id].master_id, - removed_teams, - }) - } - } else { - Err(LeaveRoomError::NoRoom) - } - } - - pub fn change_master( - &mut self, - client_id: ClientId, - room_id: RoomId, - new_master_nick: String, - ) -> Result { - let client = &mut self.clients[client_id]; - let room = &mut self.rooms[room_id]; - - if client.is_admin() || room.master_id == Some(client_id) { - let new_master_id = self - .clients - .iter() - .find(|(_, c)| c.nick == new_master_nick) - .map(|(id, _)| id); - - match new_master_id { - Some(new_master_id) if new_master_id == client_id => { - Err(ChangeMasterError::AlreadyMaster) - } - Some(new_master_id) => { - let new_master = &mut self.clients[new_master_id]; - if new_master.room_id == Some(room_id) { - self.clients[new_master_id].set_is_master(true); - let old_master_id = room.master_id; - if let Some(master_id) = old_master_id { - self.clients[master_id].set_is_master(false); - } - room.master_id = Some(new_master_id); - Ok(ChangeMasterResult { - old_master_id, - new_master_id, - }) - } else { - Err(ChangeMasterError::ClientNotInRoom) - } - } - None => Err(ChangeMasterError::NoClient), - } - } else { - Err(ChangeMasterError::NoAccess) - } - } - - pub fn start_game(&mut self, room_id: RoomId) -> Result, StartGameError> { - let (room_clients, room_nicks): (Vec<_>, Vec<_>) = self - .clients - .iter() - .map(|(id, c)| (id, c.nick.clone())) - .unzip(); - - let room = &mut self.rooms[room_id]; - - if !room.has_multiple_clans() { - Err(StartGameError::NotEnoughClans) - } else if room.protocol_number <= 43 && room.players_number != room.ready_players_number { - Err(StartGameError::NotReady) - } else if room.game_info.is_some() { - Err(StartGameError::AlreadyInGame) - } else { - room.start_round(); - for id in room_clients { - let c = &mut self.clients[id]; - c.set_is_in_game(true); - c.team_indices = room.client_team_indices(c.id); - } - Ok(room_nicks) - } - } - - pub fn leave_game(&mut self, client_id: ClientId) -> Option> { - let client = &mut self.clients[client_id]; - let client_left = client.is_in_game(); - if client_left { - client.set_is_in_game(false); - let room = &mut self.rooms[client.room_id.expect("Client should've been in the game")]; - - let team_names: Vec<_> = room - .client_teams(client_id) - .map(|t| t.name.clone()) - .collect(); - - if let Some(ref mut info) = room.game_info { - info.teams_in_game -= team_names.len() as u8; - - for team_name in &team_names { - let remove_msg = - utils::to_engine_msg(std::iter::once(b'F').chain(team_name.bytes())); - if let Some(m) = &info.sync_msg { - info.msg_log.push(m.clone()); - } - if info.sync_msg.is_some() { - info.sync_msg = None - } - info.msg_log.push(remove_msg); - } - Some(team_names) - } else { - unreachable!(); - } - } else { - None - } - } - - pub fn end_game(&mut self, room_id: RoomId) -> EndGameResult { - let room = &mut self.rooms[room_id]; - room.ready_players_number = room.master_id.is_some() as u8; - - if let Some(info) = replace(&mut room.game_info, None) { - let joined_mid_game_clients = self - .clients - .iter() - .filter(|(_, c)| c.room_id == Some(room_id) && c.is_joined_mid_game()) - .map(|(_, c)| c.id) - .collect(); - - let unreadied_nicks: Vec<_> = self - .clients - .iter_mut() - .filter(|(_, c)| c.room_id == Some(room_id)) - .map(|(_, c)| { - c.set_is_ready(c.is_master()); - c.set_is_joined_mid_game(false); - c - }) - .filter_map(|c| { - if !c.is_master() { - Some(c.nick.clone()) - } else { - None - } - }) - .collect(); - - EndGameResult { - joined_mid_game_clients, - left_teams: info.left_teams.clone(), - unreadied_nicks, - } - } else { - unreachable!() - } - } - pub fn enable_super_power(&mut self, client_id: ClientId) -> bool { let client = &mut self.clients[client_id]; if client.is_admin() { @@ -541,116 +341,6 @@ client.is_admin() } - pub fn set_room_name( - &mut self, - client_id: ClientId, - room_id: RoomId, - mut name: String, - ) -> Result { - let room_exists = self.has_room(&name); - let room = &mut self.rooms[room_id]; - if room.is_fixed() || room.master_id != Some(client_id) { - Err(ModifyRoomNameError::AccessDenied) - } else if utils::is_name_illegal(&name) { - Err(ModifyRoomNameError::InvalidName) - } else if room_exists { - Err(ModifyRoomNameError::DuplicateName) - } else { - std::mem::swap(&mut room.name, &mut name); - Ok(name) - } - } - - pub fn add_team( - &mut self, - client_id: ClientId, - mut info: Box, - ) -> Result<&TeamInfo, AddTeamError> { - let client = &mut self.clients[client_id]; - if let Some(room_id) = client.room_id { - let room = &mut self.rooms[room_id]; - if room.teams.len() >= room.max_teams as usize { - Err(AddTeamError::TooManyTeams) - } else if room.addable_hedgehogs() == 0 { - Err(AddTeamError::TooManyHedgehogs) - } else if room.find_team(|t| t.name == info.name) != None { - Err(AddTeamError::TeamAlreadyExists) - } else if room.game_info.is_some() { - Err(AddTeamError::GameInProgress) - } else if room.is_team_add_restricted() { - Err(AddTeamError::Restricted) - } else { - info.owner = client.nick.clone(); - let team = room.add_team(client.id, *info, client.protocol_number < 42); - client.teams_in_game += 1; - client.clan = Some(team.color); - Ok(team) - } - } else { - unreachable!() - } - } - - pub fn remove_team( - &mut self, - client_id: ClientId, - team_name: &str, - ) -> Result<(), RemoveTeamError> { - let client = &mut self.clients[client_id]; - if let Some(room_id) = client.room_id { - let room = &mut self.rooms[room_id]; - match room.find_team_owner(team_name) { - None => Err(RemoveTeamError::NoTeam), - Some((id, _)) if id != client_id => Err(RemoveTeamError::TeamNotOwned), - Some(_) => { - client.teams_in_game -= 1; - client.clan = room.find_team_color(client.id); - room.remove_team(team_name); - Ok(()) - } - } - } else { - unreachable!(); - } - } - - pub fn set_team_color( - &mut self, - client_id: ClientId, - room_id: RoomId, - team_name: &str, - color: u8, - ) -> Result<(), ModifyTeamError> { - let client = &self.clients[client_id]; - let room = &mut self.rooms[room_id]; - if let Some((owner, team)) = room.find_team_and_owner_mut(|t| t.name == team_name) { - if !client.is_master() { - Err(ModifyTeamError::NotMaster) - } else { - team.color = color; - self.clients[owner].clan = Some(color); - Ok(()) - } - } else { - Err(ModifyTeamError::NoTeam) - } - } - - pub fn toggle_ready(&mut self, client_id: ClientId) -> bool { - let client = &mut self.clients[client_id]; - if let Some(room_id) = client.room_id { - let room = &mut self.rooms[room_id]; - - client.set_is_ready(!client.is_ready()); - if client.is_ready() { - room.ready_players_number += 1; - } else { - room.ready_players_number -= 1; - } - } - client.is_ready() - } - #[inline] pub fn set_var(&mut self, client_id: ClientId, var: ServerVar) -> Result<(), AccessError> { if self.clients[client_id].is_admin() { @@ -790,6 +480,489 @@ } } +pub struct HwRoomControl<'a> { + server: &'a mut HwServer, + client_id: ClientId, + room_id: RoomId, +} + +impl<'a> HwRoomControl<'a> { + #[inline] + pub fn new(server: &'a mut HwServer, client_id: ClientId) -> Option { + if let Some(room_id) = server.clients[client_id].room_id { + Some(Self { + server, + client_id, + room_id, + }) + } else { + None + } + } + + #[inline] + pub fn server(&self) -> &HwServer { + self.server + } + + #[inline] + pub fn client(&self) -> &HwClient { + &self.server.clients[self.client_id] + } + + #[inline] + fn client_mut(&mut self) -> &mut HwClient { + &mut self.server.clients[self.client_id] + } + + #[inline] + pub fn room(&self) -> &HwRoom { + &self.server.rooms[self.room_id] + } + + #[inline] + fn room_mut(&mut self) -> &mut HwRoom { + &mut self.server.rooms[self.room_id] + } + + #[inline] + pub fn get(&self) -> (&HwClient, &HwRoom) { + (self.client(), self.room()) + } + + #[inline] + fn get_mut(&mut self) -> (&mut HwClient, &mut HwRoom) { + ( + &mut self.server.clients[self.client_id], + &mut self.server.rooms[self.room_id], + ) + } + + pub fn leave_room(&mut self) -> LeaveRoomResult { + let (client, room) = self.get_mut(); + room.players_number -= 1; + client.room_id = None; + + let is_empty = room.players_number == 0; + let is_fixed = room.is_fixed(); + let was_master = room.master_id == Some(client.id); + let was_in_game = client.is_in_game(); + let mut removed_teams = vec![]; + + if is_empty && !is_fixed { + if client.is_ready() && room.ready_players_number > 0 { + room.ready_players_number -= 1; + } + + removed_teams = room + .client_teams(client.id) + .map(|t| t.name.clone()) + .collect(); + + for team_name in &removed_teams { + room.remove_team(team_name); + } + + if client.is_master() && !is_fixed { + client.set_is_master(false); + room.master_id = None; + } + } + + client.set_is_ready(false); + client.set_is_in_game(false); + + if !is_fixed { + if room.players_number == 0 { + self.server.rooms.remove(self.room_id); + } else if room.master_id == None { + let protocol_number = room.protocol_number; + let new_master_id = self.server.room_clients(self.room_id).next(); + + if let Some(new_master_id) = new_master_id { + let room = self.room_mut(); + room.master_id = Some(new_master_id); + let new_master = &mut self.server.clients[new_master_id]; + new_master.set_is_master(true); + + if protocol_number < 42 { + todo!(); + let nick = new_master.nick.clone(); + self.room_mut().name = nick; + } + + let room = self.room_mut(); + room.set_join_restriction(false); + room.set_team_add_restriction(false); + room.set_unregistered_players_restriction(true); + } + } + } + + if is_empty && !is_fixed { + LeaveRoomResult::RoomRemoved + } else { + LeaveRoomResult::RoomRemains { + is_empty, + was_master, + was_in_game, + new_master: self.room().master_id, + removed_teams, + } + } + } + + pub fn change_master( + &mut self, + new_master_nick: String, + ) -> Result { + use ChangeMasterError::*; + let (client, room) = self.get_mut(); + + if client.is_admin() || room.master_id == Some(client.id) { + let new_master_id = self + .server + .clients + .iter() + .find(|(_, c)| c.nick == new_master_nick) + .map(|(id, _)| id); + + match new_master_id { + Some(new_master_id) if new_master_id == self.client_id => Err(AlreadyMaster), + Some(new_master_id) => { + let new_master = &mut self.server.clients[new_master_id]; + if new_master.room_id == Some(self.room_id) { + self.server.clients[new_master_id].set_is_master(true); + let room = self.room_mut(); + let old_master_id = self.room().master_id; + + if let Some(master_id) = old_master_id { + self.server.clients[master_id].set_is_master(false); + } + self.room_mut().master_id = Some(new_master_id); + Ok(ChangeMasterResult { + old_master_id, + new_master_id, + }) + } else { + Err(ClientNotInRoom) + } + } + None => Err(NoClient), + } + } else { + Err(NoAccess) + } + } + + pub fn vote(&mut self) { + todo!("port from the room handler") + } + + pub fn add_engine_message(&mut self) { + todo!("port from the room handler") + } + + pub fn toggle_flag(&mut self, flags: super::room::RoomFlags) -> bool { + let (client, room) = self.get_mut(); + if client.is_master() { + room.flags.toggle(flags); + } + client.is_master() + } + + pub fn fix_room(&mut self) -> Result<(), AccessError> { + let (client, room) = self.get_mut(); + if client.is_admin() { + room.set_is_fixed(true); + room.set_join_restriction(false); + room.set_team_add_restriction(false); + room.set_unregistered_players_restriction(true); + Ok(()) + } else { + Err(AccessError()) + } + } + + pub fn unfix_room(&mut self) -> Result<(), AccessError> { + let (client, room) = self.get_mut(); + if client.is_admin() { + room.set_is_fixed(false); + Ok(()) + } else { + Err(AccessError()) + } + } + + pub fn set_room_name(&mut self, mut name: String) -> Result { + use ModifyRoomNameError::*; + let room_exists = self.server.has_room(&name); + let (client, room) = self.get_mut(); + if room.is_fixed() || room.master_id != Some(client.id) { + Err(AccessDenied) + } else if utils::is_name_illegal(&name) { + Err(InvalidName) + } else if room_exists { + Err(DuplicateName) + } else { + std::mem::swap(&mut room.name, &mut name); + Ok(name) + } + } + + pub fn set_room_greeting(&mut self, greeting: Option) -> Result<(), AccessError> { + let (client, room) = self.get_mut(); + if client.is_admin() { + room.greeting = greeting.unwrap_or(String::new()); + Ok(()) + } else { + Err(AccessError()) + } + } + + pub fn set_room_max_teams(&mut self, count: u8) -> Result<(), SetTeamCountError> { + use SetTeamCountError::*; + let (client, room) = self.get_mut(); + if !client.is_master() { + Err(NotMaster) + } else if !(2..=super::room::MAX_TEAMS_IN_ROOM).contains(&count) { + Err(InvalidNumber) + } else { + room.max_teams = count; + Ok(()) + } + } + + pub fn set_team_hedgehogs_number( + &mut self, + team_name: &str, + number: u8, + ) -> Result<(), SetHedgehogsError> { + use SetHedgehogsError::*; + let (client, room) = self.get_mut(); + let addable_hedgehogs = room.addable_hedgehogs(); + if let Some((_, team)) = room.find_team_and_owner_mut(|t| t.name == team_name) { + let max_hedgehogs = min( + super::room::MAX_HEDGEHOGS_IN_ROOM, + addable_hedgehogs + team.hedgehogs_number, + ); + if !client.is_master() { + Err(NotMaster) + } else if !(1..=max_hedgehogs).contains(&number) { + Err(InvalidNumber(team.hedgehogs_number)) + } else { + team.hedgehogs_number = number; + Ok(()) + } + } else { + Err(NoTeam) + } + } + + pub fn add_team(&mut self, mut info: Box) -> Result<&TeamInfo, AddTeamError> { + use AddTeamError::*; + let (client, room) = self.get_mut(); + if room.teams.len() >= room.max_teams as usize { + Err(TooManyTeams) + } else if room.addable_hedgehogs() == 0 { + Err(TooManyHedgehogs) + } else if room.find_team(|t| t.name == info.name) != None { + Err(TeamAlreadyExists) + } else if room.game_info.is_some() { + Err(GameInProgress) + } else if room.is_team_add_restricted() { + Err(Restricted) + } else { + info.owner = client.nick.clone(); + let team = room.add_team(client.id, *info, client.protocol_number < 42); + client.teams_in_game += 1; + client.clan = Some(team.color); + Ok(team) + } + } + + pub fn remove_team(&mut self, team_name: &str) -> Result<(), RemoveTeamError> { + use RemoveTeamError::*; + let (client, room) = self.get_mut(); + match room.find_team_owner(team_name) { + None => Err(NoTeam), + Some((id, _)) if id != client.id => Err(RemoveTeamError::TeamNotOwned), + Some(_) => { + client.teams_in_game -= 1; + client.clan = room.find_team_color(client.id); + room.remove_team(team_name); + Ok(()) + } + } + } + + pub fn set_team_color(&mut self, team_name: &str, color: u8) -> Result<(), ModifyTeamError> { + use ModifyTeamError::*; + let (client, room) = self.get_mut(); + if let Some((owner, team)) = room.find_team_and_owner_mut(|t| t.name == team_name) { + if !client.is_master() { + Err(NotMaster) + } else { + team.color = color; + self.server.clients[owner].clan = Some(color); + Ok(()) + } + } else { + Err(NoTeam) + } + } + + pub fn set_config(&mut self, cfg: GameCfg) -> Result<(), SetConfigError> { + use SetConfigError::*; + let (client, room) = self.get_mut(); + if room.is_fixed() { + Err(RoomFixed) + } else if !client.is_master() { + Err(NotMaster) + } else { + let cfg = match cfg { + GameCfg::Scheme(name, mut values) => { + if client.protocol_number == 49 && values.len() >= 2 { + let mut s = "X".repeat(50); + s.push_str(&values.pop().unwrap()); + values.push(s); + } + GameCfg::Scheme(name, values) + } + cfg => cfg, + }; + + room.set_config(cfg); + Ok(()) + } + } + + pub fn save_config(&mut self, name: String, location: String) { + self.room_mut().save_config(name, location); + } + + pub fn delete_config(&mut self, name: &str) -> bool { + self.room_mut().delete_config(name) + } + + pub fn toggle_ready(&mut self) -> bool { + let (client, room) = self.get_mut(); + client.set_is_ready(!client.is_ready()); + if client.is_ready() { + room.ready_players_number += 1; + } else { + room.ready_players_number -= 1; + } + client.is_ready() + } + + pub fn start_game(&mut self) -> Result, StartGameError> { + use StartGameError::*; + let (room_clients, room_nicks): (Vec<_>, Vec<_>) = self + .server + .clients + .iter() + .map(|(id, c)| (id, c.nick.clone())) + .unzip(); + + let room = self.room_mut(); + + if !room.has_multiple_clans() { + Err(NotEnoughClans) + } else if room.protocol_number <= 43 && room.players_number != room.ready_players_number { + Err(NotReady) + } else if room.game_info.is_some() { + Err(AlreadyInGame) + } else { + room.start_round(); + for id in room_clients { + let team_indices = self.room().client_team_indices(id); + let c = &mut self.server.clients[id]; + c.set_is_in_game(true); + c.team_indices = team_indices; + } + Ok(room_nicks) + } + } + + pub fn leave_game(&mut self) -> Option> { + let (client, room) = self.get_mut(); + let client_left = client.is_in_game(); + if client_left { + client.set_is_in_game(false); + + let team_names: Vec<_> = room + .client_teams(client.id) + .map(|t| t.name.clone()) + .collect(); + + if let Some(ref mut info) = room.game_info { + info.teams_in_game -= team_names.len() as u8; + + for team_name in &team_names { + let remove_msg = + utils::to_engine_msg(std::iter::once(b'F').chain(team_name.bytes())); + if let Some(m) = &info.sync_msg { + info.msg_log.push(m.clone()); + } + if info.sync_msg.is_some() { + info.sync_msg = None + } + info.msg_log.push(remove_msg); + } + Some(team_names) + } else { + unreachable!(); + } + } else { + None + } + } + + pub fn end_game(&mut self) -> Option { + let room = self.room_mut(); + room.ready_players_number = room.master_id.is_some() as u8; + + if let Some(info) = replace(&mut room.game_info, None) { + let room_id = room.id; + let joined_mid_game_clients = self + .server + .clients + .iter() + .filter(|(_, c)| c.room_id == Some(self.room_id) && c.is_joined_mid_game()) + .map(|(_, c)| c.id) + .collect(); + + let unreadied_nicks: Vec<_> = self + .server + .clients + .iter_mut() + .filter(|(_, c)| c.room_id == Some(room_id)) + .map(|(_, c)| { + c.set_is_ready(c.is_master()); + c.set_is_joined_mid_game(false); + c + }) + .filter_map(|c| { + if !c.is_master() { + Some(c.nick.clone()) + } else { + None + } + }) + .collect(); + + Some(EndGameResult { + joined_mid_game_clients, + left_teams: info.left_teams.clone(), + unreadied_nicks, + }) + } else { + None + } + } +} + fn allocate_room(rooms: &mut Slab) -> &mut HwRoom { let entry = rooms.vacant_entry(); let room = HwRoom::new(entry.key()); diff -r 4a0b06b03199 -r f4f6060b536c rust/hedgewars-server/src/handlers.rs --- a/rust/hedgewars-server/src/handlers.rs Mon Dec 23 18:55:25 2019 +0300 +++ b/rust/hedgewars-server/src/handlers.rs Mon Dec 23 23:47:06 2019 +0300 @@ -357,11 +357,9 @@ response.warn(REPLAY_NOT_SUPPORTED); } } - _ => match state.server.client(client_id).room_id { + _ => match state.server.get_room_control(client_id) { None => inlobby::handle(&mut state.server, client_id, response, message), - Some(room_id) => { - inroom::handle(&mut state.server, client_id, response, room_id, message) - } + Some(control) => inroom::handle(control, response, message), }, } } diff -r 4a0b06b03199 -r f4f6060b536c rust/hedgewars-server/src/handlers/common.rs --- a/rust/hedgewars-server/src/handlers/common.rs Mon Dec 23 18:55:25 2019 +0300 +++ b/rust/hedgewars-server/src/handlers/common.rs Mon Dec 23 23:47:06 2019 +0300 @@ -2,9 +2,7 @@ core::{ client::HwClient, room::HwRoom, - server::{ - EndGameResult, HwServer, JoinRoomError, LeaveRoomError, LeaveRoomResult, StartGameError, - }, + server::{EndGameResult, HwServer, JoinRoomError, LeaveRoomResult, StartGameError}, types::{ClientId, GameCfg, RoomId, TeamInfo, Vote, VoteType}, }, protocol::messages::{ @@ -74,7 +72,8 @@ let server_msg = ServerMessage(server.get_greetings(client).to_string()); let rooms_msg = Rooms( - server.iter_rooms() + server + .iter_rooms() .filter(|r| r.protocol_number == client.protocol_number) .flat_map(|r| r.info(r.master_id.map(|id| server.client(id)))) .collect(), @@ -260,30 +259,15 @@ } } -pub fn get_room_leave_data( - server: &HwServer, - room_id: RoomId, - leave_message: &str, - result: Result, - response: &mut Response, -) { - match result { - Ok(result) => { - let room = server.room(room_id); - get_room_leave_result(server, room, leave_message, result, response) - } - Err(_) => (), - } -} - pub fn remove_client(server: &mut HwServer, response: &mut Response, msg: String) { let client_id = response.client_id(); let client = server.client(client_id); let nick = client.nick.clone(); - if let Some(room_id) = client.room_id { - let result = server.leave_room(client_id); - get_room_leave_data(server, room_id, &msg, result, response); + if let Some(mut room_control) = server.get_room_control(client_id) { + let room_id = room_control.room().id; + let result = room_control.leave_room(); + get_room_leave_result(server, server.room(room_id), &msg, result, response); } server.remove_client(client_id); @@ -365,12 +349,20 @@ ) { match kind { VoteType::Kick(nick) => { - if let Some(client) = server.find_client(&nick) { - if client.room_id == Some(room_id) { - let id = client.id; - response.add(Kicked.send(id)); - let result = server.leave_room(id); - get_room_leave_data(server, room_id, "kicked", result, response); + 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) { + response.add(Kicked.send(kicked_id)); + let result = room_control.leave_room(); + get_room_leave_result( + room_control.server(), + room_control.room(), + "kicked", + result, + response, + ); + } } } } diff -r 4a0b06b03199 -r f4f6060b536c rust/hedgewars-server/src/handlers/inroom.rs --- a/rust/hedgewars-server/src/handlers/inroom.rs Mon Dec 23 18:55:25 2019 +0300 +++ b/rust/hedgewars-server/src/handlers/inroom.rs Mon Dec 23 23:47:06 2019 +0300 @@ -1,11 +1,11 @@ use super::{common::rnd_reply, strings::*}; use crate::core::room::GameInfo; -use crate::core::server::AddTeamError; +use crate::core::server::{AddTeamError, SetTeamCountError}; use crate::{ core::{ room::{HwRoom, RoomFlags, MAX_TEAMS_IN_ROOM}, server::{ - ChangeMasterError, ChangeMasterResult, HwServer, LeaveRoomResult, ModifyTeamError, + ChangeMasterError, ChangeMasterResult, HwRoomControl, LeaveRoomResult, ModifyTeamError, StartGameError, }, types, @@ -106,13 +106,12 @@ } pub fn handle( - server: &mut HwServer, - client_id: ClientId, + mut room_control: HwRoomControl, response: &mut super::Response, - room_id: RoomId, message: HwProtocolMessage, ) { - let (client, room) = server.client_and_room_mut(client_id, room_id); + let (client, room) = room_control.get(); + let (client_id, room_id) = (client.id, room.id); use crate::protocol::messages::HwProtocolMessage::*; match message { @@ -122,8 +121,14 @@ None => "part".to_string(), }; - let result = server.leave_room(client_id); - super::common::get_room_leave_data(server, room_id, &msg, result, response); + let result = room_control.leave_room(); + super::common::get_room_leave_result( + room_control.server(), + room_control.room(), + &msg, + result, + response, + ); } Chat(msg) => { response.add( @@ -146,41 +151,35 @@ } } Fix => { - if client.is_admin() { - room.set_is_fixed(true); - room.set_join_restriction(false); - room.set_team_add_restriction(false); - room.set_unregistered_players_restriction(true); - } else { + if let Err(_) = room_control.fix_room() { response.warn(ACCESS_DENIED) } } Unfix => { - if client.is_admin() { - room.set_is_fixed(false); - } else { + if let Err(_) = room_control.unfix_room() { response.warn(ACCESS_DENIED) } } Greeting(text) => { - if client.is_admin() || client.is_master() && !room.is_fixed() { - room.greeting = text.unwrap_or(String::new()); + if let Err(_) = room_control.set_room_greeting(text) { + response.warn(ACCESS_DENIED) } } MaxTeams(count) => { - if !client.is_master() { - response.warn(NOT_MASTER); - } else if !(2..=MAX_TEAMS_IN_ROOM).contains(&count) { - response.warn("/maxteams: specify number from 2 to 8"); - } else { - room.max_teams = count; - } + use crate::core::server::SetTeamCountError; + match room_control.set_room_max_teams(count) { + Ok(()) => {} + Err(SetTeamCountError::NotMaster) => response.warn(NOT_MASTER), + Err(SetTeamCountError::InvalidNumber) => { + response.warn("/maxteams: specify number from 2 to 8") + } + }; } RoomName(new_name) => { use crate::core::server::ModifyRoomNameError; - match server.set_room_name(client_id, room_id, new_name) { + match room_control.set_room_name(new_name) { Ok(old_name) => { - let (client, room) = server.client_and_room(client_id, room_id); + let (client, room) = room_control.get(); super::common::get_room_update(Some(old_name), room, Some(client), response) } Err(ModifyRoomNameError::AccessDenied) => response.warn(ACCESS_DENIED), @@ -189,12 +188,12 @@ } } ToggleReady => { - let flags = if server.toggle_ready(client_id) { + let flags = if room_control.toggle_ready() { add_flags(&[Flags::Ready]) } else { remove_flags(&[Flags::Ready]) }; - let (client, room) = server.client_and_room(client_id, room_id); + let (client, room) = room_control.get(); let msg = if client.protocol_number < 38 { LegacyReady(client.is_ready(), vec![client.nick.clone()]) @@ -204,13 +203,18 @@ response.add(msg.send_all().in_room(room_id)); if room.is_fixed() && room.ready_players_number == room.players_number { - let result = server.start_game(room_id); - super::common::get_start_game_data(server, room_id, result, response); + let result = room_control.start_game(); + super::common::get_start_game_data( + room_control.server(), + room_id, + result, + response, + ); } } AddTeam(info) => { use crate::core::server::AddTeamError; - match server.add_team(client_id, info) { + match room_control.add_team(info) { Ok(team) => { response.add(TeamAccepted(team.name.clone()).send_self()); response.add( @@ -230,9 +234,9 @@ .in_room(room_id), ); - let room = server.room(room_id); + let room = room_control.room(); let room_master = if let Some(id) = room.master_id { - Some(server.client(id)) + Some(room_control.server().client(id)) } else { None }; @@ -247,9 +251,9 @@ } RemoveTeam(name) => { use crate::core::server::RemoveTeamError; - match server.remove_team(client_id, &name) { + match room_control.remove_team(&name) { Ok(()) => { - let (client, room) = server.client_and_room(client_id, room_id); + let (client, room) = room_control.get(); let removed_teams = vec![name]; super::common::get_remove_teams_data( @@ -261,8 +265,14 @@ match room.game_info { Some(ref info) if info.teams_in_game == 0 => { - let result = server.end_game(room_id); - super::common::get_end_game_result(server, room_id, result, response); + if let Some(result) = room_control.end_game() { + super::common::get_end_game_result( + room_control.server(), + room_id, + result, + response, + ); + } } _ => (), } @@ -272,62 +282,42 @@ } } SetHedgehogsNumber(team_name, number) => { - let addable_hedgehogs = room.addable_hedgehogs(); - if let Some((_, team)) = room.find_team_and_owner_mut(|t| t.name == team_name) { - let max_hedgehogs = min( - MAX_HEDGEHOGS_PER_TEAM, - addable_hedgehogs + team.hedgehogs_number, - ); - if !client.is_master() { - response.error(NOT_MASTER); - } else if !(1..=max_hedgehogs).contains(&number) { - response - .add(HedgehogsNumber(team.name.clone(), team.hedgehogs_number).send_self()); - } else { - team.hedgehogs_number = number; + use crate::core::server::SetHedgehogsError; + match room_control.set_team_hedgehogs_number(&team_name, number) { + Ok(()) => { response.add( - HedgehogsNumber(team.name.clone(), number) + HedgehogsNumber(team_name.clone(), number) .send_all() .in_room(room_id) .but_self(), ); } - } else { - response.warn(NO_TEAM); - } - } - SetTeamColor(team_name, color) => { - match server.set_team_color(client_id, room_id, &team_name, color) { - Ok(()) => response.add( - TeamColor(team_name, color) - .send_all() - .in_room(room_id) - .but_self(), - ), - Err(ModifyTeamError::NoTeam) => response.warn(NO_TEAM), - Err(ModifyTeamError::NotMaster) => response.error(NOT_MASTER), + Err(SetHedgehogsError::NotMaster) => response.error(NOT_MASTER), + Err(SetHedgehogsError::NoTeam) => response.warn(NO_TEAM), + Err(SetHedgehogsError::InvalidNumber(previous_number)) => { + response.add(HedgehogsNumber(team_name.clone(), previous_number).send_self()) + } } } + SetTeamColor(team_name, color) => match room_control.set_team_color(&team_name, color) { + Ok(()) => response.add( + TeamColor(team_name, color) + .send_all() + .in_room(room_id) + .but_self(), + ), + Err(ModifyTeamError::NoTeam) => response.warn(NO_TEAM), + Err(ModifyTeamError::NotMaster) => response.error(NOT_MASTER), + }, Cfg(cfg) => { - if room.is_fixed() { - response.warn(ACCESS_DENIED); - } else if !client.is_master() { - response.error(NOT_MASTER); - } else { - let cfg = match cfg { - GameCfg::Scheme(name, mut values) => { - if client.protocol_number == 49 && values.len() >= 2 { - let mut s = "X".repeat(50); - s.push_str(&values.pop().unwrap()); - values.push(s); - } - GameCfg::Scheme(name, values) - } - cfg => cfg, - }; - - response.add(cfg.to_server_msg().send_all().in_room(room.id).but_self()); - room.set_config(cfg); + use crate::core::server::SetConfigError; + let msg = cfg.to_server_msg(); + match room_control.set_config(cfg) { + Ok(()) => { + response.add(msg.send_all().in_room(room_control.room().id).but_self()); + } + Err(SetConfigError::NotMaster) => response.error(NOT_MASTER), + Err(SetConfigError::RoomFixed) => response.warn(ACCESS_DENIED), } } Save(name, location) => { @@ -336,7 +326,7 @@ .send_all() .in_room(room_id), ); - room.save_config(name, location); + room_control.save_config(name, location); } #[cfg(feature = "official-server")] SaveRoom(filename) => { @@ -361,7 +351,7 @@ } } Delete(name) => { - if !room.delete_config(&name) { + if !room_control.delete_config(&name) { response.add(Warning(format!("Save doesn't exist: {}", name)).send_self()); } else { response.add( @@ -375,11 +365,11 @@ response.add(server_chat("Available callvote commands: kick , map , pause, newseed, hedgehogs ".to_string()) .send_self()); } - CallVote(Some(kind)) => { + /*CallVote(Some(kind)) => { let is_in_game = room.game_info.is_some(); let error = match &kind { VoteType::Kick(nick) => { - if server + if room_control.server() .find_client(&nick) .filter(|c| c.room_id == Some(room_id)) .is_some() @@ -390,7 +380,7 @@ } } VoteType::Map(None) => { - let names: Vec<_> = server.room(room_id).saves.keys().cloned().collect(); + let names: Vec<_> = room.saves.keys().cloned().collect(); if names.is_empty() { Some("/callvote map: No maps saved in this room!".to_string()) } else { @@ -421,12 +411,12 @@ match error { None => { let msg = voting_description(&kind); - let voting = Voting::new(kind, server.room_clients(client_id).collect()); - let room = server.room_mut(room_id); + 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( - server, + room_control.server(), types::Vote { is_pro: true, is_forced: false, @@ -438,39 +428,39 @@ response.add(server_chat(msg).send_self()); } } - } - Vote(vote) => { + }*/ + /*Vote(vote) => { super::common::submit_vote( - server, + room_control.server(), types::Vote { is_pro: vote, is_forced: false, }, response, ); - } - ForceVote(vote) => { + }*/ + /*ForceVote(vote) => { let is_forced = client.is_admin(); super::common::submit_vote( - server, + room_control.server(), types::Vote { is_pro: vote, is_forced, }, response, ); - } + }*/ ToggleRestrictJoin | ToggleRestrictTeams | ToggleRegisteredOnly => { - if client.is_master() { - room.flags.toggle(room_message_flag(&message)); + if room_control.toggle_flag(room_message_flag(&message)) { + let (client, room) = room_control.get(); super::common::get_room_update(None, room, Some(&client), response); } } StartGame => { - let result = server.start_game(room_id); - super::common::get_start_game_data(server, room_id, result, response); + let result = room_control.start_game(); + super::common::get_start_game_data(room_control.server(), room_id, result, response); } - EngineMessage(em) => { + /*EngineMessage(em) => { if client.teams_in_game > 0 { let decoding = decode(&em[..]).unwrap(); let messages = by_msg(&decoding); @@ -503,10 +493,10 @@ } } } - } + }*/ RoundFinished => { - if let Some(team_names) = server.leave_game(client_id) { - let (client, room) = server.client_and_room(client_id, room_id); + if let Some(team_names) = room_control.leave_game() { + let (client, room) = room_control.get(); response.add( ClientFlags(remove_flags(&[Flags::InGame]), vec![client.nick.clone()]) .send_all() @@ -527,8 +517,14 @@ teams_in_game: 0, .. }) = room.game_info { - let result = server.end_game(room_id); - super::common::get_end_game_result(server, room_id, result, response); + if let Some(result) = room_control.end_game() { + super::common::get_end_game_result( + room_control.server(), + room_id, + result, + response, + ); + } } } } @@ -537,13 +533,13 @@ let mut echo = vec!["/rnd".to_string()]; echo.extend(v.into_iter()); let chat_msg = ChatMsg { - nick: server.client(client_id).nick.clone(), + nick: client.nick.clone(), msg: echo.join(" "), }; response.add(chat_msg.send_all().in_room(room_id)); response.add(result.send_all().in_room(room_id)); } - Delegate(nick) => match server.change_master(client_id, room_id, nick) { + Delegate(nick) => match room_control.change_master(nick) { Ok(ChangeMasterResult { old_master_id, new_master_id, @@ -552,7 +548,7 @@ response.add( ClientFlags( remove_flags(&[Flags::RoomMaster]), - vec![server.client(master_id).nick.clone()], + vec![room_control.server().client(master_id).nick.clone()], ) .send_all() .in_room(room_id), @@ -561,7 +557,7 @@ response.add( ClientFlags( add_flags(&[Flags::RoomMaster]), - vec![server.client(new_master_id).nick.clone()], + vec![room_control.server().client(new_master_id).nick.clone()], ) .send_all() .in_room(room_id),