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