--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/rust/hedgewars-server/src/core.rs Tue May 28 19:04:18 2019 +0300
@@ -0,0 +1,5 @@
+pub mod types;
+pub mod indexslab;
+pub mod client;
+pub mod room;
+pub mod server;
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/rust/hedgewars-server/src/core/client.rs Tue May 28 19:04:18 2019 +0300
@@ -0,0 +1,109 @@
+use super::types::ClientId;
+use bitflags::*;
+
+bitflags! {
+ pub struct ClientFlags: u16 {
+ const IS_ADMIN = 0b0000_0001;
+ const IS_MASTER = 0b0000_0010;
+ const IS_READY = 0b0000_0100;
+ const IS_IN_GAME = 0b0000_1000;
+ const IS_JOINED_MID_GAME = 0b0001_0000;
+ const IS_CHECKER = 0b0010_0000;
+ const IS_CONTRIBUTOR = 0b0100_0000;
+ const HAS_SUPER_POWER = 0b1000_0000;
+ const IS_REGISTERED = 0b0001_0000_0000;
+
+ const NONE = 0b0000_0000;
+ const DEFAULT = Self::NONE.bits;
+ }
+}
+
+pub struct HWClient {
+ pub id: ClientId,
+ pub room_id: Option<usize>,
+ pub nick: String,
+ pub protocol_number: u16,
+ pub flags: ClientFlags,
+ pub teams_in_game: u8,
+ pub team_indices: Vec<u8>,
+ pub clan: Option<u8>,
+}
+
+impl HWClient {
+ pub fn new(id: ClientId, protocol_number: u16, nick: String) -> HWClient {
+ HWClient {
+ id,
+ nick,
+ protocol_number,
+ room_id: None,
+ flags: ClientFlags::DEFAULT,
+ teams_in_game: 0,
+ team_indices: Vec::new(),
+ clan: None,
+ }
+ }
+
+ fn contains(&self, mask: ClientFlags) -> bool {
+ self.flags.contains(mask)
+ }
+
+ fn set(&mut self, mask: ClientFlags, value: bool) {
+ self.flags.set(mask, value);
+ }
+
+ pub fn is_admin(&self) -> bool {
+ self.contains(ClientFlags::IS_ADMIN)
+ }
+ pub fn is_master(&self) -> bool {
+ self.contains(ClientFlags::IS_MASTER)
+ }
+ pub fn is_ready(&self) -> bool {
+ self.contains(ClientFlags::IS_READY)
+ }
+ pub fn is_in_game(&self) -> bool {
+ self.contains(ClientFlags::IS_IN_GAME)
+ }
+ pub fn is_joined_mid_game(&self) -> bool {
+ self.contains(ClientFlags::IS_JOINED_MID_GAME)
+ }
+ pub fn is_checker(&self) -> bool {
+ self.contains(ClientFlags::IS_CHECKER)
+ }
+ pub fn is_contributor(&self) -> bool {
+ self.contains(ClientFlags::IS_CONTRIBUTOR)
+ }
+ pub fn has_super_power(&self) -> bool {
+ self.contains(ClientFlags::HAS_SUPER_POWER)
+ }
+ pub fn is_registered(&self) -> bool {
+ self.contains(ClientFlags::IS_REGISTERED)
+ }
+
+ pub fn set_is_admin(&mut self, value: bool) {
+ self.set(ClientFlags::IS_ADMIN, value)
+ }
+ pub fn set_is_master(&mut self, value: bool) {
+ self.set(ClientFlags::IS_MASTER, value)
+ }
+ pub fn set_is_ready(&mut self, value: bool) {
+ self.set(ClientFlags::IS_READY, value)
+ }
+ pub fn set_is_in_game(&mut self, value: bool) {
+ self.set(ClientFlags::IS_IN_GAME, value)
+ }
+ pub fn set_is_joined_mid_game(&mut self, value: bool) {
+ self.set(ClientFlags::IS_JOINED_MID_GAME, value)
+ }
+ pub fn set_is_checker(&mut self, value: bool) {
+ self.set(ClientFlags::IS_CHECKER, value)
+ }
+ pub fn set_is_contributor(&mut self, value: bool) {
+ self.set(ClientFlags::IS_CONTRIBUTOR, value)
+ }
+ pub fn set_has_super_power(&mut self, value: bool) {
+ self.set(ClientFlags::HAS_SUPER_POWER, value)
+ }
+ pub fn set_is_registered(&mut self, value: bool) {
+ self.set(ClientFlags::IS_REGISTERED, value)
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/rust/hedgewars-server/src/core/indexslab.rs Tue May 28 19:04:18 2019 +0300
@@ -0,0 +1,71 @@
+use std::{
+ iter,
+ mem::replace,
+ ops::{Index, IndexMut},
+};
+
+pub struct IndexSlab<T> {
+ data: Vec<Option<T>>,
+}
+
+impl<T> IndexSlab<T> {
+ pub fn new() -> Self {
+ Self { data: Vec::new() }
+ }
+
+ pub fn with_capacity(capacity: usize) -> Self {
+ Self {
+ data: Vec::with_capacity(capacity),
+ }
+ }
+
+ pub fn insert(&mut self, index: usize, value: T) {
+ if index >= self.data.len() {
+ self.data.reserve(index - self.data.len() + 1);
+ self.data.extend((self.data.len()..index).map(|_| None));
+ self.data.push(Some(value))
+ } else {
+ self.data[index] = Some(value);
+ }
+ }
+
+ pub fn contains(&self, index: usize) -> bool {
+ self.data.get(index).and_then(|x| x.as_ref()).is_some()
+ }
+
+ pub fn remove(&mut self, index: usize) -> Option<T> {
+ if let Some(x) = self.data.get_mut(index) {
+ replace(x, None)
+ } else {
+ None
+ }
+ }
+
+ pub fn iter(&self) -> impl Iterator<Item = (usize, &T)> {
+ self.data
+ .iter()
+ .enumerate()
+ .filter_map(|(index, opt)| opt.as_ref().and_then(|x| Some((index, x))))
+ }
+
+ pub fn iter_mut(&mut self) -> impl Iterator<Item = (usize, &mut T)> {
+ self.data
+ .iter_mut()
+ .enumerate()
+ .filter_map(|(index, opt)| opt.as_mut().and_then(|x| Some((index, x))))
+ }
+}
+
+impl<T> Index<usize> for IndexSlab<T> {
+ type Output = T;
+
+ fn index(&self, index: usize) -> &Self::Output {
+ self.data[index].as_ref().unwrap()
+ }
+}
+
+impl<T> IndexMut<usize> for IndexSlab<T> {
+ fn index_mut(&mut self, index: usize) -> &mut Self::Output {
+ self.data[index].as_mut().unwrap()
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/rust/hedgewars-server/src/core/room.rs Tue May 28 19:04:18 2019 +0300
@@ -0,0 +1,349 @@
+use super::{
+ client::HWClient,
+ types::{
+ ClientId, GameCfg, GameCfg::*, RoomConfig, RoomId, TeamInfo, Voting, MAX_HEDGEHOGS_PER_TEAM,
+ },
+};
+use bitflags::*;
+use serde::{Deserialize, Serialize};
+use serde_derive::{Deserialize, Serialize};
+use serde_yaml;
+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;
+
+fn client_teams_impl(
+ teams: &[(ClientId, TeamInfo)],
+ client_id: ClientId,
+) -> impl Iterator<Item = &TeamInfo> + Clone {
+ teams
+ .iter()
+ .filter(move |(id, _)| *id == client_id)
+ .map(|(_, t)| t)
+}
+
+pub struct GameInfo {
+ pub teams_in_game: u8,
+ pub teams_at_start: Vec<(ClientId, TeamInfo)>,
+ pub left_teams: Vec<String>,
+ pub msg_log: Vec<String>,
+ pub sync_msg: Option<String>,
+ pub is_paused: bool,
+ config: RoomConfig,
+}
+
+impl GameInfo {
+ fn new(teams: Vec<(ClientId, TeamInfo)>, config: RoomConfig) -> GameInfo {
+ GameInfo {
+ left_teams: Vec::new(),
+ msg_log: Vec::new(),
+ sync_msg: None,
+ is_paused: false,
+ teams_in_game: teams.len() as u8,
+ teams_at_start: teams,
+ config,
+ }
+ }
+
+ pub fn client_teams(&self, client_id: ClientId) -> impl Iterator<Item = &TeamInfo> + Clone {
+ client_teams_impl(&self.teams_at_start, client_id)
+ }
+}
+
+#[derive(Serialize, Deserialize)]
+pub struct RoomSave {
+ pub location: String,
+ config: RoomConfig,
+}
+
+bitflags! {
+ pub struct RoomFlags: u8 {
+ const FIXED = 0b0000_0001;
+ const RESTRICTED_JOIN = 0b0000_0010;
+ const RESTRICTED_TEAM_ADD = 0b0000_0100;
+ const RESTRICTED_UNREGISTERED_PLAYERS = 0b0000_1000;
+ }
+}
+
+pub struct HWRoom {
+ pub id: RoomId,
+ pub master_id: Option<ClientId>,
+ pub name: String,
+ pub password: Option<String>,
+ pub greeting: String,
+ pub protocol_number: u16,
+ pub flags: RoomFlags,
+
+ pub players_number: u8,
+ pub default_hedgehog_number: u8,
+ pub max_teams: u8,
+ pub ready_players_number: u8,
+ pub teams: Vec<(ClientId, TeamInfo)>,
+ config: RoomConfig,
+ pub voting: Option<Voting>,
+ pub saves: HashMap<String, RoomSave>,
+ pub game_info: Option<GameInfo>,
+}
+
+impl HWRoom {
+ pub fn new(id: RoomId) -> HWRoom {
+ HWRoom {
+ id,
+ master_id: None,
+ name: String::new(),
+ password: None,
+ greeting: "".to_string(),
+ flags: RoomFlags::empty(),
+ protocol_number: 0,
+ players_number: 0,
+ default_hedgehog_number: 4,
+ max_teams: MAX_TEAMS_IN_ROOM,
+ ready_players_number: 0,
+ teams: Vec::new(),
+ config: RoomConfig::new(),
+ voting: None,
+ saves: HashMap::new(),
+ game_info: None,
+ }
+ }
+
+ pub fn hedgehogs_number(&self) -> u8 {
+ self.teams.iter().map(|(_, t)| t.hedgehogs_number).sum()
+ }
+
+ pub fn addable_hedgehogs(&self) -> u8 {
+ MAX_HEDGEHOGS_IN_ROOM - self.hedgehogs_number()
+ }
+
+ pub fn add_team(
+ &mut self,
+ owner_id: ClientId,
+ mut team: TeamInfo,
+ preserve_color: bool,
+ ) -> &TeamInfo {
+ if !preserve_color {
+ team.color = iter::repeat(())
+ .enumerate()
+ .map(|(i, _)| i as u8)
+ .take(u8::max_value() as usize + 1)
+ .find(|i| self.teams.iter().all(|(_, t)| t.color != *i))
+ .unwrap_or(0u8)
+ };
+ team.hedgehogs_number = if self.teams.is_empty() {
+ self.default_hedgehog_number
+ } else {
+ self.teams[0]
+ .1
+ .hedgehogs_number
+ .min(self.addable_hedgehogs())
+ };
+ self.teams.push((owner_id, team));
+ &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) {
+ self.teams.remove(index);
+ }
+ }
+
+ pub fn set_hedgehogs_number(&mut self, n: u8) -> Vec<String> {
+ let mut names = Vec::new();
+ let teams = match self.game_info {
+ Some(ref mut info) => &mut info.teams_at_start,
+ None => &mut self.teams,
+ };
+
+ if teams.len() as u8 * n <= MAX_HEDGEHOGS_IN_ROOM {
+ for (_, team) in teams.iter_mut() {
+ team.hedgehogs_number = n;
+ names.push(team.name.clone())
+ }
+ self.default_hedgehog_number = n;
+ }
+ names
+ }
+
+ pub fn find_team_and_owner_mut<F>(&mut self, f: F) -> Option<(ClientId, &mut TeamInfo)>
+ where
+ F: Fn(&TeamInfo) -> bool,
+ {
+ self.teams
+ .iter_mut()
+ .find(|(_, t)| f(t))
+ .map(|(id, t)| (*id, t))
+ }
+
+ pub fn find_team<F>(&self, f: F) -> Option<&TeamInfo>
+ where
+ F: Fn(&TeamInfo) -> bool,
+ {
+ self.teams
+ .iter()
+ .find_map(|(_, t)| Some(t).filter(|t| f(&t)))
+ }
+
+ pub fn client_teams(&self, client_id: ClientId) -> impl Iterator<Item = &TeamInfo> {
+ client_teams_impl(&self.teams, client_id)
+ }
+
+ pub fn client_team_indices(&self, client_id: ClientId) -> Vec<u8> {
+ self.teams
+ .iter()
+ .enumerate()
+ .filter(move |(_, (id, _))| *id == client_id)
+ .map(|(i, _)| i as u8)
+ .collect()
+ }
+
+ pub fn clan_team_owners(&self, color: u8) -> impl Iterator<Item = ClientId> + '_ {
+ self.teams
+ .iter()
+ .filter(move |(_, t)| t.color == color)
+ .map(|(id, _)| *id)
+ }
+
+ pub fn find_team_owner(&self, team_name: &str) -> Option<(ClientId, &str)> {
+ self.teams
+ .iter()
+ .find(|(_, t)| t.name == team_name)
+ .map(|(id, t)| (*id, &t.name[..]))
+ }
+
+ pub fn find_team_color(&self, owner_id: ClientId) -> Option<u8> {
+ self.client_teams(owner_id).nth(0).map(|t| t.color)
+ }
+
+ pub fn has_multiple_clans(&self) -> bool {
+ self.teams.iter().min_by_key(|(_, t)| t.color)
+ != self.teams.iter().max_by_key(|(_, t)| t.color)
+ }
+
+ pub fn set_config(&mut self, cfg: GameCfg) {
+ self.config.set_config(cfg);
+ }
+
+ pub fn start_round(&mut self) {
+ if self.game_info.is_none() {
+ self.game_info = Some(GameInfo::new(self.teams.clone(), self.config.clone()));
+ }
+ }
+
+ pub fn is_fixed(&self) -> bool {
+ self.flags.contains(RoomFlags::FIXED)
+ }
+ pub fn is_join_restricted(&self) -> bool {
+ self.flags.contains(RoomFlags::RESTRICTED_JOIN)
+ }
+ pub fn is_team_add_restricted(&self) -> bool {
+ self.flags.contains(RoomFlags::RESTRICTED_TEAM_ADD)
+ }
+ pub fn are_unregistered_players_restricted(&self) -> bool {
+ self.flags
+ .contains(RoomFlags::RESTRICTED_UNREGISTERED_PLAYERS)
+ }
+
+ pub fn set_is_fixed(&mut self, value: bool) {
+ self.flags.set(RoomFlags::FIXED, value)
+ }
+ pub fn set_join_restriction(&mut self, value: bool) {
+ self.flags.set(RoomFlags::RESTRICTED_JOIN, value)
+ }
+ pub fn set_team_add_restriction(&mut self, value: bool) {
+ self.flags.set(RoomFlags::RESTRICTED_TEAM_ADD, value)
+ }
+ pub fn set_unregistered_players_restriction(&mut self, value: bool) {
+ self.flags
+ .set(RoomFlags::RESTRICTED_UNREGISTERED_PLAYERS, value)
+ }
+
+ fn flags_string(&self) -> String {
+ let mut result = "-".to_string();
+ if self.game_info.is_some() {
+ result += "g"
+ }
+ if self.password.is_some() {
+ result += "p"
+ }
+ if self.is_join_restricted() {
+ result += "j"
+ }
+ if self.are_unregistered_players_restricted() {
+ result += "r"
+ }
+ result
+ }
+
+ pub fn info(&self, master: Option<&HWClient>) -> Vec<String> {
+ let c = &self.config;
+ vec![
+ self.flags_string(),
+ self.name.clone(),
+ self.players_number.to_string(),
+ self.teams.len().to_string(),
+ master.map_or("[]", |c| &c.nick).to_string(),
+ c.map_type.to_string(),
+ c.script.to_string(),
+ c.scheme.name.to_string(),
+ c.ammo.name.to_string(),
+ ]
+ }
+
+ pub fn active_config(&self) -> &RoomConfig {
+ match self.game_info {
+ Some(ref info) => &info.config,
+ None => &self.config,
+ }
+ }
+
+ pub fn map_config(&self) -> Vec<String> {
+ match self.game_info {
+ Some(ref info) => info.config.to_map_config(),
+ None => self.config.to_map_config(),
+ }
+ }
+
+ pub fn game_config(&self) -> Vec<GameCfg> {
+ match self.game_info {
+ Some(ref info) => info.config.to_game_config(),
+ None => self.config.to_game_config(),
+ }
+ }
+
+ pub fn save_config(&mut self, name: String, location: String) {
+ self.saves.insert(
+ name,
+ RoomSave {
+ location,
+ config: self.config.clone(),
+ },
+ );
+ }
+
+ pub fn load_config(&mut self, name: &str) -> Option<&str> {
+ if let Some(save) = self.saves.get(name) {
+ self.config = save.config.clone();
+ Some(&save.location[..])
+ } else {
+ None
+ }
+ }
+
+ pub fn delete_config(&mut self, name: &str) -> bool {
+ self.saves.remove(name).is_some()
+ }
+
+ pub fn get_saves(&self) -> Result<String, serde_yaml::Error> {
+ serde_yaml::to_string(&(&self.greeting, &self.saves))
+ }
+
+ pub fn set_saves(&mut self, text: &str) -> Result<(), serde_yaml::Error> {
+ serde_yaml::from_str::<(String, HashMap<String, RoomSave>)>(text).map(
+ |(greeting, saves)| {
+ self.greeting = greeting;
+ self.saves = saves;
+ },
+ )
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/rust/hedgewars-server/src/core/server.rs Tue May 28 19:04:18 2019 +0300
@@ -0,0 +1,285 @@
+use super::{
+ client::HWClient,
+ types::{ClientId, RoomId},
+ indexslab::IndexSlab,
+ room::HWRoom,
+};
+use crate::{
+ utils,
+ protocol::messages::HWProtocolMessage::Greeting
+};
+
+use bitflags::*;
+use log::*;
+use slab;
+use std::{borrow::BorrowMut, iter, num::NonZeroU16};
+
+type Slab<T> = slab::Slab<T>;
+
+pub struct HWAnteClient {
+ pub nick: Option<String>,
+ pub protocol_number: Option<NonZeroU16>,
+ pub server_salt: String,
+ pub is_checker: bool,
+}
+
+pub struct HWAnteroom {
+ pub clients: IndexSlab<HWAnteClient>,
+}
+
+impl HWAnteroom {
+ pub fn new(clients_limit: usize) -> Self {
+ let clients = IndexSlab::with_capacity(clients_limit);
+ HWAnteroom { clients }
+ }
+
+ pub fn add_client(&mut self, client_id: ClientId, salt: String) {
+ let client = HWAnteClient {
+ nick: None,
+ protocol_number: None,
+ server_salt: salt,
+ is_checker: false,
+ };
+ self.clients.insert(client_id, client);
+ }
+
+ pub fn remove_client(&mut self, client_id: ClientId) -> Option<HWAnteClient> {
+ let mut client = self.clients.remove(client_id);
+ client
+ }
+}
+
+pub struct ServerGreetings {
+ pub for_latest_protocol: String,
+ pub for_old_protocols: String,
+}
+
+impl ServerGreetings {
+ fn new() -> Self {
+ Self {
+ for_latest_protocol: "\u{1f994} is watching".to_string(),
+ for_old_protocols: "\u{1f994} is watching".to_string(),
+ }
+ }
+}
+
+bitflags! {
+ pub struct ServerFlags: u8 {
+ const REGISTERED_ONLY = 0b0000_1000;
+ }
+}
+
+pub struct HWServer {
+ pub clients: IndexSlab<HWClient>,
+ pub rooms: Slab<HWRoom>,
+ pub anteroom: HWAnteroom,
+ pub latest_protocol: u16,
+ pub flags: ServerFlags,
+ pub greetings: ServerGreetings,
+}
+
+impl HWServer {
+ pub fn new(clients_limit: usize, rooms_limit: usize) -> Self {
+ let rooms = Slab::with_capacity(rooms_limit);
+ let clients = IndexSlab::with_capacity(clients_limit);
+ Self {
+ clients,
+ rooms,
+ anteroom: HWAnteroom::new(clients_limit),
+ greetings: ServerGreetings::new(),
+ latest_protocol: 58,
+ flags: ServerFlags::empty(),
+ }
+ }
+
+ pub fn add_client(&mut self, client_id: ClientId, data: HWAnteClient) {
+ if let (Some(protocol), Some(nick)) = (data.protocol_number, data.nick) {
+ let mut client = HWClient::new(client_id, protocol.get(), nick);
+ client.set_is_checker(data.is_checker);
+ self.clients.insert(client_id, client);
+ }
+ }
+
+ pub fn remove_client(&mut self, client_id: ClientId) {
+ self.clients.remove(client_id);
+ }
+
+ pub fn get_greetings(&self, client_id: ClientId) -> &str {
+ if self.clients[client_id].protocol_number < self.latest_protocol {
+ &self.greetings.for_old_protocols
+ } else {
+ &self.greetings.for_latest_protocol
+ }
+ }
+
+ #[inline]
+ pub fn create_room(
+ &mut self,
+ creator_id: ClientId,
+ name: String,
+ password: Option<String>,
+ ) -> RoomId {
+ create_room(
+ &mut self.clients[creator_id],
+ &mut self.rooms,
+ name,
+ password,
+ )
+ }
+
+ #[inline]
+ pub fn move_to_room(&mut self, client_id: ClientId, room_id: RoomId) {
+ move_to_room(&mut self.clients[client_id], &mut self.rooms[room_id])
+ }
+
+ pub fn has_room(&self, name: &str) -> bool {
+ self.find_room(name).is_some()
+ }
+
+ pub fn find_room(&self, name: &str) -> Option<&HWRoom> {
+ self.rooms
+ .iter()
+ .find_map(|(_, r)| Some(r).filter(|r| r.name == name))
+ }
+
+ pub fn find_room_mut(&mut self, name: &str) -> Option<&mut HWRoom> {
+ self.rooms
+ .iter_mut()
+ .find_map(|(_, r)| Some(r).filter(|r| r.name == name))
+ }
+
+ pub fn find_client(&self, nick: &str) -> Option<&HWClient> {
+ self.clients
+ .iter()
+ .find_map(|(_, c)| Some(c).filter(|c| c.nick == nick))
+ }
+
+ pub fn find_client_mut(&mut self, nick: &str) -> Option<&mut HWClient> {
+ self.clients
+ .iter_mut()
+ .find_map(|(_, c)| Some(c).filter(|c| c.nick == nick))
+ }
+
+ pub fn all_clients(&self) -> impl Iterator<Item = ClientId> + '_ {
+ self.clients.iter().map(|(id, _)| id)
+ }
+
+ pub fn filter_clients<'a, F>(&'a self, f: F) -> impl Iterator<Item = ClientId> + 'a
+ where
+ F: Fn(&(usize, &HWClient)) -> bool + 'a,
+ {
+ self.clients.iter().filter(f).map(|(_, c)| c.id)
+ }
+
+ pub fn filter_rooms<'a, F>(&'a self, f: F) -> impl Iterator<Item = RoomId> + 'a
+ where
+ F: Fn(&(usize, &HWRoom)) -> bool + 'a,
+ {
+ self.rooms.iter().filter(f).map(|(_, c)| c.id)
+ }
+
+ pub fn collect_clients<F>(&self, f: F) -> Vec<ClientId>
+ where
+ F: Fn(&(usize, &HWClient)) -> bool,
+ {
+ self.filter_clients(f).collect()
+ }
+
+ pub fn collect_nicks<F>(&self, f: F) -> Vec<String>
+ where
+ F: Fn(&(usize, &HWClient)) -> bool,
+ {
+ self.clients
+ .iter()
+ .filter(f)
+ .map(|(_, c)| c.nick.clone())
+ .collect()
+ }
+
+ pub fn lobby_clients(&self) -> impl Iterator<Item = ClientId> + '_ {
+ self.filter_clients(|(_, c)| c.room_id == None)
+ }
+
+ pub fn room_clients(&self, room_id: RoomId) -> impl Iterator<Item = ClientId> + '_ {
+ self.filter_clients(move |(_, c)| c.room_id == Some(room_id))
+ }
+
+ pub fn protocol_clients(&self, protocol: u16) -> impl Iterator<Item = ClientId> + '_ {
+ self.filter_clients(move |(_, c)| c.protocol_number == protocol)
+ }
+
+ pub fn protocol_rooms(&self, protocol: u16) -> impl Iterator<Item = RoomId> + '_ {
+ self.filter_rooms(move |(_, r)| r.protocol_number == protocol)
+ }
+
+ pub fn other_clients_in_room(&self, self_id: ClientId) -> Vec<ClientId> {
+ let room_id = self.clients[self_id].room_id;
+ self.collect_clients(|(id, c)| *id != self_id && c.room_id == room_id)
+ }
+
+ pub fn is_registered_only(&self) -> bool {
+ self.flags.contains(ServerFlags::REGISTERED_ONLY)
+ }
+
+ pub fn set_is_registered_only(&mut self, value: bool) {
+ self.flags.set(ServerFlags::REGISTERED_ONLY, value)
+ }
+}
+
+fn allocate_room(rooms: &mut Slab<HWRoom>) -> &mut HWRoom {
+ let entry = rooms.vacant_entry();
+ let room = HWRoom::new(entry.key());
+ entry.insert(room)
+}
+
+fn create_room(
+ client: &mut HWClient,
+ rooms: &mut Slab<HWRoom>,
+ name: String,
+ password: Option<String>,
+) -> RoomId {
+ let room = allocate_room(rooms);
+
+ room.master_id = Some(client.id);
+ room.name = name;
+ room.password = password;
+ room.protocol_number = client.protocol_number;
+
+ room.players_number = 1;
+ room.ready_players_number = 1;
+
+ client.room_id = Some(room.id);
+ client.set_is_master(true);
+ client.set_is_ready(true);
+ client.set_is_joined_mid_game(false);
+
+ room.id
+}
+
+fn move_to_room(client: &mut HWClient, room: &mut HWRoom) {
+ debug_assert!(client.room_id != Some(room.id));
+
+ room.players_number += 1;
+
+ client.room_id = Some(room.id);
+ client.set_is_joined_mid_game(room.game_info.is_some());
+ client.set_is_in_game(room.game_info.is_some());
+
+ if let Some(ref mut info) = room.game_info {
+ let teams = info.client_teams(client.id);
+ client.teams_in_game = teams.clone().count() as u8;
+ client.clan = teams.clone().next().map(|t| t.color);
+ let team_names: Vec<_> = teams.map(|t| t.name.clone()).collect();
+
+ if !team_names.is_empty() {
+ info.left_teams.retain(|name| !team_names.contains(&name));
+ info.teams_in_game += team_names.len() as u8;
+ room.teams = info
+ .teams_at_start
+ .iter()
+ .filter(|(_, t)| !team_names.contains(&t.name))
+ .cloned()
+ .collect();
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/rust/hedgewars-server/src/core/types.rs Tue May 28 19:04:18 2019 +0300
@@ -0,0 +1,193 @@
+use serde_derive::{Deserialize, Serialize};
+
+pub type ClientId = usize;
+pub type RoomId = usize;
+
+pub const MAX_HEDGEHOGS_PER_TEAM: u8 = 8;
+
+#[derive(PartialEq, Eq, Clone, Debug)]
+pub enum ServerVar {
+ MOTDNew(String),
+ MOTDOld(String),
+ LatestProto(u16),
+}
+
+#[derive(PartialEq, Eq, Clone, Debug)]
+pub enum GameCfg {
+ FeatureSize(u32),
+ MapType(String),
+ MapGenerator(u32),
+ MazeSize(u32),
+ Seed(String),
+ Template(u32),
+
+ Ammo(String, Option<String>),
+ Scheme(String, Vec<String>),
+ Script(String),
+ Theme(String),
+ DrawnMap(String),
+}
+
+#[derive(PartialEq, Eq, Clone, Debug)]
+pub struct TeamInfo {
+ pub owner: String,
+ pub name: String,
+ pub color: u8,
+ pub grave: String,
+ pub fort: String,
+ pub voice_pack: String,
+ pub flag: String,
+ pub difficulty: u8,
+ pub hedgehogs_number: u8,
+ pub hedgehogs: [HedgehogInfo; MAX_HEDGEHOGS_PER_TEAM as usize],
+}
+
+#[derive(PartialEq, Eq, Clone, Debug)]
+pub struct HedgehogInfo {
+ pub name: String,
+ pub hat: String,
+}
+
+#[derive(Clone, Serialize, Deserialize)]
+pub struct Ammo {
+ pub name: String,
+ pub settings: Option<String>,
+}
+
+#[derive(Clone, Serialize, Deserialize)]
+pub struct Scheme {
+ pub name: String,
+ pub settings: Vec<String>,
+}
+
+#[derive(Clone, Serialize, Deserialize)]
+pub struct RoomConfig {
+ pub feature_size: u32,
+ pub map_type: String,
+ pub map_generator: u32,
+ pub maze_size: u32,
+ pub seed: String,
+ pub template: u32,
+
+ pub ammo: Ammo,
+ pub scheme: Scheme,
+ pub script: String,
+ pub theme: String,
+ pub drawn_map: Option<String>,
+}
+
+impl RoomConfig {
+ pub fn new() -> RoomConfig {
+ RoomConfig {
+ feature_size: 12,
+ map_type: "+rnd+".to_string(),
+ map_generator: 0,
+ maze_size: 0,
+ seed: "seed".to_string(),
+ template: 0,
+
+ ammo: Ammo {
+ name: "Default".to_string(),
+ settings: None,
+ },
+ scheme: Scheme {
+ name: "Default".to_string(),
+ settings: Vec::new(),
+ },
+ script: "Normal".to_string(),
+ theme: "\u{1f994}".to_string(),
+ drawn_map: None,
+ }
+ }
+
+ pub fn set_config(&mut self, cfg: GameCfg) {
+ match cfg {
+ GameCfg::FeatureSize(s) => self.feature_size = s,
+ GameCfg::MapType(t) => self.map_type = t,
+ GameCfg::MapGenerator(g) => self.map_generator = g,
+ GameCfg::MazeSize(s) => self.maze_size = s,
+ GameCfg::Seed(s) => self.seed = s,
+ GameCfg::Template(t) => self.template = t,
+
+ GameCfg::Ammo(n, s) => {
+ self.ammo = Ammo {
+ name: n,
+ settings: s,
+ }
+ }
+ GameCfg::Scheme(n, s) => {
+ self.scheme = Scheme {
+ name: n,
+ settings: s,
+ }
+ }
+ GameCfg::Script(s) => self.script = s,
+ GameCfg::Theme(t) => self.theme = t,
+ GameCfg::DrawnMap(m) => self.drawn_map = Some(m),
+ };
+ }
+
+ pub fn to_map_config(&self) -> Vec<String> {
+ vec![
+ self.feature_size.to_string(),
+ self.map_type.to_string(),
+ self.map_generator.to_string(),
+ self.maze_size.to_string(),
+ self.seed.to_string(),
+ self.template.to_string(),
+ ]
+ }
+
+ pub fn to_game_config(&self) -> Vec<GameCfg> {
+ use GameCfg::*;
+ let mut v = vec![
+ Ammo(self.ammo.name.to_string(), self.ammo.settings.clone()),
+ Scheme(self.scheme.name.to_string(), self.scheme.settings.clone()),
+ Script(self.script.to_string()),
+ Theme(self.theme.to_string()),
+ ];
+ if let Some(ref m) = self.drawn_map {
+ v.push(DrawnMap(m.to_string()))
+ }
+ v
+ }
+}
+
+pub struct Replay {
+ pub config: RoomConfig,
+ pub teams: Vec<TeamInfo>,
+ pub message_log: Vec<String>,
+}
+
+#[derive(PartialEq, Eq, Clone, Debug)]
+pub enum VoteType {
+ Kick(String),
+ Map(Option<String>),
+ Pause,
+ NewSeed,
+ HedgehogsPerTeam(u8),
+}
+
+pub struct Vote {
+ pub is_pro: bool,
+ pub is_forced: bool,
+}
+
+#[derive(Clone, Debug)]
+pub struct Voting {
+ pub ttl: u32,
+ pub voters: Vec<ClientId>,
+ pub votes: Vec<(ClientId, bool)>,
+ pub kind: VoteType,
+}
+
+impl Voting {
+ pub fn new(kind: VoteType, voters: Vec<ClientId>) -> Voting {
+ Voting {
+ kind,
+ voters,
+ ttl: 2,
+ votes: Vec::new(),
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/rust/hedgewars-server/src/handlers.rs Tue May 28 19:04:18 2019 +0300
@@ -0,0 +1,402 @@
+use mio;
+use std::{collections::HashMap, io, io::Write};
+
+use self::{
+ actions::{Destination, DestinationGroup, PendingMessage},
+ inanteroom::LoginResult
+};
+use crate::{
+ core::{
+ server::HWServer,
+ types::{ClientId, Replay, RoomId, GameCfg, TeamInfo},
+ room::RoomSave
+ },
+ protocol::messages::{
+ server_chat,
+ HWProtocolMessage,
+ HWServerMessage,
+ HWServerMessage::*,
+ global_chat,
+ HWProtocolMessage::EngineMessage
+ },
+ utils,
+};
+use base64::encode;
+use log::*;
+use rand::{thread_rng, RngCore};
+
+mod actions;
+mod checker;
+mod common;
+mod inroom;
+mod inlobby;
+mod inanteroom;
+
+use std::fmt::{Formatter, LowerHex};
+
+#[derive(PartialEq)]
+pub struct Sha1Digest([u8; 20]);
+
+impl Sha1Digest {
+ pub fn new(digest: [u8; 20]) -> Self {
+ Self(digest)
+ }
+}
+
+impl LowerHex for Sha1Digest {
+ fn fmt(&self, f: &mut Formatter) -> Result<(), std::fmt::Error> {
+ for byte in &self.0 {
+ write!(f, "{:02x}", byte)?;
+ }
+ Ok(())
+ }
+}
+
+pub struct AccountInfo {
+ pub is_registered: bool,
+ pub is_admin: bool,
+ pub is_contributor: bool,
+ pub server_hash: Sha1Digest,
+}
+
+pub enum IoTask {
+ GetAccount {
+ nick: String,
+ protocol: u16,
+ password_hash: String,
+ client_salt: String,
+ server_salt: String,
+ },
+ GetReplay {
+ id: u32,
+ },
+ SaveRoom {
+ room_id: RoomId,
+ filename: String,
+ contents: String,
+ },
+ LoadRoom {
+ room_id: RoomId,
+ filename: String,
+ },
+}
+
+pub enum IoResult {
+ Account(Option<AccountInfo>),
+ Replay(Option<Replay>),
+ SaveRoom(RoomId, bool),
+ LoadRoom(RoomId, Option<String>),
+}
+
+pub struct Response {
+ client_id: ClientId,
+ messages: Vec<PendingMessage>,
+ io_tasks: Vec<IoTask>,
+ removed_clients: Vec<ClientId>,
+}
+
+impl Response {
+ pub fn new(client_id: ClientId) -> Self {
+ Self {
+ client_id,
+ messages: vec![],
+ io_tasks: vec![],
+ removed_clients: vec![],
+ }
+ }
+
+ #[inline]
+ pub fn is_empty(&self) -> bool {
+ self.messages.is_empty() && self.removed_clients.is_empty() && self.io_tasks.is_empty()
+ }
+
+ #[inline]
+ pub fn len(&self) -> usize {
+ self.messages.len()
+ }
+
+ #[inline]
+ pub fn client_id(&self) -> ClientId {
+ self.client_id
+ }
+
+ #[inline]
+ pub fn add(&mut self, message: PendingMessage) {
+ self.messages.push(message)
+ }
+
+ #[inline]
+ pub fn request_io(&mut self, task: IoTask) {
+ self.io_tasks.push(task)
+ }
+
+ pub fn extract_messages<'a, 'b: 'a>(
+ &'b mut self,
+ server: &'a HWServer,
+ ) -> impl Iterator<Item = (Vec<ClientId>, HWServerMessage)> + 'a {
+ let client_id = self.client_id;
+ self.messages.drain(..).map(move |m| {
+ let ids = get_recipients(server, client_id, m.destination);
+ (ids, m.message)
+ })
+ }
+
+ pub fn remove_client(&mut self, client_id: ClientId) {
+ self.removed_clients.push(client_id);
+ }
+
+ pub fn extract_removed_clients(&mut self) -> impl Iterator<Item = ClientId> + '_ {
+ self.removed_clients.drain(..)
+ }
+
+ pub fn extract_io_tasks(&mut self) -> impl Iterator<Item = IoTask> + '_ {
+ self.io_tasks.drain(..)
+ }
+}
+
+impl Extend<PendingMessage> for Response {
+ fn extend<T: IntoIterator<Item = PendingMessage>>(&mut self, iter: T) {
+ for msg in iter {
+ self.add(msg)
+ }
+ }
+}
+
+fn get_recipients(
+ server: &HWServer,
+ client_id: ClientId,
+ destination: Destination,
+) -> Vec<ClientId> {
+ match destination {
+ Destination::ToSelf => vec![client_id],
+ Destination::ToId(id) => vec![id],
+ Destination::ToIds(ids) => ids,
+ Destination::ToAll { group, skip_self } => {
+ let mut ids: Vec<_> = match group {
+ DestinationGroup::All => server.all_clients().collect(),
+ DestinationGroup::Lobby => server.lobby_clients().collect(),
+ DestinationGroup::Protocol(proto) => server.protocol_clients(proto).collect(),
+ DestinationGroup::Room(id) => server.room_clients(id).collect(),
+ };
+
+ if skip_self {
+ if let Some(index) = ids.iter().position(|id| *id == client_id) {
+ ids.remove(index);
+ }
+ }
+
+ ids
+ }
+ }
+}
+
+pub fn handle(
+ server: &mut HWServer,
+ client_id: ClientId,
+ response: &mut Response,
+ message: HWProtocolMessage,
+) {
+ match message {
+ HWProtocolMessage::Ping => response.add(Pong.send_self()),
+ _ => {
+ if server.anteroom.clients.contains(client_id) {
+ match inanteroom::handle(server, client_id, response, message) {
+ LoginResult::Unchanged => (),
+ LoginResult::Complete => {
+ if let Some(client) = server.anteroom.remove_client(client_id) {
+ server.add_client(client_id, client);
+ common::join_lobby(server, response);
+ }
+ }
+ LoginResult::Exit => {
+ server.anteroom.remove_client(client_id);
+ response.remove_client(client_id);
+ }
+ }
+ } else if server.clients.contains(client_id) {
+ match message {
+ HWProtocolMessage::Quit(Some(msg)) => {
+ common::remove_client(server, response, "User quit: ".to_string() + &msg);
+ }
+ HWProtocolMessage::Quit(None) => {
+ common::remove_client(server, response, "User quit".to_string());
+ }
+ HWProtocolMessage::Info(nick) => {
+ if let Some(client) = server.find_client(&nick) {
+ let admin_sign = if client.is_admin() { "@" } else { "" };
+ let master_sign = if client.is_master() { "+" } else { "" };
+ let room_info = match client.room_id {
+ Some(room_id) => {
+ let room = &server.rooms[room_id];
+ let status = match room.game_info {
+ Some(_) if client.teams_in_game == 0 => "(spectating)",
+ Some(_) => "(playing)",
+ None => "",
+ };
+ format!(
+ "[{}{}room {}]{}",
+ admin_sign, master_sign, room.name, status
+ )
+ }
+ None => format!("[{}lobby]", admin_sign),
+ };
+
+ let info = vec![
+ client.nick.clone(),
+ "[]".to_string(),
+ utils::protocol_version_string(client.protocol_number).to_string(),
+ room_info,
+ ];
+ response.add(Info(info).send_self())
+ } else {
+ response
+ .add(server_chat("Player is not online.".to_string()).send_self())
+ }
+ }
+ HWProtocolMessage::ToggleServerRegisteredOnly => {
+ if !server.clients[client_id].is_admin() {
+ response.add(Warning("Access denied.".to_string()).send_self());
+ } else {
+ server.set_is_registered_only(server.is_registered_only());
+ let msg = if server.is_registered_only() {
+ "This server no longer allows unregistered players to join."
+ } else {
+ "This server now allows unregistered players to join."
+ };
+ response.add(server_chat(msg.to_string()).send_all());
+ }
+ }
+ HWProtocolMessage::Global(msg) => {
+ if !server.clients[client_id].is_admin() {
+ response.add(Warning("Access denied.".to_string()).send_self());
+ } else {
+ response.add(global_chat(msg).send_all())
+ }
+ }
+ HWProtocolMessage::SuperPower => {
+ if !server.clients[client_id].is_admin() {
+ response.add(Warning("Access denied.".to_string()).send_self());
+ } else {
+ server.clients[client_id].set_has_super_power(true);
+ response
+ .add(server_chat("Super power activated.".to_string()).send_self())
+ }
+ }
+ HWProtocolMessage::Watch(id) => {
+ #[cfg(feature = "official-server")]
+ {
+ response.request_io(IoTask::GetReplay { id })
+ }
+
+ #[cfg(not(feature = "official-server"))]
+ {
+ response.add(
+ Warning("This server does not support replays!".to_string())
+ .send_self(),
+ );
+ }
+ }
+ _ => match server.clients[client_id].room_id {
+ None => inlobby::handle(server, client_id, response, message),
+ Some(room_id) => {
+ inroom::handle(server, client_id, response, room_id, message)
+ }
+ },
+ }
+ }
+ }
+ }
+}
+
+pub fn handle_client_accept(server: &mut HWServer, client_id: ClientId, response: &mut Response) {
+ let mut salt = [0u8; 18];
+ thread_rng().fill_bytes(&mut salt);
+
+ server.anteroom.add_client(client_id, encode(&salt));
+
+ response.add(HWServerMessage::Connected(utils::SERVER_VERSION).send_self());
+}
+
+pub fn handle_client_loss(server: &mut HWServer, client_id: ClientId, response: &mut Response) {
+ if server.anteroom.remove_client(client_id).is_none() {
+ common::remove_client(server, response, "Connection reset".to_string());
+ }
+}
+
+pub fn handle_io_result(
+ server: &mut HWServer,
+ client_id: ClientId,
+ response: &mut Response,
+ io_result: IoResult,
+) {
+ match io_result {
+ IoResult::Account(Some(info)) => {
+ if !info.is_registered && server.is_registered_only() {
+ response.add(
+ Bye("This server only allows registered users to join.".to_string())
+ .send_self(),
+ );
+ response.remove_client(client_id);
+ } else {
+ response.add(ServerAuth(format!("{:x}", info.server_hash)).send_self());
+ if let Some(client) = server.anteroom.remove_client(client_id) {
+ server.add_client(client_id, client);
+ let client = &mut server.clients[client_id];
+ client.set_is_registered(info.is_registered);
+ client.set_is_admin(info.is_admin);
+ client.set_is_contributor(info.is_admin)
+ }
+ }
+ }
+ IoResult::Account(None) => {
+ response.add(Error("Authentication failed.".to_string()).send_self());
+ response.remove_client(client_id);
+ }
+ IoResult::Replay(Some(replay)) => {
+ let protocol = server.clients[client_id].protocol_number;
+ let start_msg = if protocol < 58 {
+ RoomJoined(vec![server.clients[client_id].nick.clone()])
+ } else {
+ ReplayStart
+ };
+ response.add(start_msg.send_self());
+
+ common::get_room_config_impl(&replay.config, client_id, response);
+ common::get_teams(replay.teams.iter(), client_id, response);
+ response.add(RunGame.send_self());
+ response.add(ForwardEngineMessage(replay.message_log).send_self());
+
+ if protocol < 58 {
+ response.add(Kicked.send_self());
+ }
+ }
+ IoResult::Replay(None) => {
+ response.add(Warning("Could't load the replay".to_string()).send_self())
+ }
+ IoResult::SaveRoom(_, true) => {
+ response.add(server_chat("Room configs saved successfully.".to_string()).send_self());
+ }
+ IoResult::SaveRoom(_, false) => {
+ response.add(Warning("Unable to save the room configs.".to_string()).send_self());
+ }
+ IoResult::LoadRoom(room_id, Some(contents)) => {
+ if let Some(ref mut room) = server.rooms.get_mut(room_id) {
+ match room.set_saves(&contents) {
+ Ok(_) => response.add(
+ server_chat("Room configs loaded successfully.".to_string()).send_self(),
+ ),
+ Err(e) => {
+ warn!("Error while deserializing the room configs: {}", e);
+ response.add(
+ Warning("Unable to deserialize the room configs.".to_string())
+ .send_self(),
+ );
+ }
+ }
+ }
+ }
+ IoResult::LoadRoom(_, None) => {
+ response.add(Warning("Unable to load the room configs.".to_string()).send_self());
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/rust/hedgewars-server/src/handlers/actions.rs Tue May 28 19:04:18 2019 +0300
@@ -0,0 +1,115 @@
+use crate::{
+ core::{
+ client::HWClient,
+ server::HWServer,
+ types::{ClientId, GameCfg, RoomId, VoteType},
+ room::HWRoom,
+ room::{GameInfo, RoomFlags}
+ },
+ protocol::messages::{server_chat, HWProtocolMessage, HWServerMessage, HWServerMessage::*},
+ utils::to_engine_msg,
+};
+use rand::{distributions::Uniform, thread_rng, Rng};
+use std::{io, io::Write, iter::once, mem::replace};
+
+pub enum DestinationGroup {
+ All,
+ Lobby,
+ Room(RoomId),
+ Protocol(u16),
+}
+
+pub enum Destination {
+ ToId(ClientId),
+ ToIds(Vec<ClientId>),
+ ToSelf,
+ ToAll {
+ group: DestinationGroup,
+ skip_self: bool,
+ },
+}
+
+pub struct PendingMessage {
+ pub destination: Destination,
+ pub message: HWServerMessage,
+}
+
+impl PendingMessage {
+ pub fn send(message: HWServerMessage, client_id: ClientId) -> PendingMessage {
+ PendingMessage {
+ destination: Destination::ToId(client_id),
+ message,
+ }
+ }
+
+ pub fn send_many(message: HWServerMessage, client_ids: Vec<ClientId>) -> PendingMessage {
+ PendingMessage {
+ destination: Destination::ToIds(client_ids),
+ message,
+ }
+ }
+
+ pub fn send_self(message: HWServerMessage) -> PendingMessage {
+ PendingMessage {
+ destination: Destination::ToSelf,
+ message,
+ }
+ }
+
+ pub fn send_all(message: HWServerMessage) -> PendingMessage {
+ let destination = Destination::ToAll {
+ group: DestinationGroup::All,
+ skip_self: false,
+ };
+ PendingMessage {
+ destination,
+ message,
+ }
+ }
+
+ pub fn in_room(mut self, clients_room_id: RoomId) -> PendingMessage {
+ if let Destination::ToAll { ref mut group, .. } = self.destination {
+ *group = DestinationGroup::Room(clients_room_id)
+ }
+ self
+ }
+
+ pub fn in_lobby(mut self) -> PendingMessage {
+ if let Destination::ToAll { ref mut group, .. } = self.destination {
+ *group = DestinationGroup::Lobby
+ }
+ self
+ }
+
+ pub fn with_protocol(mut self, protocol_number: u16) -> PendingMessage {
+ if let Destination::ToAll { ref mut group, .. } = self.destination {
+ *group = DestinationGroup::Protocol(protocol_number)
+ }
+ self
+ }
+
+ pub fn but_self(mut self) -> PendingMessage {
+ if let Destination::ToAll {
+ ref mut skip_self, ..
+ } = self.destination
+ {
+ *skip_self = true
+ }
+ self
+ }
+}
+
+impl HWServerMessage {
+ pub fn send(self, client_id: ClientId) -> PendingMessage {
+ PendingMessage::send(self, client_id)
+ }
+ pub fn send_many(self, client_ids: Vec<ClientId>) -> PendingMessage {
+ PendingMessage::send_many(self, client_ids)
+ }
+ pub fn send_self(self) -> PendingMessage {
+ PendingMessage::send_self(self)
+ }
+ pub fn send_all(self) -> PendingMessage {
+ PendingMessage::send_all(self)
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/rust/hedgewars-server/src/handlers/checker.rs Tue May 28 19:04:18 2019 +0300
@@ -0,0 +1,13 @@
+use log::*;
+use mio;
+
+use crate::{
+ protocol::messages::HWProtocolMessage,
+ core::{server::HWServer, types::ClientId},
+};
+
+pub fn handle(_server: &mut HWServer, _client_id: ClientId, message: HWProtocolMessage) {
+ match message {
+ _ => warn!("Unknown command"),
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/rust/hedgewars-server/src/handlers/common.rs Tue May 28 19:04:18 2019 +0300
@@ -0,0 +1,661 @@
+use crate::{
+ protocol::messages::{
+ server_chat,
+ add_flags, remove_flags,
+ HWProtocolMessage::{self, Rnd},
+ HWServerMessage::{self, *},
+ ProtocolFlags as Flags,
+ },
+ core::{
+ client::HWClient,
+ server::HWServer,
+ types::{ClientId, GameCfg, RoomId, TeamInfo, Vote, VoteType},
+ room::HWRoom,
+ },
+ utils::to_engine_msg,
+};
+
+use super::Response;
+
+use crate::core::types::RoomConfig;
+use rand::{self, seq::SliceRandom, thread_rng, Rng};
+use std::{iter::once, mem::replace};
+
+pub fn rnd_reply(options: &[String]) -> HWServerMessage {
+ let mut rng = thread_rng();
+
+ let reply = if options.is_empty() {
+ (*&["heads", "tails"].choose(&mut rng).unwrap()).to_string()
+ } else {
+ options.choose(&mut rng).unwrap().clone()
+ };
+
+ ChatMsg {
+ nick: "[random]".to_string(),
+ msg: reply,
+ }
+}
+
+pub fn join_lobby(server: &mut HWServer, response: &mut Response) {
+ let client_id = response.client_id();
+
+ let client = &server.clients[client_id];
+ let nick = vec![client.nick.clone()];
+ let mut flags = vec![];
+ if client.is_registered() {
+ flags.push(Flags::Registered)
+ }
+ if client.is_admin() {
+ flags.push(Flags::Admin)
+ }
+ if client.is_contributor() {
+ flags.push(Flags::Contributor)
+ }
+
+ let all_nicks: Vec<_> = server.collect_nicks(|_| true);
+
+ let mut flag_selectors = [
+ (
+ Flags::Registered,
+ server.collect_nicks(|(_, c)| c.is_registered()),
+ ),
+ (Flags::Admin, server.collect_nicks(|(_, c)| c.is_admin())),
+ (
+ Flags::Contributor,
+ server.collect_nicks(|(_, c)| c.is_contributor()),
+ ),
+ (
+ Flags::InRoom,
+ server.collect_nicks(|(_, c)| c.room_id.is_some()),
+ ),
+ ];
+
+ let server_msg = ServerMessage(server.get_greetings(client_id).to_string());
+
+ let rooms_msg = Rooms(
+ server
+ .rooms
+ .iter()
+ .filter(|(_, r)| r.protocol_number == client.protocol_number)
+ .flat_map(|(_, r)| r.info(r.master_id.map(|id| &server.clients[id])))
+ .collect(),
+ );
+
+ response.add(LobbyJoined(nick).send_all().but_self());
+ response.add(
+ ClientFlags(add_flags(&flags), all_nicks.clone())
+ .send_all()
+ .but_self(),
+ );
+
+ response.add(LobbyJoined(all_nicks).send_self());
+ for (flag, nicks) in &mut flag_selectors {
+ if !nicks.is_empty() {
+ response.add(ClientFlags(add_flags(&[*flag]), replace(nicks, vec![])).send_self());
+ }
+ }
+
+ response.add(server_msg.send_self());
+ response.add(rooms_msg.send_self());
+}
+
+pub fn remove_teams(
+ room: &mut HWRoom,
+ team_names: Vec<String>,
+ 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,
+ new_master_id: ClientId,
+ response: &mut Response,
+) {
+ let room = &mut server.rooms[room_id];
+ if let Some(master_id) = room.master_id {
+ server.clients[master_id].set_is_master(false);
+ response.add(
+ ClientFlags(
+ remove_flags(&[Flags::RoomMaster]),
+ vec![server.clients[master_id].nick.clone()],
+ )
+ .send_all()
+ .in_room(room_id),
+ )
+ }
+
+ room.master_id = Some(new_master_id);
+ server.clients[new_master_id].set_is_master(true);
+
+ response.add(
+ ClientFlags(
+ add_flags(&[Flags::RoomMaster]),
+ vec![server.clients[new_master_id].nick.clone()],
+ )
+ .send_all()
+ .in_room(room_id),
+ );
+}
+
+pub fn enter_room(
+ server: &mut HWServer,
+ client_id: ClientId,
+ room_id: RoomId,
+ response: &mut Response,
+) {
+ let nick = server.clients[client_id].nick.clone();
+ server.move_to_room(client_id, room_id);
+
+ response.add(RoomJoined(vec![nick.clone()]).send_all().in_room(room_id));
+ response.add(ClientFlags(add_flags(&[Flags::InRoom]), vec![nick]).send_all());
+ let nicks = server.collect_nicks(|(_, c)| c.room_id == Some(room_id));
+ response.add(RoomJoined(nicks).send_self());
+
+ get_room_teams(server, room_id, client_id, response);
+
+ let room = &server.rooms[room_id];
+ get_room_config(room, client_id, response);
+
+ let mut flag_selectors = [
+ (
+ Flags::RoomMaster,
+ server.collect_nicks(|(_, c)| c.is_master()),
+ ),
+ (Flags::Ready, server.collect_nicks(|(_, c)| c.is_ready())),
+ (Flags::InGame, server.collect_nicks(|(_, c)| c.is_in_game())),
+ ];
+
+ for (flag, nicks) in &mut flag_selectors {
+ response.add(ClientFlags(add_flags(&[*flag]), replace(nicks, vec![])).send_self());
+ }
+
+ if !room.greeting.is_empty() {
+ response.add(
+ ChatMsg {
+ nick: "[greeting]".to_string(),
+ msg: room.greeting.clone(),
+ }
+ .send_self(),
+ );
+ }
+}
+
+pub fn exit_room(server: &mut HWServer, client_id: ClientId, response: &mut Response, msg: &str) {
+ let client = &mut server.clients[client_id];
+
+ if let Some(room_id) = client.room_id {
+ let room = &mut server.rooms[room_id];
+
+ remove_client_from_room(client, room, response, msg);
+
+ 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);
+
+ 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);
+
+ response.add(
+ ClientFlags(add_flags(&[Flags::RoomMaster]), vec![new_master_nick])
+ .send_all()
+ .in_room(room.id),
+ );
+ }
+ }
+ }
+ }
+}
+
+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 nick = client.nick.clone();
+
+ exit_room(server, client_id, response, &msg);
+
+ server.remove_client(client_id);
+
+ response.add(LobbyLeft(nick, msg.to_string()).send_all());
+ response.add(Bye("User quit: ".to_string() + &msg).send_self());
+ response.remove_client(client_id);
+}
+
+pub fn get_room_update(
+ room_name: Option<String>,
+ room: &HWRoom,
+ master: Option<&HWClient>,
+ response: &mut Response,
+) {
+ let update_msg = RoomUpdated(room_name.unwrap_or(room.name.clone()), room.info(master));
+ response.add(update_msg.send_all().with_protocol(room.protocol_number));
+}
+
+pub fn get_room_config_impl(config: &RoomConfig, to_client: ClientId, response: &mut Response) {
+ response.add(ConfigEntry("FULLMAPCONFIG".to_string(), config.to_map_config()).send(to_client));
+ for cfg in config.to_game_config() {
+ response.add(cfg.to_server_msg().send(to_client));
+ }
+}
+
+pub fn get_room_config(room: &HWRoom, to_client: ClientId, response: &mut Response) {
+ get_room_config_impl(room.active_config(), to_client, response);
+}
+
+pub fn get_teams<'a, I>(teams: I, to_client: ClientId, response: &mut Response)
+where
+ I: Iterator<Item = &'a TeamInfo>,
+{
+ for team in teams {
+ response.add(TeamAdd(team.to_protocol()).send(to_client));
+ response.add(TeamColor(team.name.clone(), team.color).send(to_client));
+ response.add(HedgehogsNumber(team.name.clone(), team.hedgehogs_number).send(to_client));
+ }
+}
+
+pub fn get_room_teams(
+ server: &HWServer,
+ room_id: RoomId,
+ to_client: ClientId,
+ response: &mut Response,
+) {
+ let room = &server.rooms[room_id];
+ let current_teams = match room.game_info {
+ Some(ref info) => &info.teams_at_start,
+ None => &room.teams,
+ };
+
+ get_teams(current_teams.iter().map(|(_, t)| t), to_client, response);
+}
+
+pub fn get_room_flags(
+ server: &HWServer,
+ room_id: RoomId,
+ to_client: ClientId,
+ response: &mut Response,
+) {
+ let room = &server.rooms[room_id];
+ if let Some(id) = room.master_id {
+ response.add(
+ ClientFlags(
+ add_flags(&[Flags::RoomMaster]),
+ vec![server.clients[id].nick.clone()],
+ )
+ .send(to_client),
+ );
+ }
+ let nicks: Vec<_> = server
+ .clients
+ .iter()
+ .filter(|(_, c)| c.room_id == Some(room_id) && c.is_ready())
+ .map(|(_, c)| c.nick.clone())
+ .collect();
+ if !nicks.is_empty() {
+ response.add(ClientFlags(add_flags(&[Flags::Ready]), nicks).send(to_client));
+ }
+}
+
+pub fn apply_voting_result(
+ server: &mut HWServer,
+ room_id: RoomId,
+ response: &mut Response,
+ kind: VoteType,
+) {
+ 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));
+ exit_room(server, id, response, "kicked");
+ }
+ }
+ }
+ VoteType::Map(None) => (),
+ VoteType::Map(Some(name)) => {
+ if let Some(location) = server.rooms[room_id].load_config(&name) {
+ response.add(
+ server_chat(location.to_string())
+ .send_all()
+ .in_room(room_id),
+ );
+ let room = &server.rooms[room_id];
+ let room_master = if let Some(id) = room.master_id {
+ Some(&server.clients[id])
+ } else {
+ None
+ };
+ get_room_update(None, room, room_master, response);
+
+ for (_, client) in server.clients.iter() {
+ if client.room_id == Some(room_id) {
+ super::common::get_room_config(&server.rooms[room_id], client.id, response);
+ }
+ }
+ }
+ }
+ VoteType::Pause => {
+ if let Some(ref mut info) = server.rooms[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::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.rooms[room_id].set_config(cfg);
+ }
+ VoteType::HedgehogsPerTeam(number) => {
+ let r = &mut server.rooms[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);
+ }
+ } 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.clients[client_id];
+
+ if let Some(room_id) = client.room_id {
+ let room = &mut server.rooms[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);
+ }
+ }
+ }
+}
+
+pub fn start_game(server: &mut HWServer, room_id: RoomId, response: &mut Response) {
+ let (room_clients, room_nicks): (Vec<_>, Vec<_>) = server
+ .clients
+ .iter()
+ .map(|(id, c)| (id, c.nick.clone()))
+ .unzip();
+ let room = &mut server.rooms[room_id];
+
+ if !room.has_multiple_clans() {
+ response.add(
+ Warning("The game can't be started with less than two clans!".to_string()).send_self(),
+ );
+ } else if room.protocol_number <= 43 && room.players_number != room.ready_players_number {
+ response.add(Warning("Not all players are ready".to_string()).send_self());
+ } else if room.game_info.is_some() {
+ response.add(Warning("The game is already in progress".to_string()).send_self());
+ } else {
+ room.start_round();
+ for id in room_clients {
+ let c = &mut server.clients[id];
+ c.set_is_in_game(true);
+ c.team_indices = room.client_team_indices(c.id);
+ }
+ response.add(RunGame.send_all().in_room(room.id));
+ response.add(
+ ClientFlags(add_flags(&[Flags::InGame]), room_nicks)
+ .send_all()
+ .in_room(room.id),
+ );
+
+ let room_master = if let Some(id) = room.master_id {
+ Some(&server.clients[id])
+ } else {
+ None
+ };
+ get_room_update(None, room, room_master, response);
+ }
+}
+
+pub fn end_game(server: &mut HWServer, room_id: RoomId, response: &mut Response) {
+ let room = &mut server.rooms[room_id];
+ room.ready_players_number = 1;
+ let room_master = if let Some(id) = room.master_id {
+ Some(&server.clients[id])
+ } else {
+ None
+ };
+ get_room_update(None, room, room_master, response);
+ response.add(RoundFinished.send_all().in_room(room_id));
+
+ if let Some(info) = replace(&mut room.game_info, None) {
+ for (_, client) in server.clients.iter() {
+ if client.room_id == Some(room_id) && client.is_joined_mid_game() {
+ super::common::get_room_config(room, client.id, response);
+ response.extend(
+ info.left_teams
+ .iter()
+ .map(|name| TeamRemove(name.clone()).send(client.id)),
+ );
+ }
+ }
+ }
+
+ let nicks: Vec<_> = 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();
+
+ if !nicks.is_empty() {
+ let msg = if room.protocol_number < 38 {
+ LegacyReady(false, nicks)
+ } else {
+ ClientFlags(remove_flags(&[Flags::Ready]), nicks)
+ };
+ response.add(msg.send_all().in_room(room_id));
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use crate::protocol::messages::HWServerMessage::ChatMsg;
+ use crate::server::actions::PendingMessage;
+
+ fn reply2string(r: HWServerMessage) -> String {
+ match r {
+ ChatMsg { msg: p, .. } => String::from(p),
+ _ => panic!("expected a ChatMsg"),
+ }
+ }
+
+ fn run_handle_test(opts: Vec<String>) {
+ let opts2 = opts.clone();
+ for opt in opts {
+ while reply2string(rnd_reply(&opts2)) != opt {}
+ }
+ }
+
+ /// This test terminates almost surely.
+ #[test]
+ fn test_handle_rnd_empty() {
+ run_handle_test(vec![])
+ }
+
+ /// This test terminates almost surely.
+ #[test]
+ fn test_handle_rnd_nonempty() {
+ run_handle_test(vec!["A".to_owned(), "B".to_owned(), "C".to_owned()])
+ }
+
+ /// This test terminates almost surely (strong law of large numbers)
+ #[test]
+ fn test_distribution() {
+ let eps = 0.000001;
+ let lim = 0.5;
+ let opts = vec![0.to_string(), 1.to_string()];
+ let mut ones = 0;
+ let mut tries = 0;
+
+ while tries < 1000 || ((ones as f64 / tries as f64) - lim).abs() >= eps {
+ tries += 1;
+ if reply2string(rnd_reply(&opts)) == 1.to_string() {
+ ones += 1;
+ }
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/rust/hedgewars-server/src/handlers/inanteroom.rs Tue May 28 19:04:18 2019 +0300
@@ -0,0 +1,148 @@
+use mio;
+
+use crate::{
+ protocol::messages::{
+ HWProtocolMessage::LoadRoom,
+ HWProtocolMessage,
+ HWServerMessage::*},
+ core::{
+ client::HWClient,
+ server::{HWServer, HWAnteClient, HWAnteroom},
+ types::ClientId
+ },
+ utils::is_name_illegal
+};
+
+use log::*;
+#[cfg(feature = "official-server")]
+use openssl::sha::sha1;
+use std::{
+ fmt::{Formatter, LowerHex},
+ num::NonZeroU16,
+};
+
+pub enum LoginResult {
+ Unchanged,
+ Complete,
+ Exit,
+}
+
+fn completion_result<'a, I>(
+ mut other_clients: I,
+ client: &mut HWAnteClient,
+ response: &mut super::Response,
+) -> LoginResult
+where
+ I: Iterator<Item = (ClientId, &'a HWClient)>,
+{
+ let has_nick_clash =
+ other_clients.any(|(_, c)| !c.is_checker() && c.nick == *client.nick.as_ref().unwrap());
+
+ if has_nick_clash {
+ if client.protocol_number.unwrap().get() < 38 {
+ response.add(Bye("User quit: Nickname is already in use".to_string()).send_self());
+ LoginResult::Exit
+ } else {
+ client.nick = None;
+ response.add(Notice("NickAlreadyInUse".to_string()).send_self());
+ LoginResult::Unchanged
+ }
+ } else {
+ #[cfg(feature = "official-server")]
+ {
+ response.add(AskPassword(client.server_salt.clone()).send_self());
+ LoginResult::Unchanged
+ }
+
+ #[cfg(not(feature = "official-server"))]
+ {
+ LoginResult::Complete
+ }
+ }
+}
+
+pub fn handle(
+ server: &mut HWServer,
+ client_id: ClientId,
+ response: &mut super::Response,
+ message: HWProtocolMessage,
+) -> LoginResult {
+ match message {
+ HWProtocolMessage::Quit(_) => {
+ response.add(Bye("User quit".to_string()).send_self());
+ LoginResult::Exit
+ }
+ HWProtocolMessage::Nick(nick) => {
+ let client = &mut server.anteroom.clients[client_id];
+
+ if client.nick.is_some() {
+ response.add(Error("Nickname already provided.".to_string()).send_self());
+ LoginResult::Unchanged
+ } else if is_name_illegal(&nick) {
+ response.add(Bye("Illegal nickname! Nicknames 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());
+ LoginResult::Exit
+ } else {
+ client.nick = Some(nick.clone());
+ response.add(Nick(nick).send_self());
+
+ if client.protocol_number.is_some() {
+ completion_result(server.clients.iter(), client, response)
+ } else {
+ LoginResult::Unchanged
+ }
+ }
+ }
+ HWProtocolMessage::Proto(proto) => {
+ let client = &mut server.anteroom.clients[client_id];
+ if client.protocol_number.is_some() {
+ response.add(Error("Protocol already known.".to_string()).send_self());
+ LoginResult::Unchanged
+ } else if proto == 0 {
+ response.add(Error("Bad number.".to_string()).send_self());
+ LoginResult::Unchanged
+ } else {
+ client.protocol_number = NonZeroU16::new(proto);
+ response.add(Proto(proto).send_self());
+
+ if client.nick.is_some() {
+ completion_result(server.clients.iter(), client, response)
+ } else {
+ LoginResult::Unchanged
+ }
+ }
+ }
+ #[cfg(feature = "official-server")]
+ HWProtocolMessage::Password(hash, salt) => {
+ let client = &server.anteroom.clients[client_id];
+
+ if let (Some(nick), Some(protocol)) = (client.nick.as_ref(), client.protocol_number) {
+ response.request_io(super::IoTask::GetAccount {
+ nick: nick.clone(),
+ protocol: protocol.get(),
+ server_salt: client.server_salt.clone(),
+ client_salt: salt,
+ password_hash: hash,
+ });
+ };
+
+ LoginResult::Unchanged
+ }
+ #[cfg(feature = "official-server")]
+ HWProtocolMessage::Checker(protocol, nick, password) => {
+ let client = &mut server.anteroom.clients[client_id];
+ if protocol == 0 {
+ response.add(Error("Bad number.".to_string()).send_self());
+ LoginResult::Unchanged
+ } else {
+ client.protocol_number = NonZeroU16::new(protocol);
+ client.nick = Some(nick);
+ client.is_checker = true;
+ LoginResult::Complete
+ }
+ }
+ _ => {
+ warn!("Incorrect command in logging-in state");
+ LoginResult::Unchanged
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/rust/hedgewars-server/src/handlers/inlobby.rs Tue May 28 19:04:18 2019 +0300
@@ -0,0 +1,164 @@
+use mio;
+
+use super::common::rnd_reply;
+use crate::{
+ protocol::messages::{
+ add_flags, remove_flags, server_chat, HWProtocolMessage, HWServerMessage::*,
+ ProtocolFlags as Flags,
+ },
+ core::{
+ client::HWClient,
+ server::HWServer,
+ types::{ClientId, ServerVar},
+ },
+ utils::is_name_illegal,
+};
+use log::*;
+use std::{collections::HashSet, convert::identity};
+
+pub fn handle(
+ server: &mut HWServer,
+ client_id: ClientId,
+ response: &mut super::Response,
+ message: HWProtocolMessage,
+) {
+ use crate::protocol::messages::HWProtocolMessage::*;
+ match message {
+ CreateRoom(name, password) => {
+ if is_name_illegal(&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());
+ } else if server.has_room(&name) {
+ response.add(
+ Warning("A room with the same name already exists.".to_string()).send_self(),
+ );
+ } else {
+ let flags_msg = ClientFlags(
+ add_flags(&[Flags::RoomMaster, Flags::Ready]),
+ vec![server.clients[client_id].nick.clone()],
+ );
+
+ let room_id = server.create_room(client_id, name, password);
+ let room = &server.rooms[room_id];
+ let client = &server.clients[client_id];
+
+ response.add(
+ RoomAdd(room.info(Some(&client)))
+ .send_all()
+ .with_protocol(room.protocol_number),
+ );
+ response.add(RoomJoined(vec![client.nick.clone()]).send_self());
+ response.add(flags_msg.send_self());
+
+ response.add(
+ ClientFlags(add_flags(&[Flags::InRoom]), vec![client.nick.clone()]).send_self(),
+ );
+ };
+ }
+ Chat(msg) => {
+ response.add(
+ ChatMsg {
+ nick: server.clients[client_id].nick.clone(),
+ msg,
+ }
+ .send_all()
+ .in_lobby()
+ .but_self(),
+ );
+ }
+ JoinRoom(name, _password) => {
+ let room = server.rooms.iter().find(|(_, r)| r.name == name);
+ let room_id = room.map(|(_, r)| r.id);
+
+ let client = &mut server.clients[client_id];
+
+ if let Some((_, room)) = room {
+ if client.protocol_number != room.protocol_number {
+ response.add(
+ Warning("Room version incompatible to your Hedgewars version!".to_string())
+ .send_self(),
+ );
+ } else if room.is_join_restricted() {
+ response.add(
+ Warning(
+ "Access denied. This room currently doesn't allow joining.".to_string(),
+ )
+ .send_self(),
+ );
+ } else if room.players_number == u8::max_value() {
+ response.add(Warning("This room is already full".to_string()).send_self());
+ } else if let Some(room_id) = room_id {
+ super::common::enter_room(server, client_id, room_id, response);
+ }
+ } else {
+ response.add(Warning("No such room.".to_string()).send_self());
+ }
+ }
+ Follow(nick) => {
+ if let Some(HWClient {
+ room_id: Some(room_id),
+ ..
+ }) = server.find_client(&nick)
+ {
+ let room = &server.rooms[*room_id];
+ response.add(Joining(room.name.clone()).send_self());
+ super::common::enter_room(server, client_id, *room_id, response);
+ }
+ }
+ SetServerVar(var) => {
+ if !server.clients[client_id].is_admin() {
+ response.add(Warning("Access denied.".to_string()).send_self());
+ } else {
+ match var {
+ ServerVar::MOTDNew(msg) => server.greetings.for_latest_protocol = msg,
+ ServerVar::MOTDOld(msg) => server.greetings.for_old_protocols = msg,
+ ServerVar::LatestProto(n) => server.latest_protocol = n,
+ }
+ }
+ }
+ GetServerVar => {
+ if !server.clients[client_id].is_admin() {
+ response.add(Warning("Access denied.".to_string()).send_self());
+ } else {
+ let vars: Vec<_> = [
+ ServerVar::MOTDNew(server.greetings.for_latest_protocol.clone()),
+ ServerVar::MOTDOld(server.greetings.for_old_protocols.clone()),
+ ServerVar::LatestProto(server.latest_protocol),
+ ]
+ .iter()
+ .flat_map(|v| v.to_protocol())
+ .collect();
+ response.add(ServerVars(vars).send_self());
+ }
+ }
+ Rnd(v) => {
+ response.add(rnd_reply(&v).send_self());
+ }
+ Stats => {
+ let mut protocols: HashSet<_> = server
+ .clients
+ .iter()
+ .map(|(_, c)| c.protocol_number)
+ .chain(server.rooms.iter().map(|(_, r)| r.protocol_number))
+ .collect();
+ let mut protocols: Vec<_> = protocols.drain().collect();
+ protocols.sort();
+
+ let mut html = Vec::with_capacity(protocols.len() + 2);
+
+ html.push("<table>".to_string());
+ for protocol in protocols {
+ html.push(format!(
+ "<tr><td>{}</td><td>{}</td><td>{}</td></tr>",
+ super::utils::protocol_version_string(protocol),
+ server.protocol_clients(protocol).count(),
+ server.protocol_rooms(protocol).count()
+ ));
+ }
+ html.push("</table>".to_string());
+
+ response.add(Warning(html.join("")).send_self());
+ }
+ List => warn!("Deprecated LIST message received"),
+ _ => warn!("Incorrect command in lobby state"),
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/rust/hedgewars-server/src/handlers/inroom.rs Tue May 28 19:04:18 2019 +0300
@@ -0,0 +1,615 @@
+use mio;
+
+use super::common::rnd_reply;
+use crate::utils::to_engine_msg;
+use crate::{
+ protocol::messages::{
+ add_flags, remove_flags, server_chat, HWProtocolMessage, HWServerMessage::*,
+ ProtocolFlags as Flags,
+ },
+ core::{
+ server::HWServer,
+ types,
+ types::{ClientId, GameCfg, RoomId, VoteType, Voting, MAX_HEDGEHOGS_PER_TEAM},
+ room::{HWRoom, RoomFlags, MAX_TEAMS_IN_ROOM},
+ },
+ utils::is_name_illegal,
+};
+use base64::{decode, encode};
+use log::*;
+use std::{cmp::min, iter::once, mem::swap};
+
+#[derive(Clone)]
+struct ByMsg<'a> {
+ messages: &'a [u8],
+}
+
+impl<'a> Iterator for ByMsg<'a> {
+ type Item = &'a [u8];
+
+ fn next(&mut self) -> Option<<Self as Iterator>::Item> {
+ if let Some(size) = self.messages.get(0) {
+ let (msg, next) = self.messages.split_at(*size as usize + 1);
+ self.messages = next;
+ Some(msg)
+ } else {
+ None
+ }
+ }
+}
+
+fn by_msg(source: &[u8]) -> ByMsg {
+ ByMsg { messages: source }
+}
+
+const VALID_MESSAGES: &[u8] =
+ 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)]
+fn is_msg_valid(msg: &[u8], team_indices: &[u8]) -> bool {
+ match msg {
+ [size, typ, body..] => {
+ VALID_MESSAGES.contains(typ)
+ && match body {
+ [1...MAX_HEDGEHOGS_PER_TEAM, team, ..] if *typ == b'h' => {
+ team_indices.contains(team)
+ }
+ _ => *typ != b'h',
+ }
+ }
+ _ => false,
+ }
+}
+
+fn is_msg_valid(msg: &[u8], _team_indices: &[u8]) -> bool {
+ if let Some(typ) = msg.get(1) {
+ VALID_MESSAGES.contains(typ)
+ } else {
+ false
+ }
+}
+
+fn is_msg_empty(msg: &[u8]) -> bool {
+ msg.get(1).filter(|t| **t == b'+').is_some()
+}
+
+fn is_msg_timed(msg: &[u8]) -> bool {
+ msg.get(1)
+ .filter(|t| !NON_TIMED_MESSAGES.contains(t))
+ .is_some()
+}
+
+fn voting_description(kind: &VoteType) -> String {
+ format!(
+ "New voting started: {}",
+ match kind {
+ VoteType::Kick(nick) => format!("kick {}", nick),
+ VoteType::Map(name) => format!("map {}", name.as_ref().unwrap()),
+ VoteType::Pause => "pause".to_string(),
+ VoteType::NewSeed => "new seed".to_string(),
+ VoteType::HedgehogsPerTeam(number) => format!("hedgehogs per team: {}", number),
+ }
+ )
+}
+
+fn room_message_flag(msg: &HWProtocolMessage) -> RoomFlags {
+ use crate::protocol::messages::HWProtocolMessage::*;
+ match msg {
+ ToggleRestrictJoin => RoomFlags::RESTRICTED_JOIN,
+ ToggleRestrictTeams => RoomFlags::RESTRICTED_TEAM_ADD,
+ ToggleRegisteredOnly => RoomFlags::RESTRICTED_UNREGISTERED_PLAYERS,
+ _ => RoomFlags::empty(),
+ }
+}
+
+pub fn handle(
+ server: &mut HWServer,
+ client_id: ClientId,
+ response: &mut super::Response,
+ room_id: RoomId,
+ message: HWProtocolMessage,
+) {
+ let client = &mut server.clients[client_id];
+ let room = &mut server.rooms[room_id];
+
+ use crate::protocol::messages::HWProtocolMessage::*;
+ match message {
+ Part(msg) => {
+ let msg = match msg {
+ Some(s) => format!("part: {}", s),
+ None => "part".to_string(),
+ };
+ super::common::exit_room(server, client_id, response, &msg);
+ }
+ Chat(msg) => {
+ response.add(
+ ChatMsg {
+ nick: client.nick.clone(),
+ msg,
+ }
+ .send_all()
+ .in_room(room_id),
+ );
+ }
+ 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();
+ response.add(ForwardEngineMessage(vec![engine_msg]).send_many(team))
+ }
+ }
+ }
+ 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);
+ }
+ }
+ Unfix => {
+ if client.is_admin() {
+ room.set_is_fixed(false);
+ }
+ }
+ Greeting(text) => {
+ if client.is_admin() || client.is_master() && !room.is_fixed() {
+ room.greeting = text;
+ }
+ }
+ MaxTeams(count) => {
+ if !client.is_master() {
+ response.add(Warning("You're not the room master!".to_string()).send_self());
+ } else if !(2..=MAX_TEAMS_IN_ROOM).contains(&count) {
+ response
+ .add(Warning("/maxteams: specify number from 2 to 8".to_string()).send_self());
+ } else {
+ server.rooms[room_id].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());
+ } else if server.has_room(&new_name) {
+ response.add(
+ Warning("A room with the same name already exists.".to_string()).send_self(),
+ );
+ } else {
+ let room = &mut server.rooms[room_id];
+ if room.is_fixed() || room.master_id != Some(client_id) {
+ response.add(Warning("Access denied.".to_string()).send_self());
+ } 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);
+ }
+ }
+ }
+ ToggleReady => {
+ let flags = if client.is_ready() {
+ room.ready_players_number -= 1;
+ remove_flags(&[Flags::Ready])
+ } else {
+ room.ready_players_number += 1;
+ add_flags(&[Flags::Ready])
+ };
+
+ let msg = if client.protocol_number < 38 {
+ LegacyReady(client.is_ready(), vec![client.nick.clone()])
+ } else {
+ ClientFlags(flags, vec![client.nick.clone()])
+ };
+ response.add(msg.send_all().in_room(room.id));
+ client.set_is_ready(!client.is_ready());
+
+ if room.is_fixed() && room.ready_players_number == room.players_number {
+ super::common::start_game(server, room_id, response);
+ }
+ }
+ AddTeam(mut info) => {
+ if room.teams.len() >= room.max_teams as usize {
+ response.add(Warning("Too many teams!".to_string()).send_self());
+ } else if room.addable_hedgehogs() == 0 {
+ response.add(Warning("Too many hedgehogs!".to_string()).send_self());
+ } 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(),
+ );
+ } else if room.game_info.is_some() {
+ response.add(
+ Warning("Joining not possible: Round is in progress.".to_string()).send_self(),
+ );
+ } else if room.is_team_add_restricted() {
+ response.add(
+ Warning("This room currently does not allow adding new teams.".to_string())
+ .send_self(),
+ );
+ } 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);
+ response.add(TeamAccepted(team.name.clone()).send_self());
+ response.add(
+ TeamAdd(team.to_protocol())
+ .send_all()
+ .in_room(room_id)
+ .but_self(),
+ );
+ response.add(
+ TeamColor(team.name.clone(), team.color)
+ .send_all()
+ .in_room(room_id),
+ );
+ response.add(
+ HedgehogsNumber(team.name.clone(), team.hedgehogs_number)
+ .send_all()
+ .in_room(room_id),
+ );
+
+ let room_master = if let Some(id) = room.master_id {
+ Some(&server.clients[id])
+ } else {
+ None
+ };
+ super::common::get_room_update(None, room, room_master, response);
+ }
+ }
+ 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()),
+ Some((_, name)) => {
+ client.teams_in_game -= 1;
+ client.clan = room.find_team_color(client.id);
+ super::common::remove_teams(
+ room,
+ vec![name.to_string()],
+ client.is_in_game(),
+ response,
+ );
+
+ match room.game_info {
+ Some(ref info) if info.teams_in_game == 0 => {
+ super::common::end_game(server, room_id, response)
+ }
+ _ => (),
+ }
+ }
+ },
+ 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.add(Error("You're not the room master!".to_string()).send_self());
+ } else if !(1..=max_hedgehogs).contains(&number) {
+ response
+ .add(HedgehogsNumber(team.name.clone(), team.hedgehogs_number).send_self());
+ } else {
+ team.hedgehogs_number = number;
+ response.add(
+ HedgehogsNumber(team.name.clone(), number)
+ .send_all()
+ .in_room(room_id)
+ .but_self(),
+ );
+ }
+ } else {
+ response.add(Warning("No such team.".to_string()).send_self());
+ }
+ }
+ 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());
+ } else {
+ team.color = color;
+ response.add(
+ TeamColor(team.name.clone(), color)
+ .send_all()
+ .in_room(room_id)
+ .but_self(),
+ );
+ server.clients[owner].clan = Some(color);
+ }
+ } else {
+ response.add(Warning("No such team.".to_string()).send_self());
+ }
+ }
+ Cfg(cfg) => {
+ if room.is_fixed() {
+ response.add(Warning("Access denied.".to_string()).send_self());
+ } else if !client.is_master() {
+ response.add(Error("You're not the room master!".to_string()).send_self());
+ } 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);
+ }
+ }
+ Save(name, location) => {
+ response.add(
+ server_chat(format!("Room config saved as {}", name))
+ .send_all()
+ .in_room(room_id),
+ );
+ room.save_config(name, location);
+ }
+ #[cfg(feature = "official-server")]
+ SaveRoom(filename) => {
+ if client.is_admin() {
+ match room.get_saves() {
+ Ok(contents) => response.request_io(super::IoTask::SaveRoom {
+ room_id,
+ filename,
+ contents,
+ }),
+ Err(e) => {
+ warn!("Error while serializing the room configs: {}", e);
+ response.add(
+ Warning("Unable to serialize the room configs.".to_string())
+ .send_self(),
+ )
+ }
+ }
+ }
+ }
+ #[cfg(feature = "official-server")]
+ LoadRoom(filename) => {
+ if client.is_admin() {
+ response.request_io(super::IoTask::LoadRoom { room_id, filename });
+ }
+ }
+ Delete(name) => {
+ if !room.delete_config(&name) {
+ response.add(Warning(format!("Save doesn't exist: {}", name)).send_self());
+ } else {
+ response.add(
+ server_chat(format!("Room config {} has been deleted", name))
+ .send_all()
+ .in_room(room_id),
+ );
+ }
+ }
+ CallVote(None) => {
+ 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 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<_> = server.rooms[room_id].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())
+ }
+ }
+ 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, server.room_clients(client_id).collect());
+ let room = &mut server.rooms[room_id];
+ room.voting = Some(voting);
+ response.add(server_chat(msg).send_all().in_room(room_id));
+ super::common::submit_vote(
+ 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(
+ server,
+ types::Vote {
+ is_pro: vote,
+ is_forced: false,
+ },
+ response,
+ );
+ }
+ ForceVote(vote) => {
+ let is_forced = client.is_admin();
+ super::common::submit_vote(
+ server,
+ types::Vote {
+ is_pro: vote,
+ is_forced,
+ },
+ response,
+ );
+ }
+ ToggleRestrictJoin | ToggleRestrictTeams | ToggleRegisteredOnly => {
+ if client.is_master() {
+ room.flags.toggle(room_message_flag(&message));
+ super::common::get_room_update(None, room, Some(&client), response);
+ }
+ }
+ StartGame => {
+ super::common::start_game(server, room_id, response);
+ }
+ EngineMessage(em) => {
+ if client.teams_in_game > 0 {
+ let decoding = decode(&em[..]).unwrap();
+ let messages = by_msg(&decoding);
+ let valid = messages.filter(|m| is_msg_valid(m, &client.team_indices));
+ let non_empty = valid.clone().filter(|m| !is_msg_empty(m));
+ let sync_msg = valid.clone().filter(|m| is_msg_timed(m)).last().map(|m| {
+ if is_msg_empty(m) {
+ Some(encode(m))
+ } else {
+ None
+ }
+ });
+
+ let em_response = encode(&valid.flat_map(|msg| msg).cloned().collect::<Vec<_>>());
+ if !em_response.is_empty() {
+ response.add(
+ ForwardEngineMessage(vec![em_response])
+ .send_all()
+ .in_room(room.id)
+ .but_self(),
+ );
+ }
+ let em_log = encode(&non_empty.flat_map(|msg| msg).cloned().collect::<Vec<_>>());
+ if let Some(ref mut info) = room.game_info {
+ if !em_log.is_empty() {
+ info.msg_log.push(em_log);
+ }
+ if let Some(msg) = sync_msg {
+ info.sync_msg = msg;
+ }
+ }
+ }
+ }
+ RoundFinished => {
+ let mut game_ended = false;
+ if client.is_in_game() {
+ client.set_is_in_game(false);
+ response.add(
+ ClientFlags(remove_flags(&[Flags::InGame]), vec![client.nick.clone()])
+ .send_all()
+ .in_room(room.id),
+ );
+ 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;
+ if info.teams_in_game == 0 {
+ game_ended = true;
+ }
+
+ for team_name in team_names {
+ 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(),
+ );
+
+ 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());
+ }
+ if info.sync_msg.is_some() {
+ 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(),
+ );
+ }
+ }
+ }
+ if game_ended {
+ super::common::end_game(server, room_id, response)
+ }
+ }
+ Rnd(v) => {
+ let result = rnd_reply(&v);
+ let mut echo = vec!["/rnd".to_string()];
+ echo.extend(v.into_iter());
+ let chat_msg = ChatMsg {
+ nick: server.clients[client_id].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) => {
+ 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(),
+ )
+ } 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()),
+ Some((id, _)) => {
+ super::common::change_master(server, room_id, id, response);
+ }
+ }
+ }
+ }
+ _ => warn!("Unimplemented!"),
+ }
+}
--- a/rust/hedgewars-server/src/main.rs Tue May 28 17:49:04 2019 +0200
+++ b/rust/hedgewars-server/src/main.rs Tue May 28 19:04:18 2019 +0300
@@ -6,7 +6,9 @@
use mio::{net::*, *};
use std::{env, str::FromStr as _, time::Duration};
+mod core;
mod protocol;
+mod handlers;
mod server;
mod utils;
--- a/rust/hedgewars-server/src/protocol.rs Tue May 28 17:49:04 2019 +0200
+++ b/rust/hedgewars-server/src/protocol.rs Tue May 28 19:04:18 2019 +0300
@@ -1,4 +1,4 @@
-use crate::protocol::parser::message;
+use self::parser::message;
use log::*;
use netbuf;
use nom::{Err, ErrorKind, IResult};
--- a/rust/hedgewars-server/src/protocol/messages.rs Tue May 28 17:49:04 2019 +0200
+++ b/rust/hedgewars-server/src/protocol/messages.rs Tue May 28 19:04:18 2019 +0300
@@ -1,4 +1,4 @@
-use crate::server::coretypes::{GameCfg, HedgehogInfo, ServerVar, TeamInfo, VoteType};
+use crate::core::types::{GameCfg, HedgehogInfo, ServerVar, TeamInfo, VoteType};
use std::{convert::From, iter::once, ops};
#[derive(PartialEq, Eq, Clone, Debug)]
@@ -183,7 +183,7 @@
impl GameCfg {
pub fn to_protocol(&self) -> (String, Vec<String>) {
- use crate::server::coretypes::GameCfg::*;
+ use crate::core::types::GameCfg::*;
match self {
FeatureSize(s) => ("FEATURE_SIZE".to_string(), vec![s.to_string()]),
MapType(t) => ("MAP".to_string(), vec![t.to_string()]),
--- a/rust/hedgewars-server/src/protocol/parser.rs Tue May 28 17:49:04 2019 +0200
+++ b/rust/hedgewars-server/src/protocol/parser.rs Tue May 28 19:04:18 2019 +0300
@@ -14,8 +14,10 @@
str::{FromStr, Utf8Error},
};
-use super::messages::{HWProtocolMessage, HWProtocolMessage::*};
-use crate::server::coretypes::{
+use super::{
+ messages::{HWProtocolMessage, HWProtocolMessage::*},
+};
+use crate::core::types::{
GameCfg, HedgehogInfo, ServerVar, TeamInfo, VoteType, MAX_HEDGEHOGS_PER_TEAM,
};
--- a/rust/hedgewars-server/src/protocol/test.rs Tue May 28 17:49:04 2019 +0200
+++ b/rust/hedgewars-server/src/protocol/test.rs Tue May 28 19:04:18 2019 +0300
@@ -4,7 +4,7 @@
test_runner::{Reason, TestRunner},
};
-use crate::server::coretypes::{GameCfg, HedgehogInfo, ServerVar, ServerVar::*, TeamInfo};
+use crate::core::types::{GameCfg, HedgehogInfo, ServerVar, ServerVar::*, TeamInfo};
use super::messages::{HWProtocolMessage, HWProtocolMessage::*};
@@ -75,7 +75,7 @@
type Parameters = ();
fn arbitrary_with(_args: <Self as Arbitrary>::Parameters) -> <Self as Arbitrary>::Strategy {
- use crate::server::coretypes::GameCfg::*;
+ use crate::core::types::GameCfg::*;
(0..10)
.no_shrink()
.prop_flat_map(|i| {
--- a/rust/hedgewars-server/src/server.rs Tue May 28 17:49:04 2019 +0200
+++ b/rust/hedgewars-server/src/server.rs Tue May 28 19:04:18 2019 +0300
@@ -1,12 +1,5 @@
-mod actions;
-pub mod client;
-pub mod core;
-pub mod coretypes;
#[cfg(feature = "official-server")]
mod database;
-mod handlers;
-pub mod indexslab;
#[cfg(feature = "official-server")]
pub mod io;
-pub mod network;
-pub mod room;
+pub mod network;
\ No newline at end of file
--- a/rust/hedgewars-server/src/server/actions.rs Tue May 28 17:49:04 2019 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,119 +0,0 @@
-use super::{
- client::HWClient,
- core::HWServer,
- coretypes::{ClientId, GameCfg, RoomId, VoteType},
- handlers,
- room::HWRoom,
- room::{GameInfo, RoomFlags},
-};
-use crate::{
- protocol::messages::{server_chat, HWProtocolMessage, HWServerMessage, HWServerMessage::*},
- utils::to_engine_msg,
-};
-use rand::{distributions::Uniform, thread_rng, Rng};
-use std::{io, io::Write, iter::once, mem::replace};
-
-#[cfg(feature = "official-server")]
-use super::database;
-
-pub enum DestinationGroup {
- All,
- Lobby,
- Room(RoomId),
- Protocol(u16),
-}
-
-pub enum Destination {
- ToId(ClientId),
- ToIds(Vec<ClientId>),
- ToSelf,
- ToAll {
- group: DestinationGroup,
- skip_self: bool,
- },
-}
-
-pub struct PendingMessage {
- pub destination: Destination,
- pub message: HWServerMessage,
-}
-
-impl PendingMessage {
- pub fn send(message: HWServerMessage, client_id: ClientId) -> PendingMessage {
- PendingMessage {
- destination: Destination::ToId(client_id),
- message,
- }
- }
-
- pub fn send_many(message: HWServerMessage, client_ids: Vec<ClientId>) -> PendingMessage {
- PendingMessage {
- destination: Destination::ToIds(client_ids),
- message,
- }
- }
-
- pub fn send_self(message: HWServerMessage) -> PendingMessage {
- PendingMessage {
- destination: Destination::ToSelf,
- message,
- }
- }
-
- pub fn send_all(message: HWServerMessage) -> PendingMessage {
- let destination = Destination::ToAll {
- group: DestinationGroup::All,
- skip_self: false,
- };
- PendingMessage {
- destination,
- message,
- }
- }
-
- pub fn in_room(mut self, clients_room_id: RoomId) -> PendingMessage {
- if let Destination::ToAll { ref mut group, .. } = self.destination {
- *group = DestinationGroup::Room(clients_room_id)
- }
- self
- }
-
- pub fn in_lobby(mut self) -> PendingMessage {
- if let Destination::ToAll { ref mut group, .. } = self.destination {
- *group = DestinationGroup::Lobby
- }
- self
- }
-
- pub fn with_protocol(mut self, protocol_number: u16) -> PendingMessage {
- if let Destination::ToAll { ref mut group, .. } = self.destination {
- *group = DestinationGroup::Protocol(protocol_number)
- }
- self
- }
-
- pub fn but_self(mut self) -> PendingMessage {
- if let Destination::ToAll {
- ref mut skip_self, ..
- } = self.destination
- {
- *skip_self = true
- }
- self
- }
-}
-
-impl HWServerMessage {
- pub fn send(self, client_id: ClientId) -> PendingMessage {
- PendingMessage::send(self, client_id)
- }
- pub fn send_many(self, client_ids: Vec<ClientId>) -> PendingMessage {
- PendingMessage::send_many(self, client_ids)
- }
- pub fn send_self(self) -> PendingMessage {
- PendingMessage::send_self(self)
- }
- pub fn send_all(self) -> PendingMessage {
- PendingMessage::send_all(self)
- }
-}
--- a/rust/hedgewars-server/src/server/client.rs Tue May 28 17:49:04 2019 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,109 +0,0 @@
-use super::coretypes::ClientId;
-use bitflags::*;
-
-bitflags! {
- pub struct ClientFlags: u16 {
- const IS_ADMIN = 0b0000_0001;
- const IS_MASTER = 0b0000_0010;
- const IS_READY = 0b0000_0100;
- const IS_IN_GAME = 0b0000_1000;
- const IS_JOINED_MID_GAME = 0b0001_0000;
- const IS_CHECKER = 0b0010_0000;
- const IS_CONTRIBUTOR = 0b0100_0000;
- const HAS_SUPER_POWER = 0b1000_0000;
- const IS_REGISTERED = 0b0001_0000_0000;
-
- const NONE = 0b0000_0000;
- const DEFAULT = Self::NONE.bits;
- }
-}
-
-pub struct HWClient {
- pub id: ClientId,
- pub room_id: Option<usize>,
- pub nick: String,
- pub protocol_number: u16,
- pub flags: ClientFlags,
- pub teams_in_game: u8,
- pub team_indices: Vec<u8>,
- pub clan: Option<u8>,
-}
-
-impl HWClient {
- pub fn new(id: ClientId, protocol_number: u16, nick: String) -> HWClient {
- HWClient {
- id,
- nick,
- protocol_number,
- room_id: None,
- flags: ClientFlags::DEFAULT,
- teams_in_game: 0,
- team_indices: Vec::new(),
- clan: None,
- }
- }
-
- fn contains(&self, mask: ClientFlags) -> bool {
- self.flags.contains(mask)
- }
-
- fn set(&mut self, mask: ClientFlags, value: bool) {
- self.flags.set(mask, value);
- }
-
- pub fn is_admin(&self) -> bool {
- self.contains(ClientFlags::IS_ADMIN)
- }
- pub fn is_master(&self) -> bool {
- self.contains(ClientFlags::IS_MASTER)
- }
- pub fn is_ready(&self) -> bool {
- self.contains(ClientFlags::IS_READY)
- }
- pub fn is_in_game(&self) -> bool {
- self.contains(ClientFlags::IS_IN_GAME)
- }
- pub fn is_joined_mid_game(&self) -> bool {
- self.contains(ClientFlags::IS_JOINED_MID_GAME)
- }
- pub fn is_checker(&self) -> bool {
- self.contains(ClientFlags::IS_CHECKER)
- }
- pub fn is_contributor(&self) -> bool {
- self.contains(ClientFlags::IS_CONTRIBUTOR)
- }
- pub fn has_super_power(&self) -> bool {
- self.contains(ClientFlags::HAS_SUPER_POWER)
- }
- pub fn is_registered(&self) -> bool {
- self.contains(ClientFlags::IS_REGISTERED)
- }
-
- pub fn set_is_admin(&mut self, value: bool) {
- self.set(ClientFlags::IS_ADMIN, value)
- }
- pub fn set_is_master(&mut self, value: bool) {
- self.set(ClientFlags::IS_MASTER, value)
- }
- pub fn set_is_ready(&mut self, value: bool) {
- self.set(ClientFlags::IS_READY, value)
- }
- pub fn set_is_in_game(&mut self, value: bool) {
- self.set(ClientFlags::IS_IN_GAME, value)
- }
- pub fn set_is_joined_mid_game(&mut self, value: bool) {
- self.set(ClientFlags::IS_JOINED_MID_GAME, value)
- }
- pub fn set_is_checker(&mut self, value: bool) {
- self.set(ClientFlags::IS_CHECKER, value)
- }
- pub fn set_is_contributor(&mut self, value: bool) {
- self.set(ClientFlags::IS_CONTRIBUTOR, value)
- }
- pub fn set_has_super_power(&mut self, value: bool) {
- self.set(ClientFlags::HAS_SUPER_POWER, value)
- }
- pub fn set_is_registered(&mut self, value: bool) {
- self.set(ClientFlags::IS_REGISTERED, value)
- }
-}
--- a/rust/hedgewars-server/src/server/core.rs Tue May 28 17:49:04 2019 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,283 +0,0 @@
-use super::{
- client::HWClient,
- coretypes::{ClientId, RoomId},
- indexslab::IndexSlab,
- room::HWRoom,
-};
-use crate::utils;
-
-use crate::protocol::messages::HWProtocolMessage::Greeting;
-use bitflags::*;
-use log::*;
-use slab;
-use std::{borrow::BorrowMut, iter, num::NonZeroU16};
-
-type Slab<T> = slab::Slab<T>;
-
-pub struct HWAnteClient {
- pub nick: Option<String>,
- pub protocol_number: Option<NonZeroU16>,
- pub server_salt: String,
- pub is_checker: bool,
-}
-
-pub struct HWAnteroom {
- pub clients: IndexSlab<HWAnteClient>,
-}
-
-impl HWAnteroom {
- pub fn new(clients_limit: usize) -> Self {
- let clients = IndexSlab::with_capacity(clients_limit);
- HWAnteroom { clients }
- }
-
- pub fn add_client(&mut self, client_id: ClientId, salt: String) {
- let client = HWAnteClient {
- nick: None,
- protocol_number: None,
- server_salt: salt,
- is_checker: false,
- };
- self.clients.insert(client_id, client);
- }
-
- pub fn remove_client(&mut self, client_id: ClientId) -> Option<HWAnteClient> {
- let mut client = self.clients.remove(client_id);
- client
- }
-}
-
-pub struct ServerGreetings {
- pub for_latest_protocol: String,
- pub for_old_protocols: String,
-}
-
-impl ServerGreetings {
- fn new() -> Self {
- Self {
- for_latest_protocol: "\u{1f994} is watching".to_string(),
- for_old_protocols: "\u{1f994} is watching".to_string(),
- }
- }
-}
-
-bitflags! {
- pub struct ServerFlags: u8 {
- const REGISTERED_ONLY = 0b0000_1000;
- }
-}
-
-pub struct HWServer {
- pub clients: IndexSlab<HWClient>,
- pub rooms: Slab<HWRoom>,
- pub anteroom: HWAnteroom,
- pub latest_protocol: u16,
- pub flags: ServerFlags,
- pub greetings: ServerGreetings,
-}
-
-impl HWServer {
- pub fn new(clients_limit: usize, rooms_limit: usize) -> Self {
- let rooms = Slab::with_capacity(rooms_limit);
- let clients = IndexSlab::with_capacity(clients_limit);
- Self {
- clients,
- rooms,
- anteroom: HWAnteroom::new(clients_limit),
- greetings: ServerGreetings::new(),
- latest_protocol: 58,
- flags: ServerFlags::empty(),
- }
- }
-
- pub fn add_client(&mut self, client_id: ClientId, data: HWAnteClient) {
- if let (Some(protocol), Some(nick)) = (data.protocol_number, data.nick) {
- let mut client = HWClient::new(client_id, protocol.get(), nick);
- client.set_is_checker(data.is_checker);
- self.clients.insert(client_id, client);
- }
- }
-
- pub fn remove_client(&mut self, client_id: ClientId) {
- self.clients.remove(client_id);
- }
-
- pub fn get_greetings(&self, client_id: ClientId) -> &str {
- if self.clients[client_id].protocol_number < self.latest_protocol {
- &self.greetings.for_old_protocols
- } else {
- &self.greetings.for_latest_protocol
- }
- }
-
- #[inline]
- pub fn create_room(
- &mut self,
- creator_id: ClientId,
- name: String,
- password: Option<String>,
- ) -> RoomId {
- create_room(
- &mut self.clients[creator_id],
- &mut self.rooms,
- name,
- password,
- )
- }
-
- #[inline]
- pub fn move_to_room(&mut self, client_id: ClientId, room_id: RoomId) {
- move_to_room(&mut self.clients[client_id], &mut self.rooms[room_id])
- }
-
- pub fn has_room(&self, name: &str) -> bool {
- self.find_room(name).is_some()
- }
-
- pub fn find_room(&self, name: &str) -> Option<&HWRoom> {
- self.rooms
- .iter()
- .find_map(|(_, r)| Some(r).filter(|r| r.name == name))
- }
-
- pub fn find_room_mut(&mut self, name: &str) -> Option<&mut HWRoom> {
- self.rooms
- .iter_mut()
- .find_map(|(_, r)| Some(r).filter(|r| r.name == name))
- }
-
- pub fn find_client(&self, nick: &str) -> Option<&HWClient> {
- self.clients
- .iter()
- .find_map(|(_, c)| Some(c).filter(|c| c.nick == nick))
- }
-
- pub fn find_client_mut(&mut self, nick: &str) -> Option<&mut HWClient> {
- self.clients
- .iter_mut()
- .find_map(|(_, c)| Some(c).filter(|c| c.nick == nick))
- }
-
- pub fn all_clients(&self) -> impl Iterator<Item = ClientId> + '_ {
- self.clients.iter().map(|(id, _)| id)
- }
-
- pub fn filter_clients<'a, F>(&'a self, f: F) -> impl Iterator<Item = ClientId> + 'a
- where
- F: Fn(&(usize, &HWClient)) -> bool + 'a,
- {
- self.clients.iter().filter(f).map(|(_, c)| c.id)
- }
-
- pub fn filter_rooms<'a, F>(&'a self, f: F) -> impl Iterator<Item = RoomId> + 'a
- where
- F: Fn(&(usize, &HWRoom)) -> bool + 'a,
- {
- self.rooms.iter().filter(f).map(|(_, c)| c.id)
- }
-
- pub fn collect_clients<F>(&self, f: F) -> Vec<ClientId>
- where
- F: Fn(&(usize, &HWClient)) -> bool,
- {
- self.filter_clients(f).collect()
- }
-
- pub fn collect_nicks<F>(&self, f: F) -> Vec<String>
- where
- F: Fn(&(usize, &HWClient)) -> bool,
- {
- self.clients
- .iter()
- .filter(f)
- .map(|(_, c)| c.nick.clone())
- .collect()
- }
-
- pub fn lobby_clients(&self) -> impl Iterator<Item = ClientId> + '_ {
- self.filter_clients(|(_, c)| c.room_id == None)
- }
-
- pub fn room_clients(&self, room_id: RoomId) -> impl Iterator<Item = ClientId> + '_ {
- self.filter_clients(move |(_, c)| c.room_id == Some(room_id))
- }
-
- pub fn protocol_clients(&self, protocol: u16) -> impl Iterator<Item = ClientId> + '_ {
- self.filter_clients(move |(_, c)| c.protocol_number == protocol)
- }
-
- pub fn protocol_rooms(&self, protocol: u16) -> impl Iterator<Item = RoomId> + '_ {
- self.filter_rooms(move |(_, r)| r.protocol_number == protocol)
- }
-
- pub fn other_clients_in_room(&self, self_id: ClientId) -> Vec<ClientId> {
- let room_id = self.clients[self_id].room_id;
- self.collect_clients(|(id, c)| *id != self_id && c.room_id == room_id)
- }
-
- pub fn is_registered_only(&self) -> bool {
- self.flags.contains(ServerFlags::REGISTERED_ONLY)
- }
-
- pub fn set_is_registered_only(&mut self, value: bool) {
- self.flags.set(ServerFlags::REGISTERED_ONLY, value)
- }
-}
-
-fn allocate_room(rooms: &mut Slab<HWRoom>) -> &mut HWRoom {
- let entry = rooms.vacant_entry();
- let room = HWRoom::new(entry.key());
- entry.insert(room)
-}
-
-fn create_room(
- client: &mut HWClient,
- rooms: &mut Slab<HWRoom>,
- name: String,
- password: Option<String>,
-) -> RoomId {
- let room = allocate_room(rooms);
-
- room.master_id = Some(client.id);
- room.name = name;
- room.password = password;
- room.protocol_number = client.protocol_number;
-
- room.players_number = 1;
- room.ready_players_number = 1;
-
- client.room_id = Some(room.id);
- client.set_is_master(true);
- client.set_is_ready(true);
- client.set_is_joined_mid_game(false);
-
- room.id
-}
-
-fn move_to_room(client: &mut HWClient, room: &mut HWRoom) {
- debug_assert!(client.room_id != Some(room.id));
-
- room.players_number += 1;
-
- client.room_id = Some(room.id);
- client.set_is_joined_mid_game(room.game_info.is_some());
- client.set_is_in_game(room.game_info.is_some());
-
- if let Some(ref mut info) = room.game_info {
- let teams = info.client_teams(client.id);
- client.teams_in_game = teams.clone().count() as u8;
- client.clan = teams.clone().next().map(|t| t.color);
- let team_names: Vec<_> = teams.map(|t| t.name.clone()).collect();
-
- if !team_names.is_empty() {
- info.left_teams.retain(|name| !team_names.contains(&name));
- info.teams_in_game += team_names.len() as u8;
- room.teams = info
- .teams_at_start
- .iter()
- .filter(|(_, t)| !team_names.contains(&t.name))
- .cloned()
- .collect();
- }
- }
-}
--- a/rust/hedgewars-server/src/server/coretypes.rs Tue May 28 17:49:04 2019 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,193 +0,0 @@
-use serde_derive::{Deserialize, Serialize};
-
-pub type ClientId = usize;
-pub type RoomId = usize;
-
-pub const MAX_HEDGEHOGS_PER_TEAM: u8 = 8;
-
-#[derive(PartialEq, Eq, Clone, Debug)]
-pub enum ServerVar {
- MOTDNew(String),
- MOTDOld(String),
- LatestProto(u16),
-}
-
-#[derive(PartialEq, Eq, Clone, Debug)]
-pub enum GameCfg {
- FeatureSize(u32),
- MapType(String),
- MapGenerator(u32),
- MazeSize(u32),
- Seed(String),
- Template(u32),
-
- Ammo(String, Option<String>),
- Scheme(String, Vec<String>),
- Script(String),
- Theme(String),
- DrawnMap(String),
-}
-
-#[derive(PartialEq, Eq, Clone, Debug)]
-pub struct TeamInfo {
- pub owner: String,
- pub name: String,
- pub color: u8,
- pub grave: String,
- pub fort: String,
- pub voice_pack: String,
- pub flag: String,
- pub difficulty: u8,
- pub hedgehogs_number: u8,
- pub hedgehogs: [HedgehogInfo; MAX_HEDGEHOGS_PER_TEAM as usize],
-}
-
-#[derive(PartialEq, Eq, Clone, Debug)]
-pub struct HedgehogInfo {
- pub name: String,
- pub hat: String,
-}
-
-#[derive(Clone, Serialize, Deserialize)]
-pub struct Ammo {
- pub name: String,
- pub settings: Option<String>,
-}
-
-#[derive(Clone, Serialize, Deserialize)]
-pub struct Scheme {
- pub name: String,
- pub settings: Vec<String>,
-}
-
-#[derive(Clone, Serialize, Deserialize)]
-pub struct RoomConfig {
- pub feature_size: u32,
- pub map_type: String,
- pub map_generator: u32,
- pub maze_size: u32,
- pub seed: String,
- pub template: u32,
-
- pub ammo: Ammo,
- pub scheme: Scheme,
- pub script: String,
- pub theme: String,
- pub drawn_map: Option<String>,
-}
-
-impl RoomConfig {
- pub fn new() -> RoomConfig {
- RoomConfig {
- feature_size: 12,
- map_type: "+rnd+".to_string(),
- map_generator: 0,
- maze_size: 0,
- seed: "seed".to_string(),
- template: 0,
-
- ammo: Ammo {
- name: "Default".to_string(),
- settings: None,
- },
- scheme: Scheme {
- name: "Default".to_string(),
- settings: Vec::new(),
- },
- script: "Normal".to_string(),
- theme: "\u{1f994}".to_string(),
- drawn_map: None,
- }
- }
-
- pub fn set_config(&mut self, cfg: GameCfg) {
- match cfg {
- GameCfg::FeatureSize(s) => self.feature_size = s,
- GameCfg::MapType(t) => self.map_type = t,
- GameCfg::MapGenerator(g) => self.map_generator = g,
- GameCfg::MazeSize(s) => self.maze_size = s,
- GameCfg::Seed(s) => self.seed = s,
- GameCfg::Template(t) => self.template = t,
-
- GameCfg::Ammo(n, s) => {
- self.ammo = Ammo {
- name: n,
- settings: s,
- }
- }
- GameCfg::Scheme(n, s) => {
- self.scheme = Scheme {
- name: n,
- settings: s,
- }
- }
- GameCfg::Script(s) => self.script = s,
- GameCfg::Theme(t) => self.theme = t,
- GameCfg::DrawnMap(m) => self.drawn_map = Some(m),
- };
- }
-
- pub fn to_map_config(&self) -> Vec<String> {
- vec![
- self.feature_size.to_string(),
- self.map_type.to_string(),
- self.map_generator.to_string(),
- self.maze_size.to_string(),
- self.seed.to_string(),
- self.template.to_string(),
- ]
- }
-
- pub fn to_game_config(&self) -> Vec<GameCfg> {
- use crate::server::coretypes::GameCfg::*;
- let mut v = vec![
- Ammo(self.ammo.name.to_string(), self.ammo.settings.clone()),
- Scheme(self.scheme.name.to_string(), self.scheme.settings.clone()),
- Script(self.script.to_string()),
- Theme(self.theme.to_string()),
- ];
- if let Some(ref m) = self.drawn_map {
- v.push(DrawnMap(m.to_string()))
- }
- v
- }
-}
-
-pub struct Replay {
- pub config: RoomConfig,
- pub teams: Vec<TeamInfo>,
- pub message_log: Vec<String>,
-}
-
-#[derive(PartialEq, Eq, Clone, Debug)]
-pub enum VoteType {
- Kick(String),
- Map(Option<String>),
- Pause,
- NewSeed,
- HedgehogsPerTeam(u8),
-}
-
-pub struct Vote {
- pub is_pro: bool,
- pub is_forced: bool,
-}
-
-#[derive(Clone, Debug)]
-pub struct Voting {
- pub ttl: u32,
- pub voters: Vec<ClientId>,
- pub votes: Vec<(ClientId, bool)>,
- pub kind: VoteType,
-}
-
-impl Voting {
- pub fn new(kind: VoteType, voters: Vec<ClientId>) -> Voting {
- Voting {
- kind,
- voters,
- ttl: 2,
- votes: Vec::new(),
- }
- }
-}
--- a/rust/hedgewars-server/src/server/database.rs Tue May 28 17:49:04 2019 +0200
+++ b/rust/hedgewars-server/src/server/database.rs Tue May 28 19:04:18 2019 +0300
@@ -2,8 +2,9 @@
use mysql::{error::DriverError, error::Error, from_row_opt, params};
use openssl::sha::sha1;
-use super::handlers::AccountInfo;
-use crate::server::handlers::Sha1Digest;
+use crate::{
+ handlers::{Sha1Digest, AccountInfo}
+};
const GET_ACCOUNT_QUERY: &str =
r"SELECT CASE WHEN users.status = 1 THEN users.pass ELSE '' END,
--- a/rust/hedgewars-server/src/server/handlers.rs Tue May 28 17:49:04 2019 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,396 +0,0 @@
-use mio;
-use std::{collections::HashMap, io, io::Write};
-
-use super::{
- actions::{Destination, DestinationGroup},
- core::HWServer,
- coretypes::{ClientId, Replay, RoomId},
- room::RoomSave,
-};
-use crate::{
- protocol::messages::{server_chat, HWProtocolMessage, HWServerMessage, HWServerMessage::*},
- server::actions::PendingMessage,
- utils,
-};
-use base64::encode;
-use log::*;
-use rand::{thread_rng, RngCore};
-
-mod checker;
-mod common;
-mod inroom;
-mod lobby;
-mod loggingin;
-
-use self::loggingin::LoginResult;
-use crate::protocol::messages::global_chat;
-use crate::protocol::messages::HWProtocolMessage::EngineMessage;
-use crate::server::coretypes::{GameCfg, TeamInfo};
-use std::fmt::{Formatter, LowerHex};
-
-#[derive(PartialEq)]
-pub struct Sha1Digest([u8; 20]);
-
-impl Sha1Digest {
- pub fn new(digest: [u8; 20]) -> Self {
- Self(digest)
- }
-}
-
-impl LowerHex for Sha1Digest {
- fn fmt(&self, f: &mut Formatter) -> Result<(), std::fmt::Error> {
- for byte in &self.0 {
- write!(f, "{:02x}", byte)?;
- }
- Ok(())
- }
-}
-
-pub struct AccountInfo {
- pub is_registered: bool,
- pub is_admin: bool,
- pub is_contributor: bool,
- pub server_hash: Sha1Digest,
-}
-
-pub enum IoTask {
- GetAccount {
- nick: String,
- protocol: u16,
- password_hash: String,
- client_salt: String,
- server_salt: String,
- },
- GetReplay {
- id: u32,
- },
- SaveRoom {
- room_id: RoomId,
- filename: String,
- contents: String,
- },
- LoadRoom {
- room_id: RoomId,
- filename: String,
- },
-}
-
-pub enum IoResult {
- Account(Option<AccountInfo>),
- Replay(Option<Replay>),
- SaveRoom(RoomId, bool),
- LoadRoom(RoomId, Option<String>),
-}
-
-pub struct Response {
- client_id: ClientId,
- messages: Vec<PendingMessage>,
- io_tasks: Vec<IoTask>,
- removed_clients: Vec<ClientId>,
-}
-
-impl Response {
- pub fn new(client_id: ClientId) -> Self {
- Self {
- client_id,
- messages: vec![],
- io_tasks: vec![],
- removed_clients: vec![],
- }
- }
-
- #[inline]
- pub fn is_empty(&self) -> bool {
- self.messages.is_empty() && self.removed_clients.is_empty() && self.io_tasks.is_empty()
- }
-
- #[inline]
- pub fn len(&self) -> usize {
- self.messages.len()
- }
-
- #[inline]
- pub fn client_id(&self) -> ClientId {
- self.client_id
- }
-
- #[inline]
- pub fn add(&mut self, message: PendingMessage) {
- self.messages.push(message)
- }
-
- #[inline]
- pub fn request_io(&mut self, task: IoTask) {
- self.io_tasks.push(task)
- }
-
- pub fn extract_messages<'a, 'b: 'a>(
- &'b mut self,
- server: &'a HWServer,
- ) -> impl Iterator<Item = (Vec<ClientId>, HWServerMessage)> + 'a {
- let client_id = self.client_id;
- self.messages.drain(..).map(move |m| {
- let ids = get_recipients(server, client_id, m.destination);
- (ids, m.message)
- })
- }
-
- pub fn remove_client(&mut self, client_id: ClientId) {
- self.removed_clients.push(client_id);
- }
-
- pub fn extract_removed_clients(&mut self) -> impl Iterator<Item = ClientId> + '_ {
- self.removed_clients.drain(..)
- }
-
- pub fn extract_io_tasks(&mut self) -> impl Iterator<Item = IoTask> + '_ {
- self.io_tasks.drain(..)
- }
-}
-
-impl Extend<PendingMessage> for Response {
- fn extend<T: IntoIterator<Item = PendingMessage>>(&mut self, iter: T) {
- for msg in iter {
- self.add(msg)
- }
- }
-}
-
-fn get_recipients(
- server: &HWServer,
- client_id: ClientId,
- destination: Destination,
-) -> Vec<ClientId> {
- match destination {
- Destination::ToSelf => vec![client_id],
- Destination::ToId(id) => vec![id],
- Destination::ToIds(ids) => ids,
- Destination::ToAll { group, skip_self } => {
- let mut ids: Vec<_> = match group {
- DestinationGroup::All => server.all_clients().collect(),
- DestinationGroup::Lobby => server.lobby_clients().collect(),
- DestinationGroup::Protocol(proto) => server.protocol_clients(proto).collect(),
- DestinationGroup::Room(id) => server.room_clients(id).collect(),
- };
-
- if skip_self {
- if let Some(index) = ids.iter().position(|id| *id == client_id) {
- ids.remove(index);
- }
- }
-
- ids
- }
- }
-}
-
-pub fn handle(
- server: &mut HWServer,
- client_id: ClientId,
- response: &mut Response,
- message: HWProtocolMessage,
-) {
- match message {
- HWProtocolMessage::Ping => response.add(Pong.send_self()),
- _ => {
- if server.anteroom.clients.contains(client_id) {
- match loggingin::handle(server, client_id, response, message) {
- LoginResult::Unchanged => (),
- LoginResult::Complete => {
- if let Some(client) = server.anteroom.remove_client(client_id) {
- server.add_client(client_id, client);
- common::join_lobby(server, response);
- }
- }
- LoginResult::Exit => {
- server.anteroom.remove_client(client_id);
- response.remove_client(client_id);
- }
- }
- } else if server.clients.contains(client_id) {
- match message {
- HWProtocolMessage::Quit(Some(msg)) => {
- common::remove_client(server, response, "User quit: ".to_string() + &msg);
- }
- HWProtocolMessage::Quit(None) => {
- common::remove_client(server, response, "User quit".to_string());
- }
- HWProtocolMessage::Info(nick) => {
- if let Some(client) = server.find_client(&nick) {
- let admin_sign = if client.is_admin() { "@" } else { "" };
- let master_sign = if client.is_master() { "+" } else { "" };
- let room_info = match client.room_id {
- Some(room_id) => {
- let room = &server.rooms[room_id];
- let status = match room.game_info {
- Some(_) if client.teams_in_game == 0 => "(spectating)",
- Some(_) => "(playing)",
- None => "",
- };
- format!(
- "[{}{}room {}]{}",
- admin_sign, master_sign, room.name, status
- )
- }
- None => format!("[{}lobby]", admin_sign),
- };
-
- let info = vec![
- client.nick.clone(),
- "[]".to_string(),
- utils::protocol_version_string(client.protocol_number).to_string(),
- room_info,
- ];
- response.add(Info(info).send_self())
- } else {
- response
- .add(server_chat("Player is not online.".to_string()).send_self())
- }
- }
- HWProtocolMessage::ToggleServerRegisteredOnly => {
- if !server.clients[client_id].is_admin() {
- response.add(Warning("Access denied.".to_string()).send_self());
- } else {
- server.set_is_registered_only(server.is_registered_only());
- let msg = if server.is_registered_only() {
- "This server no longer allows unregistered players to join."
- } else {
- "This server now allows unregistered players to join."
- };
- response.add(server_chat(msg.to_string()).send_all());
- }
- }
- HWProtocolMessage::Global(msg) => {
- if !server.clients[client_id].is_admin() {
- response.add(Warning("Access denied.".to_string()).send_self());
- } else {
- response.add(global_chat(msg).send_all())
- }
- }
- HWProtocolMessage::SuperPower => {
- if !server.clients[client_id].is_admin() {
- response.add(Warning("Access denied.".to_string()).send_self());
- } else {
- server.clients[client_id].set_has_super_power(true);
- response
- .add(server_chat("Super power activated.".to_string()).send_self())
- }
- }
- HWProtocolMessage::Watch(id) => {
- #[cfg(feature = "official-server")]
- {
- response.request_io(IoTask::GetReplay { id })
- }
-
- #[cfg(not(feature = "official-server"))]
- {
- response.add(
- Warning("This server does not support replays!".to_string())
- .send_self(),
- );
- }
- }
- _ => match server.clients[client_id].room_id {
- None => lobby::handle(server, client_id, response, message),
- Some(room_id) => {
- inroom::handle(server, client_id, response, room_id, message)
- }
- },
- }
- }
- }
- }
-}
-
-pub fn handle_client_accept(server: &mut HWServer, client_id: ClientId, response: &mut Response) {
- let mut salt = [0u8; 18];
- thread_rng().fill_bytes(&mut salt);
-
- server.anteroom.add_client(client_id, encode(&salt));
-
- response.add(HWServerMessage::Connected(utils::SERVER_VERSION).send_self());
-}
-
-pub fn handle_client_loss(server: &mut HWServer, client_id: ClientId, response: &mut Response) {
- if server.anteroom.remove_client(client_id).is_none() {
- common::remove_client(server, response, "Connection reset".to_string());
- }
-}
-
-pub fn handle_io_result(
- server: &mut HWServer,
- client_id: ClientId,
- response: &mut Response,
- io_result: IoResult,
-) {
- match io_result {
- IoResult::Account(Some(info)) => {
- if !info.is_registered && server.is_registered_only() {
- response.add(
- Bye("This server only allows registered users to join.".to_string())
- .send_self(),
- );
- response.remove_client(client_id);
- } else {
- response.add(ServerAuth(format!("{:x}", info.server_hash)).send_self());
- if let Some(client) = server.anteroom.remove_client(client_id) {
- server.add_client(client_id, client);
- let client = &mut server.clients[client_id];
- client.set_is_registered(info.is_registered);
- client.set_is_admin(info.is_admin);
- client.set_is_contributor(info.is_admin)
- }
- }
- }
- IoResult::Account(None) => {
- response.add(Error("Authentication failed.".to_string()).send_self());
- response.remove_client(client_id);
- }
- IoResult::Replay(Some(replay)) => {
- let protocol = server.clients[client_id].protocol_number;
- let start_msg = if protocol < 58 {
- RoomJoined(vec![server.clients[client_id].nick.clone()])
- } else {
- ReplayStart
- };
- response.add(start_msg.send_self());
-
- common::get_room_config_impl(&replay.config, client_id, response);
- common::get_teams(replay.teams.iter(), client_id, response);
- response.add(RunGame.send_self());
- response.add(ForwardEngineMessage(replay.message_log).send_self());
-
- if protocol < 58 {
- response.add(Kicked.send_self());
- }
- }
- IoResult::Replay(None) => {
- response.add(Warning("Could't load the replay".to_string()).send_self())
- }
- IoResult::SaveRoom(_, true) => {
- response.add(server_chat("Room configs saved successfully.".to_string()).send_self());
- }
- IoResult::SaveRoom(_, false) => {
- response.add(Warning("Unable to save the room configs.".to_string()).send_self());
- }
- IoResult::LoadRoom(room_id, Some(contents)) => {
- if let Some(ref mut room) = server.rooms.get_mut(room_id) {
- match room.set_saves(&contents) {
- Ok(_) => response.add(
- server_chat("Room configs loaded successfully.".to_string()).send_self(),
- ),
- Err(e) => {
- warn!("Error while deserializing the room configs: {}", e);
- response.add(
- Warning("Unable to deserialize the room configs.".to_string())
- .send_self(),
- );
- }
- }
- }
- }
- IoResult::LoadRoom(_, None) => {
- response.add(Warning("Unable to load the room configs.".to_string()).send_self());
- }
- }
-}
--- a/rust/hedgewars-server/src/server/handlers/checker.rs Tue May 28 17:49:04 2019 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,13 +0,0 @@
-use log::*;
-use mio;
-
-use crate::{
- protocol::messages::HWProtocolMessage,
- server::{core::HWServer, coretypes::ClientId},
-};
-
-pub fn handle(_server: &mut HWServer, _client_id: ClientId, message: HWProtocolMessage) {
- match message {
- _ => warn!("Unknown command"),
- }
-}
--- a/rust/hedgewars-server/src/server/handlers/common.rs Tue May 28 17:49:04 2019 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,661 +0,0 @@
-use crate::{
- protocol::messages::server_chat,
- protocol::messages::{
- add_flags, remove_flags,
- HWProtocolMessage::{self, Rnd},
- HWServerMessage::{self, *},
- ProtocolFlags as Flags,
- },
- server::{
- client::HWClient,
- core::HWServer,
- coretypes::{ClientId, GameCfg, RoomId, TeamInfo, Vote, VoteType},
- room::HWRoom,
- },
- utils::to_engine_msg,
-};
-
-use super::Response;
-
-use crate::server::coretypes::RoomConfig;
-use rand::{self, seq::SliceRandom, thread_rng, Rng};
-use std::{iter::once, mem::replace};
-
-pub fn rnd_reply(options: &[String]) -> HWServerMessage {
- let mut rng = thread_rng();
-
- let reply = if options.is_empty() {
- (*&["heads", "tails"].choose(&mut rng).unwrap()).to_string()
- } else {
- options.choose(&mut rng).unwrap().clone()
- };
-
- ChatMsg {
- nick: "[random]".to_string(),
- msg: reply,
- }
-}
-
-pub fn join_lobby(server: &mut HWServer, response: &mut Response) {
- let client_id = response.client_id();
-
- let client = &server.clients[client_id];
- let nick = vec![client.nick.clone()];
- let mut flags = vec![];
- if client.is_registered() {
- flags.push(Flags::Registered)
- }
- if client.is_admin() {
- flags.push(Flags::Admin)
- }
- if client.is_contributor() {
- flags.push(Flags::Contributor)
- }
-
- let all_nicks: Vec<_> = server.collect_nicks(|_| true);
-
- let mut flag_selectors = [
- (
- Flags::Registered,
- server.collect_nicks(|(_, c)| c.is_registered()),
- ),
- (Flags::Admin, server.collect_nicks(|(_, c)| c.is_admin())),
- (
- Flags::Contributor,
- server.collect_nicks(|(_, c)| c.is_contributor()),
- ),
- (
- Flags::InRoom,
- server.collect_nicks(|(_, c)| c.room_id.is_some()),
- ),
- ];
-
- let server_msg = ServerMessage(server.get_greetings(client_id).to_string());
-
- let rooms_msg = Rooms(
- server
- .rooms
- .iter()
- .filter(|(_, r)| r.protocol_number == client.protocol_number)
- .flat_map(|(_, r)| r.info(r.master_id.map(|id| &server.clients[id])))
- .collect(),
- );
-
- response.add(LobbyJoined(nick).send_all().but_self());
- response.add(
- ClientFlags(add_flags(&flags), all_nicks.clone())
- .send_all()
- .but_self(),
- );
-
- response.add(LobbyJoined(all_nicks).send_self());
- for (flag, nicks) in &mut flag_selectors {
- if !nicks.is_empty() {
- response.add(ClientFlags(add_flags(&[*flag]), replace(nicks, vec![])).send_self());
- }
- }
-
- response.add(server_msg.send_self());
- response.add(rooms_msg.send_self());
-}
-
-pub fn remove_teams(
- room: &mut HWRoom,
- team_names: Vec<String>,
- 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,
- new_master_id: ClientId,
- response: &mut Response,
-) {
- let room = &mut server.rooms[room_id];
- if let Some(master_id) = room.master_id {
- server.clients[master_id].set_is_master(false);
- response.add(
- ClientFlags(
- remove_flags(&[Flags::RoomMaster]),
- vec![server.clients[master_id].nick.clone()],
- )
- .send_all()
- .in_room(room_id),
- )
- }
-
- room.master_id = Some(new_master_id);
- server.clients[new_master_id].set_is_master(true);
-
- response.add(
- ClientFlags(
- add_flags(&[Flags::RoomMaster]),
- vec![server.clients[new_master_id].nick.clone()],
- )
- .send_all()
- .in_room(room_id),
- );
-}
-
-pub fn enter_room(
- server: &mut HWServer,
- client_id: ClientId,
- room_id: RoomId,
- response: &mut Response,
-) {
- let nick = server.clients[client_id].nick.clone();
- server.move_to_room(client_id, room_id);
-
- response.add(RoomJoined(vec![nick.clone()]).send_all().in_room(room_id));
- response.add(ClientFlags(add_flags(&[Flags::InRoom]), vec![nick]).send_all());
- let nicks = server.collect_nicks(|(_, c)| c.room_id == Some(room_id));
- response.add(RoomJoined(nicks).send_self());
-
- get_room_teams(server, room_id, client_id, response);
-
- let room = &server.rooms[room_id];
- get_room_config(room, client_id, response);
-
- let mut flag_selectors = [
- (
- Flags::RoomMaster,
- server.collect_nicks(|(_, c)| c.is_master()),
- ),
- (Flags::Ready, server.collect_nicks(|(_, c)| c.is_ready())),
- (Flags::InGame, server.collect_nicks(|(_, c)| c.is_in_game())),
- ];
-
- for (flag, nicks) in &mut flag_selectors {
- response.add(ClientFlags(add_flags(&[*flag]), replace(nicks, vec![])).send_self());
- }
-
- if !room.greeting.is_empty() {
- response.add(
- ChatMsg {
- nick: "[greeting]".to_string(),
- msg: room.greeting.clone(),
- }
- .send_self(),
- );
- }
-}
-
-pub fn exit_room(server: &mut HWServer, client_id: ClientId, response: &mut Response, msg: &str) {
- let client = &mut server.clients[client_id];
-
- if let Some(room_id) = client.room_id {
- let room = &mut server.rooms[room_id];
-
- remove_client_from_room(client, room, response, msg);
-
- 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);
-
- 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);
-
- response.add(
- ClientFlags(add_flags(&[Flags::RoomMaster]), vec![new_master_nick])
- .send_all()
- .in_room(room.id),
- );
- }
- }
- }
- }
-}
-
-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 nick = client.nick.clone();
-
- exit_room(server, client_id, response, &msg);
-
- server.remove_client(client_id);
-
- response.add(LobbyLeft(nick, msg.to_string()).send_all());
- response.add(Bye("User quit: ".to_string() + &msg).send_self());
- response.remove_client(client_id);
-}
-
-pub fn get_room_update(
- room_name: Option<String>,
- room: &HWRoom,
- master: Option<&HWClient>,
- response: &mut Response,
-) {
- let update_msg = RoomUpdated(room_name.unwrap_or(room.name.clone()), room.info(master));
- response.add(update_msg.send_all().with_protocol(room.protocol_number));
-}
-
-pub fn get_room_config_impl(config: &RoomConfig, to_client: ClientId, response: &mut Response) {
- response.add(ConfigEntry("FULLMAPCONFIG".to_string(), config.to_map_config()).send(to_client));
- for cfg in config.to_game_config() {
- response.add(cfg.to_server_msg().send(to_client));
- }
-}
-
-pub fn get_room_config(room: &HWRoom, to_client: ClientId, response: &mut Response) {
- get_room_config_impl(room.active_config(), to_client, response);
-}
-
-pub fn get_teams<'a, I>(teams: I, to_client: ClientId, response: &mut Response)
-where
- I: Iterator<Item = &'a TeamInfo>,
-{
- for team in teams {
- response.add(TeamAdd(team.to_protocol()).send(to_client));
- response.add(TeamColor(team.name.clone(), team.color).send(to_client));
- response.add(HedgehogsNumber(team.name.clone(), team.hedgehogs_number).send(to_client));
- }
-}
-
-pub fn get_room_teams(
- server: &HWServer,
- room_id: RoomId,
- to_client: ClientId,
- response: &mut Response,
-) {
- let room = &server.rooms[room_id];
- let current_teams = match room.game_info {
- Some(ref info) => &info.teams_at_start,
- None => &room.teams,
- };
-
- get_teams(current_teams.iter().map(|(_, t)| t), to_client, response);
-}
-
-pub fn get_room_flags(
- server: &HWServer,
- room_id: RoomId,
- to_client: ClientId,
- response: &mut Response,
-) {
- let room = &server.rooms[room_id];
- if let Some(id) = room.master_id {
- response.add(
- ClientFlags(
- add_flags(&[Flags::RoomMaster]),
- vec![server.clients[id].nick.clone()],
- )
- .send(to_client),
- );
- }
- let nicks: Vec<_> = server
- .clients
- .iter()
- .filter(|(_, c)| c.room_id == Some(room_id) && c.is_ready())
- .map(|(_, c)| c.nick.clone())
- .collect();
- if !nicks.is_empty() {
- response.add(ClientFlags(add_flags(&[Flags::Ready]), nicks).send(to_client));
- }
-}
-
-pub fn apply_voting_result(
- server: &mut HWServer,
- room_id: RoomId,
- response: &mut Response,
- kind: VoteType,
-) {
- 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));
- exit_room(server, id, response, "kicked");
- }
- }
- }
- VoteType::Map(None) => (),
- VoteType::Map(Some(name)) => {
- if let Some(location) = server.rooms[room_id].load_config(&name) {
- response.add(
- server_chat(location.to_string())
- .send_all()
- .in_room(room_id),
- );
- let room = &server.rooms[room_id];
- let room_master = if let Some(id) = room.master_id {
- Some(&server.clients[id])
- } else {
- None
- };
- get_room_update(None, room, room_master, response);
-
- for (_, client) in server.clients.iter() {
- if client.room_id == Some(room_id) {
- super::common::get_room_config(&server.rooms[room_id], client.id, response);
- }
- }
- }
- }
- VoteType::Pause => {
- if let Some(ref mut info) = server.rooms[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::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.rooms[room_id].set_config(cfg);
- }
- VoteType::HedgehogsPerTeam(number) => {
- let r = &mut server.rooms[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);
- }
- } 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.clients[client_id];
-
- if let Some(room_id) = client.room_id {
- let room = &mut server.rooms[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);
- }
- }
- }
-}
-
-pub fn start_game(server: &mut HWServer, room_id: RoomId, response: &mut Response) {
- let (room_clients, room_nicks): (Vec<_>, Vec<_>) = server
- .clients
- .iter()
- .map(|(id, c)| (id, c.nick.clone()))
- .unzip();
- let room = &mut server.rooms[room_id];
-
- if !room.has_multiple_clans() {
- response.add(
- Warning("The game can't be started with less than two clans!".to_string()).send_self(),
- );
- } else if room.protocol_number <= 43 && room.players_number != room.ready_players_number {
- response.add(Warning("Not all players are ready".to_string()).send_self());
- } else if room.game_info.is_some() {
- response.add(Warning("The game is already in progress".to_string()).send_self());
- } else {
- room.start_round();
- for id in room_clients {
- let c = &mut server.clients[id];
- c.set_is_in_game(true);
- c.team_indices = room.client_team_indices(c.id);
- }
- response.add(RunGame.send_all().in_room(room.id));
- response.add(
- ClientFlags(add_flags(&[Flags::InGame]), room_nicks)
- .send_all()
- .in_room(room.id),
- );
-
- let room_master = if let Some(id) = room.master_id {
- Some(&server.clients[id])
- } else {
- None
- };
- get_room_update(None, room, room_master, response);
- }
-}
-
-pub fn end_game(server: &mut HWServer, room_id: RoomId, response: &mut Response) {
- let room = &mut server.rooms[room_id];
- room.ready_players_number = 1;
- let room_master = if let Some(id) = room.master_id {
- Some(&server.clients[id])
- } else {
- None
- };
- get_room_update(None, room, room_master, response);
- response.add(RoundFinished.send_all().in_room(room_id));
-
- if let Some(info) = replace(&mut room.game_info, None) {
- for (_, client) in server.clients.iter() {
- if client.room_id == Some(room_id) && client.is_joined_mid_game() {
- super::common::get_room_config(room, client.id, response);
- response.extend(
- info.left_teams
- .iter()
- .map(|name| TeamRemove(name.clone()).send(client.id)),
- );
- }
- }
- }
-
- let nicks: Vec<_> = 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();
-
- if !nicks.is_empty() {
- let msg = if room.protocol_number < 38 {
- LegacyReady(false, nicks)
- } else {
- ClientFlags(remove_flags(&[Flags::Ready]), nicks)
- };
- response.add(msg.send_all().in_room(room_id));
- }
-}
-
-#[cfg(test)]
-mod tests {
- use super::*;
- use crate::protocol::messages::HWServerMessage::ChatMsg;
- use crate::server::actions::PendingMessage;
-
- fn reply2string(r: HWServerMessage) -> String {
- match r {
- ChatMsg { msg: p, .. } => String::from(p),
- _ => panic!("expected a ChatMsg"),
- }
- }
-
- fn run_handle_test(opts: Vec<String>) {
- let opts2 = opts.clone();
- for opt in opts {
- while reply2string(rnd_reply(&opts2)) != opt {}
- }
- }
-
- /// This test terminates almost surely.
- #[test]
- fn test_handle_rnd_empty() {
- run_handle_test(vec![])
- }
-
- /// This test terminates almost surely.
- #[test]
- fn test_handle_rnd_nonempty() {
- run_handle_test(vec!["A".to_owned(), "B".to_owned(), "C".to_owned()])
- }
-
- /// This test terminates almost surely (strong law of large numbers)
- #[test]
- fn test_distribution() {
- let eps = 0.000001;
- let lim = 0.5;
- let opts = vec![0.to_string(), 1.to_string()];
- let mut ones = 0;
- let mut tries = 0;
-
- while tries < 1000 || ((ones as f64 / tries as f64) - lim).abs() >= eps {
- tries += 1;
- if reply2string(rnd_reply(&opts)) == 1.to_string() {
- ones += 1;
- }
- }
- }
-}
--- a/rust/hedgewars-server/src/server/handlers/inroom.rs Tue May 28 17:49:04 2019 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,615 +0,0 @@
-use mio;
-
-use super::common::rnd_reply;
-use crate::utils::to_engine_msg;
-use crate::{
- protocol::messages::{
- add_flags, remove_flags, server_chat, HWProtocolMessage, HWServerMessage::*,
- ProtocolFlags as Flags,
- },
- server::{
- core::HWServer,
- coretypes,
- coretypes::{ClientId, GameCfg, RoomId, VoteType, Voting, MAX_HEDGEHOGS_PER_TEAM},
- room::{HWRoom, RoomFlags, MAX_TEAMS_IN_ROOM},
- },
- utils::is_name_illegal,
-};
-use base64::{decode, encode};
-use log::*;
-use std::{cmp::min, iter::once, mem::swap};
-
-#[derive(Clone)]
-struct ByMsg<'a> {
- messages: &'a [u8],
-}
-
-impl<'a> Iterator for ByMsg<'a> {
- type Item = &'a [u8];
-
- fn next(&mut self) -> Option<<Self as Iterator>::Item> {
- if let Some(size) = self.messages.get(0) {
- let (msg, next) = self.messages.split_at(*size as usize + 1);
- self.messages = next;
- Some(msg)
- } else {
- None
- }
- }
-}
-
-fn by_msg(source: &[u8]) -> ByMsg {
- ByMsg { messages: source }
-}
-
-const VALID_MESSAGES: &[u8] =
- 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)]
-fn is_msg_valid(msg: &[u8], team_indices: &[u8]) -> bool {
- match msg {
- [size, typ, body..] => {
- VALID_MESSAGES.contains(typ)
- && match body {
- [1...MAX_HEDGEHOGS_PER_TEAM, team, ..] if *typ == b'h' => {
- team_indices.contains(team)
- }
- _ => *typ != b'h',
- }
- }
- _ => false,
- }
-}
-
-fn is_msg_valid(msg: &[u8], _team_indices: &[u8]) -> bool {
- if let Some(typ) = msg.get(1) {
- VALID_MESSAGES.contains(typ)
- } else {
- false
- }
-}
-
-fn is_msg_empty(msg: &[u8]) -> bool {
- msg.get(1).filter(|t| **t == b'+').is_some()
-}
-
-fn is_msg_timed(msg: &[u8]) -> bool {
- msg.get(1)
- .filter(|t| !NON_TIMED_MESSAGES.contains(t))
- .is_some()
-}
-
-fn voting_description(kind: &VoteType) -> String {
- format!(
- "New voting started: {}",
- match kind {
- VoteType::Kick(nick) => format!("kick {}", nick),
- VoteType::Map(name) => format!("map {}", name.as_ref().unwrap()),
- VoteType::Pause => "pause".to_string(),
- VoteType::NewSeed => "new seed".to_string(),
- VoteType::HedgehogsPerTeam(number) => format!("hedgehogs per team: {}", number),
- }
- )
-}
-
-fn room_message_flag(msg: &HWProtocolMessage) -> RoomFlags {
- use crate::protocol::messages::HWProtocolMessage::*;
- match msg {
- ToggleRestrictJoin => RoomFlags::RESTRICTED_JOIN,
- ToggleRestrictTeams => RoomFlags::RESTRICTED_TEAM_ADD,
- ToggleRegisteredOnly => RoomFlags::RESTRICTED_UNREGISTERED_PLAYERS,
- _ => RoomFlags::empty(),
- }
-}
-
-pub fn handle(
- server: &mut HWServer,
- client_id: ClientId,
- response: &mut super::Response,
- room_id: RoomId,
- message: HWProtocolMessage,
-) {
- let client = &mut server.clients[client_id];
- let room = &mut server.rooms[room_id];
-
- use crate::protocol::messages::HWProtocolMessage::*;
- match message {
- Part(msg) => {
- let msg = match msg {
- Some(s) => format!("part: {}", s),
- None => "part".to_string(),
- };
- super::common::exit_room(server, client_id, response, &msg);
- }
- Chat(msg) => {
- response.add(
- ChatMsg {
- nick: client.nick.clone(),
- msg,
- }
- .send_all()
- .in_room(room_id),
- );
- }
- 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();
- response.add(ForwardEngineMessage(vec![engine_msg]).send_many(team))
- }
- }
- }
- 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);
- }
- }
- Unfix => {
- if client.is_admin() {
- room.set_is_fixed(false);
- }
- }
- Greeting(text) => {
- if client.is_admin() || client.is_master() && !room.is_fixed() {
- room.greeting = text;
- }
- }
- MaxTeams(count) => {
- if !client.is_master() {
- response.add(Warning("You're not the room master!".to_string()).send_self());
- } else if !(2..=MAX_TEAMS_IN_ROOM).contains(&count) {
- response
- .add(Warning("/maxteams: specify number from 2 to 8".to_string()).send_self());
- } else {
- server.rooms[room_id].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());
- } else if server.has_room(&new_name) {
- response.add(
- Warning("A room with the same name already exists.".to_string()).send_self(),
- );
- } else {
- let room = &mut server.rooms[room_id];
- if room.is_fixed() || room.master_id != Some(client_id) {
- response.add(Warning("Access denied.".to_string()).send_self());
- } 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);
- }
- }
- }
- ToggleReady => {
- let flags = if client.is_ready() {
- room.ready_players_number -= 1;
- remove_flags(&[Flags::Ready])
- } else {
- room.ready_players_number += 1;
- add_flags(&[Flags::Ready])
- };
-
- let msg = if client.protocol_number < 38 {
- LegacyReady(client.is_ready(), vec![client.nick.clone()])
- } else {
- ClientFlags(flags, vec![client.nick.clone()])
- };
- response.add(msg.send_all().in_room(room.id));
- client.set_is_ready(!client.is_ready());
-
- if room.is_fixed() && room.ready_players_number == room.players_number {
- super::common::start_game(server, room_id, response);
- }
- }
- AddTeam(mut info) => {
- if room.teams.len() >= room.max_teams as usize {
- response.add(Warning("Too many teams!".to_string()).send_self());
- } else if room.addable_hedgehogs() == 0 {
- response.add(Warning("Too many hedgehogs!".to_string()).send_self());
- } 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(),
- );
- } else if room.game_info.is_some() {
- response.add(
- Warning("Joining not possible: Round is in progress.".to_string()).send_self(),
- );
- } else if room.is_team_add_restricted() {
- response.add(
- Warning("This room currently does not allow adding new teams.".to_string())
- .send_self(),
- );
- } 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);
- response.add(TeamAccepted(team.name.clone()).send_self());
- response.add(
- TeamAdd(team.to_protocol())
- .send_all()
- .in_room(room_id)
- .but_self(),
- );
- response.add(
- TeamColor(team.name.clone(), team.color)
- .send_all()
- .in_room(room_id),
- );
- response.add(
- HedgehogsNumber(team.name.clone(), team.hedgehogs_number)
- .send_all()
- .in_room(room_id),
- );
-
- let room_master = if let Some(id) = room.master_id {
- Some(&server.clients[id])
- } else {
- None
- };
- super::common::get_room_update(None, room, room_master, response);
- }
- }
- 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()),
- Some((_, name)) => {
- client.teams_in_game -= 1;
- client.clan = room.find_team_color(client.id);
- super::common::remove_teams(
- room,
- vec![name.to_string()],
- client.is_in_game(),
- response,
- );
-
- match room.game_info {
- Some(ref info) if info.teams_in_game == 0 => {
- super::common::end_game(server, room_id, response)
- }
- _ => (),
- }
- }
- },
- 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.add(Error("You're not the room master!".to_string()).send_self());
- } else if !(1..=max_hedgehogs).contains(&number) {
- response
- .add(HedgehogsNumber(team.name.clone(), team.hedgehogs_number).send_self());
- } else {
- team.hedgehogs_number = number;
- response.add(
- HedgehogsNumber(team.name.clone(), number)
- .send_all()
- .in_room(room_id)
- .but_self(),
- );
- }
- } else {
- response.add(Warning("No such team.".to_string()).send_self());
- }
- }
- 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());
- } else {
- team.color = color;
- response.add(
- TeamColor(team.name.clone(), color)
- .send_all()
- .in_room(room_id)
- .but_self(),
- );
- server.clients[owner].clan = Some(color);
- }
- } else {
- response.add(Warning("No such team.".to_string()).send_self());
- }
- }
- Cfg(cfg) => {
- if room.is_fixed() {
- response.add(Warning("Access denied.".to_string()).send_self());
- } else if !client.is_master() {
- response.add(Error("You're not the room master!".to_string()).send_self());
- } 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);
- }
- }
- Save(name, location) => {
- response.add(
- server_chat(format!("Room config saved as {}", name))
- .send_all()
- .in_room(room_id),
- );
- room.save_config(name, location);
- }
- #[cfg(feature = "official-server")]
- SaveRoom(filename) => {
- if client.is_admin() {
- match room.get_saves() {
- Ok(contents) => response.request_io(super::IoTask::SaveRoom {
- room_id,
- filename,
- contents,
- }),
- Err(e) => {
- warn!("Error while serializing the room configs: {}", e);
- response.add(
- Warning("Unable to serialize the room configs.".to_string())
- .send_self(),
- )
- }
- }
- }
- }
- #[cfg(feature = "official-server")]
- LoadRoom(filename) => {
- if client.is_admin() {
- response.request_io(super::IoTask::LoadRoom { room_id, filename });
- }
- }
- Delete(name) => {
- if !room.delete_config(&name) {
- response.add(Warning(format!("Save doesn't exist: {}", name)).send_self());
- } else {
- response.add(
- server_chat(format!("Room config {} has been deleted", name))
- .send_all()
- .in_room(room_id),
- );
- }
- }
- CallVote(None) => {
- 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 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<_> = server.rooms[room_id].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())
- }
- }
- 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, server.room_clients(client_id).collect());
- let room = &mut server.rooms[room_id];
- room.voting = Some(voting);
- response.add(server_chat(msg).send_all().in_room(room_id));
- super::common::submit_vote(
- server,
- coretypes::Vote {
- is_pro: true,
- is_forced: false,
- },
- response,
- );
- }
- Some(msg) => {
- response.add(server_chat(msg).send_self());
- }
- }
- }
- Vote(vote) => {
- super::common::submit_vote(
- server,
- coretypes::Vote {
- is_pro: vote,
- is_forced: false,
- },
- response,
- );
- }
- ForceVote(vote) => {
- let is_forced = client.is_admin();
- super::common::submit_vote(
- server,
- coretypes::Vote {
- is_pro: vote,
- is_forced,
- },
- response,
- );
- }
- ToggleRestrictJoin | ToggleRestrictTeams | ToggleRegisteredOnly => {
- if client.is_master() {
- room.flags.toggle(room_message_flag(&message));
- super::common::get_room_update(None, room, Some(&client), response);
- }
- }
- StartGame => {
- super::common::start_game(server, room_id, response);
- }
- EngineMessage(em) => {
- if client.teams_in_game > 0 {
- let decoding = decode(&em[..]).unwrap();
- let messages = by_msg(&decoding);
- let valid = messages.filter(|m| is_msg_valid(m, &client.team_indices));
- let non_empty = valid.clone().filter(|m| !is_msg_empty(m));
- let sync_msg = valid.clone().filter(|m| is_msg_timed(m)).last().map(|m| {
- if is_msg_empty(m) {
- Some(encode(m))
- } else {
- None
- }
- });
-
- let em_response = encode(&valid.flat_map(|msg| msg).cloned().collect::<Vec<_>>());
- if !em_response.is_empty() {
- response.add(
- ForwardEngineMessage(vec![em_response])
- .send_all()
- .in_room(room.id)
- .but_self(),
- );
- }
- let em_log = encode(&non_empty.flat_map(|msg| msg).cloned().collect::<Vec<_>>());
- if let Some(ref mut info) = room.game_info {
- if !em_log.is_empty() {
- info.msg_log.push(em_log);
- }
- if let Some(msg) = sync_msg {
- info.sync_msg = msg;
- }
- }
- }
- }
- RoundFinished => {
- let mut game_ended = false;
- if client.is_in_game() {
- client.set_is_in_game(false);
- response.add(
- ClientFlags(remove_flags(&[Flags::InGame]), vec![client.nick.clone()])
- .send_all()
- .in_room(room.id),
- );
- 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;
- if info.teams_in_game == 0 {
- game_ended = true;
- }
-
- for team_name in team_names {
- 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(),
- );
-
- 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());
- }
- if info.sync_msg.is_some() {
- 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(),
- );
- }
- }
- }
- if game_ended {
- super::common::end_game(server, room_id, response)
- }
- }
- Rnd(v) => {
- let result = rnd_reply(&v);
- let mut echo = vec!["/rnd".to_string()];
- echo.extend(v.into_iter());
- let chat_msg = ChatMsg {
- nick: server.clients[client_id].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) => {
- 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(),
- )
- } 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()),
- Some((id, _)) => {
- super::common::change_master(server, room_id, id, response);
- }
- }
- }
- }
- _ => warn!("Unimplemented!"),
- }
-}
--- a/rust/hedgewars-server/src/server/handlers/lobby.rs Tue May 28 17:49:04 2019 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,164 +0,0 @@
-use mio;
-
-use super::common::rnd_reply;
-use crate::{
- protocol::messages::{
- add_flags, remove_flags, server_chat, HWProtocolMessage, HWServerMessage::*,
- ProtocolFlags as Flags,
- },
- server::{
- client::HWClient,
- core::HWServer,
- coretypes::{ClientId, ServerVar},
- },
- utils::is_name_illegal,
-};
-use log::*;
-use std::{collections::HashSet, convert::identity};
-
-pub fn handle(
- server: &mut HWServer,
- client_id: ClientId,
- response: &mut super::Response,
- message: HWProtocolMessage,
-) {
- use crate::protocol::messages::HWProtocolMessage::*;
- match message {
- CreateRoom(name, password) => {
- if is_name_illegal(&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());
- } else if server.has_room(&name) {
- response.add(
- Warning("A room with the same name already exists.".to_string()).send_self(),
- );
- } else {
- let flags_msg = ClientFlags(
- add_flags(&[Flags::RoomMaster, Flags::Ready]),
- vec![server.clients[client_id].nick.clone()],
- );
-
- let room_id = server.create_room(client_id, name, password);
- let room = &server.rooms[room_id];
- let client = &server.clients[client_id];
-
- response.add(
- RoomAdd(room.info(Some(&client)))
- .send_all()
- .with_protocol(room.protocol_number),
- );
- response.add(RoomJoined(vec![client.nick.clone()]).send_self());
- response.add(flags_msg.send_self());
-
- response.add(
- ClientFlags(add_flags(&[Flags::InRoom]), vec![client.nick.clone()]).send_self(),
- );
- };
- }
- Chat(msg) => {
- response.add(
- ChatMsg {
- nick: server.clients[client_id].nick.clone(),
- msg,
- }
- .send_all()
- .in_lobby()
- .but_self(),
- );
- }
- JoinRoom(name, _password) => {
- let room = server.rooms.iter().find(|(_, r)| r.name == name);
- let room_id = room.map(|(_, r)| r.id);
-
- let client = &mut server.clients[client_id];
-
- if let Some((_, room)) = room {
- if client.protocol_number != room.protocol_number {
- response.add(
- Warning("Room version incompatible to your Hedgewars version!".to_string())
- .send_self(),
- );
- } else if room.is_join_restricted() {
- response.add(
- Warning(
- "Access denied. This room currently doesn't allow joining.".to_string(),
- )
- .send_self(),
- );
- } else if room.players_number == u8::max_value() {
- response.add(Warning("This room is already full".to_string()).send_self());
- } else if let Some(room_id) = room_id {
- super::common::enter_room(server, client_id, room_id, response);
- }
- } else {
- response.add(Warning("No such room.".to_string()).send_self());
- }
- }
- Follow(nick) => {
- if let Some(HWClient {
- room_id: Some(room_id),
- ..
- }) = server.find_client(&nick)
- {
- let room = &server.rooms[*room_id];
- response.add(Joining(room.name.clone()).send_self());
- super::common::enter_room(server, client_id, *room_id, response);
- }
- }
- SetServerVar(var) => {
- if !server.clients[client_id].is_admin() {
- response.add(Warning("Access denied.".to_string()).send_self());
- } else {
- match var {
- ServerVar::MOTDNew(msg) => server.greetings.for_latest_protocol = msg,
- ServerVar::MOTDOld(msg) => server.greetings.for_old_protocols = msg,
- ServerVar::LatestProto(n) => server.latest_protocol = n,
- }
- }
- }
- GetServerVar => {
- if !server.clients[client_id].is_admin() {
- response.add(Warning("Access denied.".to_string()).send_self());
- } else {
- let vars: Vec<_> = [
- ServerVar::MOTDNew(server.greetings.for_latest_protocol.clone()),
- ServerVar::MOTDOld(server.greetings.for_old_protocols.clone()),
- ServerVar::LatestProto(server.latest_protocol),
- ]
- .iter()
- .flat_map(|v| v.to_protocol())
- .collect();
- response.add(ServerVars(vars).send_self());
- }
- }
- Rnd(v) => {
- response.add(rnd_reply(&v).send_self());
- }
- Stats => {
- let mut protocols: HashSet<_> = server
- .clients
- .iter()
- .map(|(_, c)| c.protocol_number)
- .chain(server.rooms.iter().map(|(_, r)| r.protocol_number))
- .collect();
- let mut protocols: Vec<_> = protocols.drain().collect();
- protocols.sort();
-
- let mut html = Vec::with_capacity(protocols.len() + 2);
-
- html.push("<table>".to_string());
- for protocol in protocols {
- html.push(format!(
- "<tr><td>{}</td><td>{}</td><td>{}</td></tr>",
- super::utils::protocol_version_string(protocol),
- server.protocol_clients(protocol).count(),
- server.protocol_rooms(protocol).count()
- ));
- }
- html.push("</table>".to_string());
-
- response.add(Warning(html.join("")).send_self());
- }
- List => warn!("Deprecated LIST message received"),
- _ => warn!("Incorrect command in lobby state"),
- }
-}
--- a/rust/hedgewars-server/src/server/handlers/loggingin.rs Tue May 28 17:49:04 2019 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,146 +0,0 @@
-use mio;
-
-use crate::protocol::messages::HWProtocolMessage::LoadRoom;
-use crate::server::client::HWClient;
-use crate::server::core::HWServer;
-use crate::{
- protocol::messages::{HWProtocolMessage, HWServerMessage::*},
- server::{
- core::{HWAnteClient, HWAnteroom},
- coretypes::ClientId,
- },
- utils::is_name_illegal,
-};
-use log::*;
-#[cfg(feature = "official-server")]
-use openssl::sha::sha1;
-use std::{
- fmt::{Formatter, LowerHex},
- num::NonZeroU16,
-};
-
-pub enum LoginResult {
- Unchanged,
- Complete,
- Exit,
-}
-
-fn completion_result<'a, I>(
- mut other_clients: I,
- client: &mut HWAnteClient,
- response: &mut super::Response,
-) -> LoginResult
-where
- I: Iterator<Item = (ClientId, &'a HWClient)>,
-{
- let has_nick_clash =
- other_clients.any(|(_, c)| !c.is_checker() && c.nick == *client.nick.as_ref().unwrap());
-
- if has_nick_clash {
- if client.protocol_number.unwrap().get() < 38 {
- response.add(Bye("User quit: Nickname is already in use".to_string()).send_self());
- LoginResult::Exit
- } else {
- client.nick = None;
- response.add(Notice("NickAlreadyInUse".to_string()).send_self());
- LoginResult::Unchanged
- }
- } else {
- #[cfg(feature = "official-server")]
- {
- response.add(AskPassword(client.server_salt.clone()).send_self());
- LoginResult::Unchanged
- }
-
- #[cfg(not(feature = "official-server"))]
- {
- LoginResult::Complete
- }
- }
-}
-
-pub fn handle(
- server: &mut HWServer,
- client_id: ClientId,
- response: &mut super::Response,
- message: HWProtocolMessage,
-) -> LoginResult {
- match message {
- HWProtocolMessage::Quit(_) => {
- response.add(Bye("User quit".to_string()).send_self());
- LoginResult::Exit
- }
- HWProtocolMessage::Nick(nick) => {
- let client = &mut server.anteroom.clients[client_id];
-
- if client.nick.is_some() {
- response.add(Error("Nickname already provided.".to_string()).send_self());
- LoginResult::Unchanged
- } else if is_name_illegal(&nick) {
- response.add(Bye("Illegal nickname! Nicknames 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());
- LoginResult::Exit
- } else {
- client.nick = Some(nick.clone());
- response.add(Nick(nick).send_self());
-
- if client.protocol_number.is_some() {
- completion_result(server.clients.iter(), client, response)
- } else {
- LoginResult::Unchanged
- }
- }
- }
- HWProtocolMessage::Proto(proto) => {
- let client = &mut server.anteroom.clients[client_id];
- if client.protocol_number.is_some() {
- response.add(Error("Protocol already known.".to_string()).send_self());
- LoginResult::Unchanged
- } else if proto == 0 {
- response.add(Error("Bad number.".to_string()).send_self());
- LoginResult::Unchanged
- } else {
- client.protocol_number = NonZeroU16::new(proto);
- response.add(Proto(proto).send_self());
-
- if client.nick.is_some() {
- completion_result(server.clients.iter(), client, response)
- } else {
- LoginResult::Unchanged
- }
- }
- }
- #[cfg(feature = "official-server")]
- HWProtocolMessage::Password(hash, salt) => {
- let client = &server.anteroom.clients[client_id];
-
- if let (Some(nick), Some(protocol)) = (client.nick.as_ref(), client.protocol_number) {
- response.request_io(super::IoTask::GetAccount {
- nick: nick.clone(),
- protocol: protocol.get(),
- server_salt: client.server_salt.clone(),
- client_salt: salt,
- password_hash: hash,
- });
- };
-
- LoginResult::Unchanged
- }
- #[cfg(feature = "official-server")]
- HWProtocolMessage::Checker(protocol, nick, password) => {
- let client = &mut server.anteroom.clients[client_id];
- if protocol == 0 {
- response.add(Error("Bad number.".to_string()).send_self());
- LoginResult::Unchanged
- } else {
- client.protocol_number = NonZeroU16::new(protocol);
- client.nick = Some(nick);
- client.is_checker = true;
- LoginResult::Complete
- }
- }
- _ => {
- warn!("Incorrect command in logging-in state");
- LoginResult::Unchanged
- }
- }
-}
--- a/rust/hedgewars-server/src/server/indexslab.rs Tue May 28 17:49:04 2019 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,71 +0,0 @@
-use std::{
- iter,
- mem::replace,
- ops::{Index, IndexMut},
-};
-
-pub struct IndexSlab<T> {
- data: Vec<Option<T>>,
-}
-
-impl<T> IndexSlab<T> {
- pub fn new() -> Self {
- Self { data: Vec::new() }
- }
-
- pub fn with_capacity(capacity: usize) -> Self {
- Self {
- data: Vec::with_capacity(capacity),
- }
- }
-
- pub fn insert(&mut self, index: usize, value: T) {
- if index >= self.data.len() {
- self.data.reserve(index - self.data.len() + 1);
- self.data.extend((self.data.len()..index).map(|_| None));
- self.data.push(Some(value))
- } else {
- self.data[index] = Some(value);
- }
- }
-
- pub fn contains(&self, index: usize) -> bool {
- self.data.get(index).and_then(|x| x.as_ref()).is_some()
- }
-
- pub fn remove(&mut self, index: usize) -> Option<T> {
- if let Some(x) = self.data.get_mut(index) {
- replace(x, None)
- } else {
- None
- }
- }
-
- pub fn iter(&self) -> impl Iterator<Item = (usize, &T)> {
- self.data
- .iter()
- .enumerate()
- .filter_map(|(index, opt)| opt.as_ref().and_then(|x| Some((index, x))))
- }
-
- pub fn iter_mut(&mut self) -> impl Iterator<Item = (usize, &mut T)> {
- self.data
- .iter_mut()
- .enumerate()
- .filter_map(|(index, opt)| opt.as_mut().and_then(|x| Some((index, x))))
- }
-}
-
-impl<T> Index<usize> for IndexSlab<T> {
- type Output = T;
-
- fn index(&self, index: usize) -> &Self::Output {
- self.data[index].as_ref().unwrap()
- }
-}
-
-impl<T> IndexMut<usize> for IndexSlab<T> {
- fn index_mut(&mut self, index: usize) -> &mut Self::Output {
- self.data[index].as_mut().unwrap()
- }
-}
--- a/rust/hedgewars-server/src/server/io.rs Tue May 28 17:49:04 2019 +0200
+++ b/rust/hedgewars-server/src/server/io.rs Tue May 28 19:04:18 2019 +0300
@@ -5,8 +5,8 @@
thread,
};
-use crate::server::{
- database::Database,
+use crate::{
+ server::database::Database,
handlers::{IoResult, IoTask},
};
use log::*;
--- a/rust/hedgewars-server/src/server/network.rs Tue May 28 17:49:04 2019 +0200
+++ b/rust/hedgewars-server/src/server/network.rs Tue May 28 19:04:18 2019 +0300
@@ -17,8 +17,12 @@
use netbuf;
use slab::Slab;
-use super::{core::HWServer, coretypes::ClientId, handlers};
use crate::{
+ core::{
+ server::HWServer,
+ types::ClientId
+ },
+ handlers,
protocol::{messages::*, ProtocolDecoder},
utils,
};
@@ -26,8 +30,11 @@
#[cfg(feature = "official-server")]
use super::io::{IOThread, RequestId};
-use crate::protocol::messages::HWServerMessage::Redirect;
-use crate::server::handlers::{IoResult, IoTask};
+use crate::{
+ protocol::messages::HWServerMessage::Redirect,
+ handlers::{IoResult, IoTask}
+};
+
#[cfg(feature = "tls-connections")]
use openssl::{
error::ErrorStack,
--- a/rust/hedgewars-server/src/server/room.rs Tue May 28 17:49:04 2019 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,349 +0,0 @@
-use crate::server::{
- client::HWClient,
- coretypes::{
- ClientId, GameCfg, GameCfg::*, RoomConfig, RoomId, TeamInfo, Voting, MAX_HEDGEHOGS_PER_TEAM,
- },
-};
-use bitflags::*;
-use serde::{Deserialize, Serialize};
-use serde_derive::{Deserialize, Serialize};
-use serde_yaml;
-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;
-
-fn client_teams_impl(
- teams: &[(ClientId, TeamInfo)],
- client_id: ClientId,
-) -> impl Iterator<Item = &TeamInfo> + Clone {
- teams
- .iter()
- .filter(move |(id, _)| *id == client_id)
- .map(|(_, t)| t)
-}
-
-pub struct GameInfo {
- pub teams_in_game: u8,
- pub teams_at_start: Vec<(ClientId, TeamInfo)>,
- pub left_teams: Vec<String>,
- pub msg_log: Vec<String>,
- pub sync_msg: Option<String>,
- pub is_paused: bool,
- config: RoomConfig,
-}
-
-impl GameInfo {
- fn new(teams: Vec<(ClientId, TeamInfo)>, config: RoomConfig) -> GameInfo {
- GameInfo {
- left_teams: Vec::new(),
- msg_log: Vec::new(),
- sync_msg: None,
- is_paused: false,
- teams_in_game: teams.len() as u8,
- teams_at_start: teams,
- config,
- }
- }
-
- pub fn client_teams(&self, client_id: ClientId) -> impl Iterator<Item = &TeamInfo> + Clone {
- client_teams_impl(&self.teams_at_start, client_id)
- }
-}
-
-#[derive(Serialize, Deserialize)]
-pub struct RoomSave {
- pub location: String,
- config: RoomConfig,
-}
-
-bitflags! {
- pub struct RoomFlags: u8 {
- const FIXED = 0b0000_0001;
- const RESTRICTED_JOIN = 0b0000_0010;
- const RESTRICTED_TEAM_ADD = 0b0000_0100;
- const RESTRICTED_UNREGISTERED_PLAYERS = 0b0000_1000;
- }
-}
-
-pub struct HWRoom {
- pub id: RoomId,
- pub master_id: Option<ClientId>,
- pub name: String,
- pub password: Option<String>,
- pub greeting: String,
- pub protocol_number: u16,
- pub flags: RoomFlags,
-
- pub players_number: u8,
- pub default_hedgehog_number: u8,
- pub max_teams: u8,
- pub ready_players_number: u8,
- pub teams: Vec<(ClientId, TeamInfo)>,
- config: RoomConfig,
- pub voting: Option<Voting>,
- pub saves: HashMap<String, RoomSave>,
- pub game_info: Option<GameInfo>,
-}
-
-impl HWRoom {
- pub fn new(id: RoomId) -> HWRoom {
- HWRoom {
- id,
- master_id: None,
- name: String::new(),
- password: None,
- greeting: "".to_string(),
- flags: RoomFlags::empty(),
- protocol_number: 0,
- players_number: 0,
- default_hedgehog_number: 4,
- max_teams: MAX_TEAMS_IN_ROOM,
- ready_players_number: 0,
- teams: Vec::new(),
- config: RoomConfig::new(),
- voting: None,
- saves: HashMap::new(),
- game_info: None,
- }
- }
-
- pub fn hedgehogs_number(&self) -> u8 {
- self.teams.iter().map(|(_, t)| t.hedgehogs_number).sum()
- }
-
- pub fn addable_hedgehogs(&self) -> u8 {
- MAX_HEDGEHOGS_IN_ROOM - self.hedgehogs_number()
- }
-
- pub fn add_team(
- &mut self,
- owner_id: ClientId,
- mut team: TeamInfo,
- preserve_color: bool,
- ) -> &TeamInfo {
- if !preserve_color {
- team.color = iter::repeat(())
- .enumerate()
- .map(|(i, _)| i as u8)
- .take(u8::max_value() as usize + 1)
- .find(|i| self.teams.iter().all(|(_, t)| t.color != *i))
- .unwrap_or(0u8)
- };
- team.hedgehogs_number = if self.teams.is_empty() {
- self.default_hedgehog_number
- } else {
- self.teams[0]
- .1
- .hedgehogs_number
- .min(self.addable_hedgehogs())
- };
- self.teams.push((owner_id, team));
- &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) {
- self.teams.remove(index);
- }
- }
-
- pub fn set_hedgehogs_number(&mut self, n: u8) -> Vec<String> {
- let mut names = Vec::new();
- let teams = match self.game_info {
- Some(ref mut info) => &mut info.teams_at_start,
- None => &mut self.teams,
- };
-
- if teams.len() as u8 * n <= MAX_HEDGEHOGS_IN_ROOM {
- for (_, team) in teams.iter_mut() {
- team.hedgehogs_number = n;
- names.push(team.name.clone())
- }
- self.default_hedgehog_number = n;
- }
- names
- }
-
- pub fn find_team_and_owner_mut<F>(&mut self, f: F) -> Option<(ClientId, &mut TeamInfo)>
- where
- F: Fn(&TeamInfo) -> bool,
- {
- self.teams
- .iter_mut()
- .find(|(_, t)| f(t))
- .map(|(id, t)| (*id, t))
- }
-
- pub fn find_team<F>(&self, f: F) -> Option<&TeamInfo>
- where
- F: Fn(&TeamInfo) -> bool,
- {
- self.teams
- .iter()
- .find_map(|(_, t)| Some(t).filter(|t| f(&t)))
- }
-
- pub fn client_teams(&self, client_id: ClientId) -> impl Iterator<Item = &TeamInfo> {
- client_teams_impl(&self.teams, client_id)
- }
-
- pub fn client_team_indices(&self, client_id: ClientId) -> Vec<u8> {
- self.teams
- .iter()
- .enumerate()
- .filter(move |(_, (id, _))| *id == client_id)
- .map(|(i, _)| i as u8)
- .collect()
- }
-
- pub fn clan_team_owners(&self, color: u8) -> impl Iterator<Item = ClientId> + '_ {
- self.teams
- .iter()
- .filter(move |(_, t)| t.color == color)
- .map(|(id, _)| *id)
- }
-
- pub fn find_team_owner(&self, team_name: &str) -> Option<(ClientId, &str)> {
- self.teams
- .iter()
- .find(|(_, t)| t.name == team_name)
- .map(|(id, t)| (*id, &t.name[..]))
- }
-
- pub fn find_team_color(&self, owner_id: ClientId) -> Option<u8> {
- self.client_teams(owner_id).nth(0).map(|t| t.color)
- }
-
- pub fn has_multiple_clans(&self) -> bool {
- self.teams.iter().min_by_key(|(_, t)| t.color)
- != self.teams.iter().max_by_key(|(_, t)| t.color)
- }
-
- pub fn set_config(&mut self, cfg: GameCfg) {
- self.config.set_config(cfg);
- }
-
- pub fn start_round(&mut self) {
- if self.game_info.is_none() {
- self.game_info = Some(GameInfo::new(self.teams.clone(), self.config.clone()));
- }
- }
-
- pub fn is_fixed(&self) -> bool {
- self.flags.contains(RoomFlags::FIXED)
- }
- pub fn is_join_restricted(&self) -> bool {
- self.flags.contains(RoomFlags::RESTRICTED_JOIN)
- }
- pub fn is_team_add_restricted(&self) -> bool {
- self.flags.contains(RoomFlags::RESTRICTED_TEAM_ADD)
- }
- pub fn are_unregistered_players_restricted(&self) -> bool {
- self.flags
- .contains(RoomFlags::RESTRICTED_UNREGISTERED_PLAYERS)
- }
-
- pub fn set_is_fixed(&mut self, value: bool) {
- self.flags.set(RoomFlags::FIXED, value)
- }
- pub fn set_join_restriction(&mut self, value: bool) {
- self.flags.set(RoomFlags::RESTRICTED_JOIN, value)
- }
- pub fn set_team_add_restriction(&mut self, value: bool) {
- self.flags.set(RoomFlags::RESTRICTED_TEAM_ADD, value)
- }
- pub fn set_unregistered_players_restriction(&mut self, value: bool) {
- self.flags
- .set(RoomFlags::RESTRICTED_UNREGISTERED_PLAYERS, value)
- }
-
- fn flags_string(&self) -> String {
- let mut result = "-".to_string();
- if self.game_info.is_some() {
- result += "g"
- }
- if self.password.is_some() {
- result += "p"
- }
- if self.is_join_restricted() {
- result += "j"
- }
- if self.are_unregistered_players_restricted() {
- result += "r"
- }
- result
- }
-
- pub fn info(&self, master: Option<&HWClient>) -> Vec<String> {
- let c = &self.config;
- vec![
- self.flags_string(),
- self.name.clone(),
- self.players_number.to_string(),
- self.teams.len().to_string(),
- master.map_or("[]", |c| &c.nick).to_string(),
- c.map_type.to_string(),
- c.script.to_string(),
- c.scheme.name.to_string(),
- c.ammo.name.to_string(),
- ]
- }
-
- pub fn active_config(&self) -> &RoomConfig {
- match self.game_info {
- Some(ref info) => &info.config,
- None => &self.config,
- }
- }
-
- pub fn map_config(&self) -> Vec<String> {
- match self.game_info {
- Some(ref info) => info.config.to_map_config(),
- None => self.config.to_map_config(),
- }
- }
-
- pub fn game_config(&self) -> Vec<GameCfg> {
- match self.game_info {
- Some(ref info) => info.config.to_game_config(),
- None => self.config.to_game_config(),
- }
- }
-
- pub fn save_config(&mut self, name: String, location: String) {
- self.saves.insert(
- name,
- RoomSave {
- location,
- config: self.config.clone(),
- },
- );
- }
-
- pub fn load_config(&mut self, name: &str) -> Option<&str> {
- if let Some(save) = self.saves.get(name) {
- self.config = save.config.clone();
- Some(&save.location[..])
- } else {
- None
- }
- }
-
- pub fn delete_config(&mut self, name: &str) -> bool {
- self.saves.remove(name).is_some()
- }
-
- pub fn get_saves(&self) -> Result<String, serde_yaml::Error> {
- serde_yaml::to_string(&(&self.greeting, &self.saves))
- }
-
- pub fn set_saves(&mut self, text: &str) -> Result<(), serde_yaml::Error> {
- serde_yaml::from_str::<(String, HashMap<String, RoomSave>)>(text).map(
- |(greeting, saves)| {
- self.greeting = greeting;
- self.saves = saves;
- },
- )
- }
-}