# HG changeset patch # User alfadur # Date 1572046568 -10800 # Node ID 4cc9ec732392b1a0790b866da5597fbf98310403 # Parent 58ce582ae87d658da49c9a89a78c6059dfeacd23 refactor some of the room handler diff -r 58ce582ae87d -r 4cc9ec732392 rust/hedgewars-server/src/core/room.rs --- a/rust/hedgewars-server/src/core/room.rs Thu Oct 24 09:41:10 2019 -0400 +++ b/rust/hedgewars-server/src/core/room.rs Sat Oct 26 02:36:08 2019 +0300 @@ -142,9 +142,22 @@ &self.teams.last().unwrap().1 } - pub fn remove_team(&mut self, name: &str) { - if let Some(index) = self.teams.iter().position(|(_, t)| t.name == name) { + pub fn remove_team(&mut self, team_name: &str) { + if let Some(index) = self.teams.iter().position(|(_, t)| t.name == team_name) { self.teams.remove(index); + + if let Some(info) = &mut self.game_info { + info.left_teams.push(team_name.to_string()); + info.teams_in_game -= 1; + + if let Some(m) = &info.sync_msg { + info.msg_log.push(m.clone()); + info.sync_msg = None + } + let remove_msg = + crate::utils::to_engine_msg(iter::once(b'F').chain(team_name.bytes())); + info.msg_log.push(remove_msg.clone()); + } } } diff -r 58ce582ae87d -r 4cc9ec732392 rust/hedgewars-server/src/core/server.rs --- a/rust/hedgewars-server/src/core/server.rs Thu Oct 24 09:41:10 2019 -0400 +++ b/rust/hedgewars-server/src/core/server.rs Sat Oct 26 02:36:08 2019 +0300 @@ -28,6 +28,22 @@ Restricted, } +pub enum LeaveRoomResult { + RoomRemoved, + RoomRemains { + is_empty: bool, + was_master: bool, + was_in_game: bool, + new_master: Option, + removed_teams: Vec, + }, +} + +#[derive(Debug)] +pub enum LeaveRoomError { + NoRoom, +} + #[derive(Debug)] pub struct UninitializedError(); #[derive(Debug)] @@ -138,6 +154,20 @@ } #[inline] + pub fn client_and_room(&self, client_id: ClientId, room_id: RoomId) -> (&HwClient, &HwRoom) { + (&self.clients[client_id], &self.rooms[room_id]) + } + + #[inline] + pub fn client_and_room_mut( + &mut self, + client_id: ClientId, + room_id: RoomId, + ) -> (&mut HwClient, &mut HwRoom) { + (&mut self.clients[client_id], &mut self.rooms[room_id]) + } + + #[inline] pub fn is_admin(&self, client_id: ClientId) -> bool { self.clients .get(client_id) @@ -244,6 +274,81 @@ } } + 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) + } + } + #[inline] pub fn set_var(&mut self, client_id: ClientId, var: ServerVar) -> Result<(), AccessError> { if self.clients[client_id].is_admin() { diff -r 58ce582ae87d -r 4cc9ec732392 rust/hedgewars-server/src/handlers.rs --- a/rust/hedgewars-server/src/handlers.rs Thu Oct 24 09:41:10 2019 -0400 +++ b/rust/hedgewars-server/src/handlers.rs Sat Oct 26 02:36:08 2019 +0300 @@ -1,4 +1,3 @@ -use mio; use std::{ cmp::PartialEq, collections::HashMap, diff -r 58ce582ae87d -r 4cc9ec732392 rust/hedgewars-server/src/handlers/common.rs --- a/rust/hedgewars-server/src/handlers/common.rs Thu Oct 24 09:41:10 2019 -0400 +++ b/rust/hedgewars-server/src/handlers/common.rs Sat Oct 26 02:36:08 2019 +0300 @@ -2,7 +2,7 @@ core::{ client::HwClient, room::HwRoom, - server::{HwServer, JoinRoomError}, + server::{HwServer, JoinRoomError, LeaveRoomResult}, types::{ClientId, GameCfg, RoomId, TeamInfo, Vote, VoteType}, }, protocol::messages::{ @@ -98,103 +98,6 @@ response.add(rooms_msg.send_self()); } -pub fn remove_teams( - room: &mut HwRoom, - team_names: Vec, - is_in_game: bool, - response: &mut Response, -) { - if let Some(ref mut info) = room.game_info { - for team_name in &team_names { - info.left_teams.push(team_name.clone()); - - if is_in_game { - let msg = once(b'F').chain(team_name.bytes()); - response.add( - ForwardEngineMessage(vec![to_engine_msg(msg)]) - .send_all() - .in_room(room.id) - .but_self(), - ); - - info.teams_in_game -= 1; - - let remove_msg = to_engine_msg(once(b'F').chain(team_name.bytes())); - if let Some(m) = &info.sync_msg { - info.msg_log.push(m.clone()); - info.sync_msg = None - } - info.msg_log.push(remove_msg.clone()); - - response.add( - ForwardEngineMessage(vec![remove_msg]) - .send_all() - .in_room(room.id) - .but_self(), - ); - } - } - } - - for team_name in team_names { - room.remove_team(&team_name); - response.add(TeamRemove(team_name).send_all().in_room(room.id)); - } -} - -fn remove_client_from_room( - client: &mut HwClient, - room: &mut HwRoom, - response: &mut Response, - msg: &str, -) { - room.players_number -= 1; - if room.players_number > 0 || room.is_fixed() { - if client.is_ready() && room.ready_players_number > 0 { - room.ready_players_number -= 1; - } - - let team_names: Vec<_> = room - .client_teams(client.id) - .map(|t| t.name.clone()) - .collect(); - remove_teams(room, team_names, client.is_in_game(), response); - - if room.players_number > 0 { - response.add( - RoomLeft(client.nick.clone(), msg.to_string()) - .send_all() - .in_room(room.id) - .but_self(), - ); - } - - if client.is_master() && !room.is_fixed() { - client.set_is_master(false); - response.add( - ClientFlags( - remove_flags(&[Flags::RoomMaster]), - vec![client.nick.clone()], - ) - .send_all() - .in_room(room.id), - ); - room.master_id = None; - } - } - - client.room_id = None; - - let update_msg = if room.players_number == 0 && !room.is_fixed() { - RoomRemove(room.name.clone()) - } else { - RoomUpdated(room.name.clone(), room.info(Some(&client))) - }; - response.add(update_msg.send_all().with_protocol(room.protocol_number)); - - response.add(ClientFlags(remove_flags(&[Flags::InRoom]), vec![client.nick.clone()]).send_all()); -} - pub fn change_master( server: &mut HwServer, room_id: RoomId, @@ -291,33 +194,85 @@ } } -pub fn exit_room(server: &mut HwServer, client_id: ClientId, response: &mut Response, msg: &str) { - let client = &mut server.clients[client_id]; +pub fn get_remove_teams_data( + room_id: RoomId, + was_in_game: bool, + removed_teams: Vec, + response: &mut Response, +) { + if was_in_game { + for team_name in &removed_teams { + let msg = once(b'F').chain(team_name.bytes()); + response.add( + ForwardEngineMessage(vec![to_engine_msg(msg)]) + .send_all() + .in_room(room_id) + .but_self(), + ); - if let Some(room_id) = client.room_id { - let room = &mut server.rooms[room_id]; + let remove_msg = to_engine_msg(once(b'F').chain(team_name.bytes())); - remove_client_from_room(client, room, response, msg); + response.add( + ForwardEngineMessage(vec![remove_msg]) + .send_all() + .in_room(room_id) + .but_self(), + ); + } + } + + for team_name in removed_teams { + response.add(TeamRemove(team_name).send_all().in_room(room_id)); + } +} - if !room.is_fixed() { - if room.players_number == 0 { - server.rooms.remove(room_id); - } else if room.master_id == None { - let new_master_id = server.room_clients(room_id).next(); - if let Some(new_master_id) = new_master_id { - let new_master_nick = server.clients[new_master_id].nick.clone(); - let room = &mut server.rooms[room_id]; - room.master_id = Some(new_master_id); - server.clients[new_master_id].set_is_master(true); +pub fn get_room_leave_data( + server: &HwServer, + room: &HwRoom, + leave_message: &str, + result: LeaveRoomResult, + response: &mut Response, +) { + let client = server.client(response.client_id); + response.add(ClientFlags(remove_flags(&[Flags::InRoom]), vec![client.nick.clone()]).send_all()); + + match (result) { + LeaveRoomResult::RoomRemoved => { + response.add( + RoomRemove(room.name.clone()) + .send_all() + .with_protocol(room.protocol_number), + ); + } - if room.protocol_number < 42 { - room.name = new_master_nick.clone(); - } + LeaveRoomResult::RoomRemains { + is_empty, + was_master, + new_master, + was_in_game, + removed_teams, + } => { + if !is_empty { + response.add( + RoomLeft(client.nick.clone(), leave_message.to_string()) + .send_all() + .in_room(room.id) + .but_self(), + ); + } - room.set_join_restriction(false); - room.set_team_add_restriction(false); - room.set_unregistered_players_restriction(true); + if was_master { + response.add( + ClientFlags( + remove_flags(&[Flags::RoomMaster]), + vec![client.nick.clone()], + ) + .send_all() + .in_room(room.id), + ); + if let Some(new_master_id) = new_master { + let new_master_nick = server.client(new_master_id).nick.clone(); response.add( ClientFlags(add_flags(&[Flags::RoomMaster]), vec![new_master_nick]) .send_all() @@ -325,16 +280,32 @@ ); } } + + get_remove_teams_data(room.id, was_in_game, removed_teams, response); + + response.add( + RoomUpdated(room.name.clone(), room.info(Some(&client))) + .send_all() + .with_protocol(room.protocol_number), + ); } } } pub fn remove_client(server: &mut HwServer, response: &mut Response, msg: String) { let client_id = response.client_id(); - let client = &mut server.clients[client_id]; + let client = server.client(client_id); let nick = client.nick.clone(); - exit_room(server, client_id, response, &msg); + if let Some(room_id) = client.room_id { + match server.leave_room(client_id) { + Ok(result) => { + let room = server.room(room_id); + super::common::get_room_leave_data(server, room, &msg, result, response) + } + Err(_) => (), + } + } server.remove_client(client_id); @@ -423,7 +394,15 @@ if client.room_id == Some(room_id) { let id = client.id; response.add(Kicked.send(id)); - exit_room(server, id, response, "kicked"); + match server.leave_room(response.client_id) { + Ok(result) => { + let room = server.room(room_id); + super::common::get_room_leave_data( + server, room, "kicked", result, response, + ) + } + Err(_) => (), + } } } } diff -r 58ce582ae87d -r 4cc9ec732392 rust/hedgewars-server/src/handlers/inlobby.rs --- a/rust/hedgewars-server/src/handlers/inlobby.rs Thu Oct 24 09:41:10 2019 -0400 +++ b/rust/hedgewars-server/src/handlers/inlobby.rs Sat Oct 26 02:36:08 2019 +0300 @@ -1,5 +1,3 @@ -use mio; - use super::{common::rnd_reply, strings::*}; use crate::{ core::{ diff -r 58ce582ae87d -r 4cc9ec732392 rust/hedgewars-server/src/handlers/inroom.rs --- a/rust/hedgewars-server/src/handlers/inroom.rs Thu Oct 24 09:41:10 2019 -0400 +++ b/rust/hedgewars-server/src/handlers/inroom.rs Sat Oct 26 02:36:08 2019 +0300 @@ -1,11 +1,8 @@ -use mio; - -use super::common::rnd_reply; -use crate::utils::to_engine_msg; +use super::{common::rnd_reply, strings::*}; use crate::{ core::{ room::{HwRoom, RoomFlags, MAX_TEAMS_IN_ROOM}, - server::HwServer, + server::{HwServer, LeaveRoomResult}, types, types::{ClientId, GameCfg, RoomId, VoteType, Voting, MAX_HEDGEHOGS_PER_TEAM}, }, @@ -13,7 +10,7 @@ add_flags, remove_flags, server_chat, HwProtocolMessage, HwServerMessage::*, ProtocolFlags as Flags, }, - utils::is_name_illegal, + utils::{is_name_illegal, to_engine_msg}, }; use base64::{decode, encode}; use log::*; @@ -46,7 +43,7 @@ b"M#+LlRrUuDdZzAaSjJ,NpPwtgfhbc12345\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8A"; const NON_TIMED_MESSAGES: &[u8] = b"M#hb"; -#[cfg(canhazslicepatterns)] +/*#[cfg(canhazslicepatterns)] fn is_msg_valid(msg: &[u8], team_indices: &[u8]) -> bool { match msg { [size, typ, body..MAX] => { @@ -60,7 +57,7 @@ } _ => false, } -} +}*/ fn is_msg_valid(msg: &[u8], _team_indices: &[u8]) -> bool { if let Some(typ) = msg.get(1) { @@ -110,8 +107,7 @@ room_id: RoomId, message: HwProtocolMessage, ) { - let client = &mut server.clients[client_id]; - let room = &mut server.rooms[room_id]; + let (client, room) = server.client_and_room_mut(client_id, room_id); use crate::protocol::messages::HwProtocolMessage::*; match message { @@ -120,7 +116,14 @@ Some(s) => format!("part: {}", s), None => "part".to_string(), }; - super::common::exit_room(server, client_id, response, &msg); + + match server.leave_room(client_id) { + Ok(result) => { + let room = server.room(room_id); + super::common::get_room_leave_data(server, room, &msg, result, response) + } + Err(_) => (), + } } Chat(msg) => { response.add( @@ -133,10 +136,8 @@ ); } TeamChat(msg) => { - let room = &server.rooms[room_id]; if let Some(ref info) = room.game_info { if let Some(clan_color) = room.find_team_color(client_id) { - let client = &server.clients[client_id]; let engine_msg = to_engine_msg(format!("b{}]{}\x20\x20", client.nick, msg).bytes()); let team = room.clan_team_owners(clan_color).collect(); @@ -164,28 +165,24 @@ } MaxTeams(count) => { if !client.is_master() { - response.add(Warning("You're not the room master!".to_string()).send_self()); + response.warn(NOT_MASTER); } else if !(2..=MAX_TEAMS_IN_ROOM).contains(&count) { - response - .add(Warning("/maxteams: specify number from 2 to 8".to_string()).send_self()); + response.warn("/maxteams: specify number from 2 to 8"); } else { - server.rooms[room_id].max_teams = count; + room.max_teams = count; } } RoomName(new_name) => { if is_name_illegal(&new_name) { - response.add(Warning("Illegal room name! A room name must be between 1-40 characters long, must not have a trailing or leading space and must not have any of these characters: $()*+?[]^{|}".to_string()).send_self()); + response.warn("Illegal room name! A room name must be between 1-40 characters long, must not have a trailing or leading space and must not have any of these characters: $()*+?[]^{|}"); } else if server.has_room(&new_name) { - response.add( - Warning("A room with the same name already exists.".to_string()).send_self(), - ); + response.warn("A room with the same name already exists."); } else { - let room = &mut server.rooms[room_id]; + let (client, room) = server.client_and_room_mut(client_id, room_id); if room.is_fixed() || room.master_id != Some(client_id) { - response.add(Warning("Access denied.".to_string()).send_self()); + response.warn(ACCESS_DENIED); } else { let mut old_name = new_name.clone(); - let client = &server.clients[client_id]; swap(&mut room.name, &mut old_name); super::common::get_room_update(Some(old_name), room, Some(&client), response); } @@ -214,23 +211,15 @@ } AddTeam(mut info) => { if room.teams.len() >= room.max_teams as usize { - response.add(Warning("Too many teams!".to_string()).send_self()); + response.warn("Too many teams!"); } else if room.addable_hedgehogs() == 0 { - response.add(Warning("Too many hedgehogs!".to_string()).send_self()); + response.warn("Too many hedgehogs!"); } else if room.find_team(|t| t.name == info.name) != None { - response.add( - Warning("There's already a team with same name in the list.".to_string()) - .send_self(), - ); + response.warn("There's already a team with same name in the list."); } else if room.game_info.is_some() { - response.add( - Warning("Joining not possible: Round is in progress.".to_string()).send_self(), - ); + response.warn("Joining not possible: Round is in progress."); } else if room.is_team_add_restricted() { - response.add( - Warning("This room currently does not allow adding new teams.".to_string()) - .send_self(), - ); + response.warn("This room currently does not allow adding new teams."); } else { info.owner = client.nick.clone(); let team = room.add_team(client.id, *info, client.protocol_number < 42); @@ -254,8 +243,9 @@ .in_room(room_id), ); + let room = server.room(room_id); let room_master = if let Some(id) = room.master_id { - Some(&server.clients[id]) + Some(server.client(id)) } else { None }; @@ -263,17 +253,22 @@ } } RemoveTeam(name) => match room.find_team_owner(&name) { - None => response.add( - Warning("Error: The team you tried to remove does not exist.".to_string()) - .send_self(), - ), - Some((id, _)) if id != client_id => response - .add(Warning("You can't remove a team you don't own.".to_string()).send_self()), + None => response.warn("Error: The team you tried to remove does not exist."), + Some((id, _)) if id != client_id => { + response.warn("You can't remove a team you don't own.") + } Some((_, name)) => { + let name = name.to_string(); client.teams_in_game -= 1; client.clan = room.find_team_color(client.id); - let names = vec![name.to_string()]; - super::common::remove_teams(room, names, client.is_in_game(), response); + room.remove_team(&name); + let removed_teams = vec![name]; + super::common::get_remove_teams_data( + room_id, + client.is_in_game(), + removed_teams, + response, + ); match room.game_info { Some(ref info) if info.teams_in_game == 0 => { @@ -291,7 +286,7 @@ addable_hedgehogs + team.hedgehogs_number, ); if !client.is_master() { - response.add(Error("You're not the room master!".to_string()).send_self()); + response.error(NOT_MASTER); } else if !(1..=max_hedgehogs).contains(&number) { response .add(HedgehogsNumber(team.name.clone(), team.hedgehogs_number).send_self()); @@ -305,13 +300,13 @@ ); } } else { - response.add(Warning("No such team.".to_string()).send_self()); + response.warn(NO_TEAM); } } SetTeamColor(team_name, color) => { if let Some((owner, team)) = room.find_team_and_owner_mut(|t| t.name == team_name) { if !client.is_master() { - response.add(Error("You're not the room master!".to_string()).send_self()); + response.error(NOT_MASTER); } else { team.color = color; response.add( @@ -323,14 +318,14 @@ server.clients[owner].clan = Some(color); } } else { - response.add(Warning("No such team.".to_string()).send_self()); + response.warn(NO_TEAM); } } Cfg(cfg) => { if room.is_fixed() { - response.add(Warning("Access denied.".to_string()).send_self()); + response.warn(ACCESS_DENIED); } else if !client.is_master() { - response.add(Error("You're not the room master!".to_string()).send_self()); + response.error(NOT_MASTER); } else { let cfg = match cfg { GameCfg::Scheme(name, mut values) => { @@ -367,10 +362,7 @@ }), Err(e) => { warn!("Error while serializing the room configs: {}", e); - response.add( - Warning("Unable to serialize the room configs.".to_string()) - .send_self(), - ) + response.warn("Unable to serialize the room configs.") } } } @@ -589,17 +581,16 @@ let delegate_id = server.find_client(&nick).map(|c| (c.id, c.room_id)); let client = &server.clients[client_id]; if !(client.is_admin() || client.is_master()) { - response.add( - Warning("You're not the room master or a server admin!".to_string()) - .send_self(), - ) + response.warn("You're not the room master or a server admin!") } else { match delegate_id { - None => response.add(Warning("Player is not online.".to_string()).send_self()), - Some((id, _)) if id == client_id => response - .add(Warning("You're already the room master.".to_string()).send_self()), - Some((_, id)) if id != Some(room_id) => response - .add(Warning("The player is not in your room.".to_string()).send_self()), + None => response.warn("Player is not online."), + Some((id, _)) if id == client_id => { + response.warn("You're already the room master.") + } + Some((_, id)) if id != Some(room_id) => { + response.warn("The player is not in your room.") + } Some((id, _)) => { super::common::change_master(server, room_id, id, response); } diff -r 58ce582ae87d -r 4cc9ec732392 rust/hedgewars-server/src/handlers/strings.rs --- a/rust/hedgewars-server/src/handlers/strings.rs Thu Oct 24 09:41:10 2019 -0400 +++ b/rust/hedgewars-server/src/handlers/strings.rs Sat Oct 26 02:36:08 2019 +0300 @@ -2,7 +2,9 @@ pub const AUTHENTICATION_FAILED: &str = "Authentication failed."; pub const ILLEGAL_ROOM_NAME: &str = "Illegal room name! A room name must be between 1-40 characters long, must not have a trailing or leading space and must not have any of these characters: $()*+?[]^{|}"; pub const NO_ROOM: &str = "No such room."; +pub const NO_TEAM: &str = "No such team."; pub const NO_USER: &str = "No such user."; +pub const NOT_MASTER: &str = "You're not the room master!"; pub const REPLAY_LOAD_FAILED: &str = "Could't load the replay"; pub const REPLAY_NOT_SUPPORTED: &str = "This server does not support replays!"; pub const REGISTRATION_REQUIRED: &str = "This server only allows registered users to join.";