rust/hedgewars-server/src/server/room.rs
changeset 14420 06672690d71b
parent 14355 31717e1436cd
child 14462 98ef2913ec73
equal deleted inserted replaced
14419:6843c4551cde 14420:06672690d71b
       
     1 use std::{
       
     2     iter, collections::HashMap
       
     3 };
       
     4 use crate::server::{
       
     5     coretypes::{
       
     6         ClientId, RoomId, TeamInfo, GameCfg, GameCfg::*, Voting,
       
     7         MAX_HEDGEHOGS_PER_TEAM
       
     8     },
       
     9     client::{HWClient}
       
    10 };
       
    11 use bitflags::*;
       
    12 use serde::{Serialize, Deserialize};
       
    13 use serde_derive::{Serialize, Deserialize};
       
    14 use serde_yaml;
       
    15 
       
    16 const MAX_TEAMS_IN_ROOM: u8 = 8;
       
    17 const MAX_HEDGEHOGS_IN_ROOM: u8 =
       
    18     MAX_HEDGEHOGS_PER_TEAM * MAX_HEDGEHOGS_PER_TEAM;
       
    19 
       
    20 #[derive(Clone, Serialize, Deserialize)]
       
    21 struct Ammo {
       
    22     name: String,
       
    23     settings: Option<String>
       
    24 }
       
    25 
       
    26 #[derive(Clone, Serialize, Deserialize)]
       
    27 struct Scheme {
       
    28     name: String,
       
    29     settings: Vec<String>
       
    30 }
       
    31 
       
    32 #[derive(Clone, Serialize, Deserialize)]
       
    33 struct RoomConfig {
       
    34     feature_size: u32,
       
    35     map_type: String,
       
    36     map_generator: u32,
       
    37     maze_size: u32,
       
    38     seed: String,
       
    39     template: u32,
       
    40 
       
    41     ammo: Ammo,
       
    42     scheme: Scheme,
       
    43     script: String,
       
    44     theme: String,
       
    45     drawn_map: Option<String>
       
    46 }
       
    47 
       
    48 impl RoomConfig {
       
    49     fn new() -> RoomConfig {
       
    50         RoomConfig {
       
    51             feature_size: 12,
       
    52             map_type: "+rnd+".to_string(),
       
    53             map_generator: 0,
       
    54             maze_size: 0,
       
    55             seed: "seed".to_string(),
       
    56             template: 0,
       
    57 
       
    58             ammo: Ammo {name: "Default".to_string(), settings: None },
       
    59             scheme: Scheme {name: "Default".to_string(), settings: Vec::new() },
       
    60             script: "Normal".to_string(),
       
    61             theme: "\u{1f994}".to_string(),
       
    62             drawn_map: None
       
    63         }
       
    64     }
       
    65 }
       
    66 
       
    67 fn client_teams_impl(teams: &[(ClientId, TeamInfo)], client_id: ClientId)
       
    68     -> impl Iterator<Item = &TeamInfo> + Clone
       
    69 {
       
    70     teams.iter().filter(move |(id, _)| *id == client_id).map(|(_, t)| t)
       
    71 }
       
    72 
       
    73 fn map_config_from(c: &RoomConfig) -> Vec<String> {
       
    74     vec![c.feature_size.to_string(), c.map_type.to_string(),
       
    75          c.map_generator.to_string(), c.maze_size.to_string(),
       
    76          c.seed.to_string(), c.template.to_string()]
       
    77 }
       
    78 
       
    79 fn game_config_from(c: &RoomConfig) -> Vec<GameCfg> {
       
    80     use crate::server::coretypes::GameCfg::*;
       
    81     let mut v = vec![
       
    82         Ammo(c.ammo.name.to_string(), c.ammo.settings.clone()),
       
    83         Scheme(c.scheme.name.to_string(), c.scheme.settings.clone()),
       
    84         Script(c.script.to_string()),
       
    85         Theme(c.theme.to_string())];
       
    86     if let Some(ref m) = c.drawn_map {
       
    87         v.push(DrawnMap(m.to_string()))
       
    88     }
       
    89     v
       
    90 }
       
    91 
       
    92 pub struct GameInfo {
       
    93     pub teams_in_game: u8,
       
    94     pub teams_at_start: Vec<(ClientId, TeamInfo)>,
       
    95     pub left_teams: Vec<String>,
       
    96     pub msg_log: Vec<String>,
       
    97     pub sync_msg: Option<String>,
       
    98     pub is_paused: bool,
       
    99     config: RoomConfig
       
   100 }
       
   101 
       
   102 impl GameInfo {
       
   103     fn new(teams: Vec<(ClientId, TeamInfo)>, config: RoomConfig) -> GameInfo {
       
   104         GameInfo {
       
   105             left_teams: Vec::new(),
       
   106             msg_log: Vec::new(),
       
   107             sync_msg: None,
       
   108             is_paused: false,
       
   109             teams_in_game: teams.len() as u8,
       
   110             teams_at_start: teams,
       
   111             config
       
   112         }
       
   113     }
       
   114 
       
   115     pub fn client_teams(&self, client_id: ClientId) -> impl Iterator<Item = &TeamInfo> + Clone {
       
   116         client_teams_impl(&self.teams_at_start, client_id)
       
   117     }
       
   118 }
       
   119 
       
   120 #[derive(Serialize, Deserialize)]
       
   121 pub struct RoomSave {
       
   122     pub location: String,
       
   123     config: RoomConfig
       
   124 }
       
   125 
       
   126 bitflags!{
       
   127     pub struct RoomFlags: u8 {
       
   128         const FIXED = 0b0000_0001;
       
   129         const RESTRICTED_JOIN = 0b0000_0010;
       
   130         const RESTRICTED_TEAM_ADD = 0b0000_0100;
       
   131         const RESTRICTED_UNREGISTERED_PLAYERS = 0b0000_1000;
       
   132     }
       
   133 }
       
   134 
       
   135 pub struct HWRoom {
       
   136     pub id: RoomId,
       
   137     pub master_id: Option<ClientId>,
       
   138     pub name: String,
       
   139     pub password: Option<String>,
       
   140     pub greeting: String,
       
   141     pub protocol_number: u16,
       
   142     pub flags: RoomFlags,
       
   143 
       
   144     pub players_number: u8,
       
   145     pub default_hedgehog_number: u8,
       
   146     pub team_limit: u8,
       
   147     pub ready_players_number: u8,
       
   148     pub teams: Vec<(ClientId, TeamInfo)>,
       
   149     config: RoomConfig,
       
   150     pub voting: Option<Voting>,
       
   151     pub saves: HashMap<String, RoomSave>,
       
   152     pub game_info: Option<GameInfo>
       
   153 }
       
   154 
       
   155 impl HWRoom {
       
   156     pub fn new(id: RoomId) -> HWRoom {
       
   157         HWRoom {
       
   158             id,
       
   159             master_id: None,
       
   160             name: String::new(),
       
   161             password: None,
       
   162             greeting: "".to_string(),
       
   163             flags: RoomFlags::empty(),
       
   164             protocol_number: 0,
       
   165             players_number: 0,
       
   166             default_hedgehog_number: 4,
       
   167             team_limit: MAX_TEAMS_IN_ROOM,
       
   168             ready_players_number: 0,
       
   169             teams: Vec::new(),
       
   170             config: RoomConfig::new(),
       
   171             voting: None,
       
   172             saves: HashMap::new(),
       
   173             game_info: None
       
   174         }
       
   175     }
       
   176 
       
   177     pub fn hedgehogs_number(&self) -> u8 {
       
   178         self.teams.iter().map(|(_, t)| t.hedgehogs_number).sum()
       
   179     }
       
   180 
       
   181     pub fn addable_hedgehogs(&self) -> u8 {
       
   182         MAX_HEDGEHOGS_IN_ROOM - self.hedgehogs_number()
       
   183     }
       
   184 
       
   185     pub fn add_team(&mut self, owner_id: ClientId, mut team: TeamInfo, preserve_color: bool) -> &TeamInfo {
       
   186         if !preserve_color {
       
   187             team.color = iter::repeat(()).enumerate()
       
   188                 .map(|(i, _)| i as u8).take(u8::max_value() as usize + 1)
       
   189                 .find(|i| self.teams.iter().all(|(_, t)| t.color != *i))
       
   190                 .unwrap_or(0u8)
       
   191         };
       
   192         team.hedgehogs_number = if self.teams.is_empty() {
       
   193             self.default_hedgehog_number
       
   194         } else {
       
   195             self.teams[0].1.hedgehogs_number.min(self.addable_hedgehogs())
       
   196         };
       
   197         self.teams.push((owner_id, team));
       
   198         &self.teams.last().unwrap().1
       
   199     }
       
   200 
       
   201     pub fn remove_team(&mut self, name: &str) {
       
   202         if let Some(index) = self.teams.iter().position(|(_, t)| t.name == name) {
       
   203             self.teams.remove(index);
       
   204         }
       
   205     }
       
   206 
       
   207     pub fn set_hedgehogs_number(&mut self, n: u8) -> Vec<String> {
       
   208         let mut names = Vec::new();
       
   209         let teams = match self.game_info {
       
   210             Some(ref mut info) => &mut info.teams_at_start,
       
   211             None => &mut self.teams
       
   212         };
       
   213 
       
   214         if teams.len() as u8 * n <= MAX_HEDGEHOGS_IN_ROOM {
       
   215             for (_, team) in teams.iter_mut() {
       
   216                 team.hedgehogs_number = n;
       
   217                 names.push(team.name.clone())
       
   218             };
       
   219             self.default_hedgehog_number = n;
       
   220         }
       
   221         names
       
   222     }
       
   223 
       
   224     pub fn find_team_and_owner_mut<F>(&mut self, f: F) -> Option<(ClientId, &mut TeamInfo)>
       
   225         where F: Fn(&TeamInfo) -> bool {
       
   226         self.teams.iter_mut().find(|(_, t)| f(t)).map(|(id, t)| (*id, t))
       
   227     }
       
   228 
       
   229     pub fn find_team<F>(&self, f: F) -> Option<&TeamInfo>
       
   230         where F: Fn(&TeamInfo) -> bool {
       
   231         self.teams.iter().find_map(|(_, t)| Some(t).filter(|t| f(&t)))
       
   232     }
       
   233 
       
   234     pub fn client_teams(&self, client_id: ClientId) -> impl Iterator<Item = &TeamInfo> {
       
   235         client_teams_impl(&self.teams, client_id)
       
   236     }
       
   237 
       
   238     pub fn client_team_indices(&self, client_id: ClientId) -> Vec<u8> {
       
   239         self.teams.iter().enumerate()
       
   240             .filter(move |(_, (id, _))| *id == client_id)
       
   241             .map(|(i, _)| i as u8).collect()
       
   242     }
       
   243 
       
   244     pub fn find_team_owner(&self, team_name: &str) -> Option<(ClientId, &str)> {
       
   245         self.teams.iter().find(|(_, t)| t.name == team_name)
       
   246             .map(|(id, t)| (*id, &t.name[..]))
       
   247     }
       
   248 
       
   249     pub fn find_team_color(&self, owner_id: ClientId) -> Option<u8> {
       
   250         self.client_teams(owner_id).nth(0).map(|t| t.color)
       
   251     }
       
   252 
       
   253     pub fn has_multiple_clans(&self) -> bool {
       
   254         self.teams.iter().min_by_key(|(_, t)| t.color) !=
       
   255             self.teams.iter().max_by_key(|(_, t)| t.color)
       
   256     }
       
   257 
       
   258     pub fn set_config(&mut self, cfg: GameCfg) {
       
   259         let c = &mut self.config;
       
   260         match cfg {
       
   261             FeatureSize(s) => c.feature_size = s,
       
   262             MapType(t) => c.map_type = t,
       
   263             MapGenerator(g) => c.map_generator = g,
       
   264             MazeSize(s) => c.maze_size = s,
       
   265             Seed(s) => c.seed = s,
       
   266             Template(t) => c.template = t,
       
   267 
       
   268             Ammo(n, s) => c.ammo = Ammo {name: n, settings: s},
       
   269             Scheme(n, s) => c.scheme = Scheme {name: n, settings: s},
       
   270             Script(s) => c.script = s,
       
   271             Theme(t) => c.theme = t,
       
   272             DrawnMap(m) => c.drawn_map = Some(m)
       
   273         };
       
   274     }
       
   275 
       
   276     pub fn start_round(&mut self) {
       
   277         if self.game_info.is_none() {
       
   278             self.game_info = Some(GameInfo::new(
       
   279                 self.teams.clone(), self.config.clone()));
       
   280         }
       
   281     }
       
   282 
       
   283     pub fn is_fixed(&self) -> bool {
       
   284         self.flags.contains(RoomFlags::FIXED)
       
   285     }
       
   286     pub fn is_join_restricted(&self) -> bool {
       
   287         self.flags.contains(RoomFlags::RESTRICTED_JOIN)
       
   288     }
       
   289     pub fn is_team_add_restricted(&self) -> bool {
       
   290         self.flags.contains(RoomFlags::RESTRICTED_TEAM_ADD)
       
   291     }
       
   292     pub fn are_unregistered_players_restricted(&self) -> bool {
       
   293         self.flags.contains(RoomFlags::RESTRICTED_UNREGISTERED_PLAYERS)
       
   294     }
       
   295 
       
   296     pub fn set_is_fixed(&mut self, value: bool) {
       
   297         self.flags.set(RoomFlags::FIXED, value)
       
   298     }
       
   299     pub fn set_join_restriction(&mut self, value: bool) {
       
   300         self.flags.set(RoomFlags::RESTRICTED_JOIN, value)
       
   301     }
       
   302     pub fn set_team_add_restriction(&mut self, value: bool) {
       
   303         self.flags.set(RoomFlags::RESTRICTED_TEAM_ADD, value)
       
   304     }
       
   305     pub fn set_unregistered_players_restriction(&mut self, value: bool) {
       
   306         self.flags.set(RoomFlags::RESTRICTED_UNREGISTERED_PLAYERS, value)
       
   307     }
       
   308 
       
   309     fn flags_string(&self) -> String {
       
   310         let mut result = "-".to_string();
       
   311         if self.game_info.is_some()  { result += "g" }
       
   312         if self.password.is_some()   { result += "p" }
       
   313         if self.is_join_restricted() { result += "j" }
       
   314         if self.are_unregistered_players_restricted() {
       
   315             result += "r"
       
   316         }
       
   317         result
       
   318     }
       
   319 
       
   320     pub fn info(&self, master: Option<&HWClient>) -> Vec<String> {
       
   321         let c = &self.config;
       
   322         vec![
       
   323             self.flags_string(),
       
   324             self.name.clone(),
       
   325             self.players_number.to_string(),
       
   326             self.teams.len().to_string(),
       
   327             master.map_or("[]", |c| &c.nick).to_string(),
       
   328             c.map_type.to_string(),
       
   329             c.script.to_string(),
       
   330             c.scheme.name.to_string(),
       
   331             c.ammo.name.to_string()
       
   332         ]
       
   333     }
       
   334 
       
   335     pub fn map_config(&self) -> Vec<String> {
       
   336         match self.game_info {
       
   337             Some(ref info) => map_config_from(&info.config),
       
   338             None => map_config_from(&self.config)
       
   339         }
       
   340     }
       
   341 
       
   342     pub fn game_config(&self) -> Vec<GameCfg> {
       
   343         match self.game_info {
       
   344             Some(ref info) => game_config_from(&info.config),
       
   345             None => game_config_from(&self.config)
       
   346         }
       
   347     }
       
   348 
       
   349     pub fn save_config(&mut self, name: String, location: String) {
       
   350         self.saves.insert(name, RoomSave { location, config: self.config.clone() });
       
   351     }
       
   352 
       
   353     pub fn load_config(&mut self, name: &str) -> Option<&str> {
       
   354         if let Some(save) = self.saves.get(name) {
       
   355             self.config = save.config.clone();
       
   356             Some(&save.location[..])
       
   357         } else {
       
   358             None
       
   359         }
       
   360     }
       
   361 
       
   362     pub fn delete_config(&mut self, name: &str) -> bool {
       
   363         self.saves.remove(name).is_some()
       
   364     }
       
   365 
       
   366     pub fn get_saves(&self) -> Result<String, serde_yaml::Error> {
       
   367         serde_yaml::to_string(&(&self.greeting, &self.saves))
       
   368     }
       
   369 
       
   370     pub fn set_saves(&mut self, text: &str) -> Result<(), serde_yaml::Error> {
       
   371         serde_yaml::from_str::<(String, HashMap<String, RoomSave>)>(text).map(|(greeting, saves)| {
       
   372             self.greeting = greeting;
       
   373             self.saves = saves;
       
   374         })
       
   375     }
       
   376 
       
   377     pub fn team_info(owner: &HWClient, team: &TeamInfo) -> Vec<String> {
       
   378         let mut info = vec![
       
   379             team.name.clone(),
       
   380             team.grave.clone(),
       
   381             team.fort.clone(),
       
   382             team.voice_pack.clone(),
       
   383             team.flag.clone(),
       
   384             owner.nick.clone(),
       
   385             team.difficulty.to_string()];
       
   386         let hogs = team.hedgehogs.iter().flat_map(|h|
       
   387             iter::once(h.name.clone()).chain(iter::once(h.hat.clone())));
       
   388         info.extend(hogs);
       
   389         info
       
   390     }
       
   391 }