rust/hedgewars-server/src/server/demo.rs
changeset 15796 2939d8599418
parent 15581 ab095fc0256c
child 15804 747278149393
equal deleted inserted replaced
15795:40929af15167 15796:2939d8599418
     1 use crate::{
     1 use crate::{
     2     core::types::{GameCfg, HedgehogInfo, TeamInfo},
     2     core::types::{Ammo, GameCfg, HedgehogInfo, Replay, RoomConfig, Scheme, TeamInfo},
     3     server::haskell::HaskellValue,
     3     server::haskell::HaskellValue,
     4 };
     4 };
     5 use std::{
     5 use std::{
     6     collections::HashMap,
     6     collections::HashMap,
     7     fs,
     7     fs,
    15     config: Vec<GameCfg>,
    15     config: Vec<GameCfg>,
    16     messages: Vec<String>,
    16     messages: Vec<String>,
    17 }
    17 }
    18 
    18 
    19 impl Demo {
    19 impl Demo {
    20     fn save(self, filename: String) -> io::Result<()> {
       
    21         let text = format!("{}", demo_to_haskell(self));
       
    22         let mut file = fs::File::open(filename)?;
       
    23         file.write(text.as_bytes())?;
       
    24         Ok(())
       
    25     }
       
    26 
       
    27     fn load(filename: String) -> io::Result<Self> {
       
    28         let mut file = fs::File::open(filename)?;
       
    29         let mut bytes = vec![];
       
    30         file.read_to_end(&mut bytes)?;
       
    31         match super::haskell::parse(&bytes[..]) {
       
    32             Ok((_, value)) => haskell_to_demo(value).ok_or(io::Error::new(
       
    33                 io::ErrorKind::InvalidData,
       
    34                 "Invalid demo structure",
       
    35             )),
       
    36             Err(_) => Err(io::Error::new(
       
    37                 io::ErrorKind::InvalidData,
       
    38                 "Unable to parse file",
       
    39             )),
       
    40         }
       
    41     }
       
    42 
       
    43     fn load_hwd(filename: String) -> io::Result<Self> {
    20     fn load_hwd(filename: String) -> io::Result<Self> {
    44         let file = fs::File::open(filename)?;
    21         let file = fs::File::open(filename)?;
    45         let mut reader = io::BufReader::new(file);
    22         let mut reader = io::BufReader::new(file);
    46 
    23 
    47         #[inline]
    24         #[inline]
   271             messages,
   248             messages,
   272         })
   249         })
   273     }
   250     }
   274 }
   251 }
   275 
   252 
   276 fn demo_to_haskell(mut demo: Demo) -> HaskellValue {
   253 fn replay_to_haskell(mut replay: Replay) -> HaskellValue {
   277     use HaskellValue as Hs;
   254     use HaskellValue as Hs;
   278 
   255 
   279     let mut teams = Vec::with_capacity(demo.teams.len());
   256     let mut teams = Vec::with_capacity(replay.teams.len());
   280     for team in demo.teams {
   257     for team in replay.teams {
   281         let mut fields = HashMap::<String, HaskellValue>::new();
   258         let mut fields = HashMap::<String, HaskellValue>::new();
   282 
   259 
   283         fields.insert("teamowner".to_string(), Hs::String(team.owner));
   260         fields.insert("teamowner".to_string(), Hs::String(team.owner));
   284         fields.insert("teamname".to_string(), Hs::String(team.name));
   261         fields.insert("teamname".to_string(), Hs::String(team.name));
   285         fields.insert("teamcolor".to_string(), Hs::Number(team.color));
   262         fields.insert("teamcolor".to_string(), Hs::Number(team.color));
   314             Hs::String(name.to_string()),
   291             Hs::String(name.to_string()),
   315             Hs::String(value),
   292             Hs::String(value),
   316         ]));
   293         ]));
   317     };
   294     };
   318 
   295 
   319     for config_item in &demo.config {
   296     let config = replay.config;
   320         match config_item {
   297 
   321             GameCfg::FeatureSize(size) => save_map_config("FEATURE_SIZE", size.to_string()),
   298     save_map_config("FEATURE_SIZE", config.feature_size.to_string());
   322             GameCfg::MapType(map_type) => save_map_config("MAP", map_type.clone()),
   299     save_map_config("MAP", config.map_type);
   323             GameCfg::MapGenerator(generator) => save_map_config("MAPGEN", generator.to_string()),
   300     save_map_config("MAPGEN", config.map_generator.to_string());
   324             GameCfg::MazeSize(size) => save_map_config("MAZE_SIZE", size.to_string()),
   301     save_map_config("MAZE_SIZE", config.maze_size.to_string());
   325             GameCfg::Seed(seed) => save_map_config("SEED", seed.clone()),
   302     save_map_config("SEED", config.seed);
   326             GameCfg::Template(template) => save_map_config("TEMPLATE", template.to_string()),
   303     save_map_config("TEMPLATE", config.template.to_string());
   327             GameCfg::DrawnMap(map) => save_map_config("DRAWNMAP", map.clone()),
   304     if let Some(drawn_map) = config.drawn_map {
   328             _ => (),
   305         save_map_config("DRAWNMAP", drawn_map);
   329         }
       
   330     }
   306     }
   331 
   307 
   332     let mut save_game_config = |name: &str, mut value: Vec<String>| {
   308     let mut save_game_config = |name: &str, mut value: Vec<String>| {
   333         map_config.push(Hs::Tuple(vec![
   309         map_config.push(Hs::Tuple(vec![
   334             Hs::String(name.to_string()),
   310             Hs::String(name.to_string()),
   335             Hs::List(value.drain(..).map(Hs::String).collect()),
   311             Hs::List(value.drain(..).map(Hs::String).collect()),
   336         ]));
   312         ]));
   337     };
   313     };
   338 
   314 
   339     for config_item in &demo.config {
   315     match config.ammo {
   340         match config_item {
   316         Ammo {
   341             GameCfg::Ammo(name, Some(ammo)) => {
   317             name,
   342                 save_game_config("AMMO", vec![name.clone(), ammo.clone()])
   318             settings: Some(settings),
   343             }
   319         } => save_game_config("AMMO", vec![name, settings.clone()]),
   344             GameCfg::Ammo(name, None) => save_game_config("AMMO", vec![name.clone()]),
   320         Ammo { name, .. } => save_game_config("AMMO", vec![name.clone()]),
   345             GameCfg::Scheme(name, scheme) => {
   321     }
   346                 let mut values = vec![name.clone()];
   322 
   347                 values.extend_from_slice(&scheme);
   323     match config.scheme {
   348                 save_game_config("SCHEME", values);
   324         Scheme { name, settings } => {
   349             }
   325             let mut values = vec![name];
   350             GameCfg::Script(script) => save_game_config("SCRIPT", vec![script.clone()]),
   326             values.extend_from_slice(&settings);
   351             GameCfg::Theme(theme) => save_game_config("THEME", vec![theme.clone()]),
   327             save_game_config("SCHEME", values);
   352             _ => (),
   328         }
   353         }
   329     }
   354     }
   330 
       
   331     save_game_config("SCRIPT", vec![config.script]);
       
   332     save_game_config("THEME", vec![config.theme]);
   355 
   333 
   356     Hs::Tuple(vec![
   334     Hs::Tuple(vec![
   357         Hs::List(teams),
   335         Hs::List(teams),
   358         Hs::List(map_config),
   336         Hs::List(map_config),
   359         Hs::List(game_config),
   337         Hs::List(game_config),
   360         Hs::List(demo.messages.drain(..).map(Hs::String).collect()),
   338         Hs::List(replay.message_log.drain(..).map(Hs::String).collect()),
   361     ])
   339     ])
   362 }
   340 }
   363 
   341 
   364 fn haskell_to_demo(value: HaskellValue) -> Option<Demo> {
   342 fn haskell_to_replay(value: HaskellValue) -> Option<Replay> {
   365     use HaskellValue::*;
   343     use HaskellValue::*;
       
   344     let mut config = RoomConfig::new();
   366     let mut lists = value.into_tuple()?;
   345     let mut lists = value.into_tuple()?;
   367     let mut lists_iter = lists.drain(..);
   346     let mut lists_iter = lists.drain(..);
   368 
   347 
   369     let teams_list = lists_iter.next()?.into_list()?;
   348     let teams_list = lists_iter.next()?.into_list()?;
   370     let map_config = lists_iter.next()?.into_list()?;
   349     let map_config = lists_iter.next()?.into_list()?;
   407             }
   386             }
   408         }
   387         }
   409         teams.push(team_info)
   388         teams.push(team_info)
   410     }
   389     }
   411 
   390 
   412     let mut config = Vec::with_capacity(map_config.len() + game_config.len());
       
   413 
       
   414     for item in map_config {
   391     for item in map_config {
   415         let mut tuple = item.into_tuple()?;
   392         let mut tuple = item.into_tuple()?;
   416         let mut tuple_iter = tuple.drain(..);
   393         let mut tuple_iter = tuple.drain(..);
   417         let name = tuple_iter.next()?.into_string()?;
   394         let name = tuple_iter.next()?.into_string()?;
   418         let value = tuple_iter.next()?.into_string()?;
   395         let value = tuple_iter.next()?.into_string()?;
   419 
   396 
   420         let config_item = match &name[..] {
   397         match &name[..] {
   421             "FEATURE_SIZE" => GameCfg::FeatureSize(u32::from_str(&value).ok()?),
   398             "FEATURE_SIZE" => config.feature_size = u32::from_str(&value).ok()?,
   422             "MAP" => GameCfg::MapType(value),
   399             "MAP" => config.map_type = value,
   423             "MAPGEN" => GameCfg::MapGenerator(u32::from_str(&value).ok()?),
   400             "MAPGEN" => config.map_generator = u32::from_str(&value).ok()?,
   424             "MAZE_SIZE" => GameCfg::MazeSize(u32::from_str(&value).ok()?),
   401             "MAZE_SIZE" => config.maze_size = u32::from_str(&value).ok()?,
   425             "SEED" => GameCfg::Seed(value),
   402             "SEED" => config.seed = value,
   426             "TEMPLATE" => GameCfg::Template(u32::from_str(&value).ok()?),
   403             "TEMPLATE" => config.template = u32::from_str(&value).ok()?,
   427             "DRAWNMAP" => GameCfg::DrawnMap(value),
   404             "DRAWNMAP" => config.drawn_map = Some(value),
   428             _ => None?,
   405             _ => {}
   429         };
   406         };
   430         config.push(config_item);
       
   431     }
   407     }
   432 
   408 
   433     for item in game_config {
   409     for item in game_config {
   434         let mut tuple = item.into_tuple()?;
   410         let mut tuple = item.into_tuple()?;
   435         let mut tuple_iter = tuple.drain(..);
   411         let mut tuple_iter = tuple.drain(..);
   436         let name = tuple_iter.next()?.into_string()?;
   412         let name = tuple_iter.next()?.into_string()?;
   437         let mut value = tuple_iter.next()?.into_list()?;
   413         let mut value = tuple_iter.next()?.into_list()?;
   438         let mut value_iter = value.drain(..);
   414         let mut value_iter = value.drain(..);
   439 
   415 
   440         let config_item = match &name[..] {
   416         let config_item = match &name[..] {
   441             "AMMO" => GameCfg::Ammo(
   417             "AMMO" => {
   442                 value_iter.next()?.into_string()?,
   418                 config.ammo = Ammo {
   443                 value_iter.next().and_then(|v| v.into_string()),
   419                     name: value_iter.next()?.into_string()?,
   444             ),
   420                     settings: value_iter.next().and_then(|v| v.into_string()),
   445             "SCHEME" => GameCfg::Scheme(
   421                 }
   446                 value_iter.next()?.into_string()?,
   422             }
   447                 value_iter.filter_map(|v| v.into_string()).collect(),
   423             "SCHEME" => {
   448             ),
   424                 config.scheme = Scheme {
   449             "SCRIPT" => GameCfg::Script(value_iter.next()?.into_string()?),
   425                     name: value_iter.next()?.into_string()?,
   450             "THEME" => GameCfg::Theme(value_iter.next()?.into_string()?),
   426                     settings: value_iter.filter_map(|v| v.into_string()).collect(),
       
   427                 }
       
   428             }
       
   429             "SCRIPT" => config.script = value_iter.next()?.into_string()?,
       
   430             "THEME" => config.theme = value_iter.next()?.into_string()?,
   451             _ => None?,
   431             _ => None?,
   452         };
   432         };
   453         config.push(config_item);
       
   454     }
   433     }
   455 
   434 
   456     let mut messages = Vec::with_capacity(engine_messages.len());
   435     let mut messages = Vec::with_capacity(engine_messages.len());
   457 
   436 
   458     for message in engine_messages {
   437     for message in engine_messages {
   459         messages.push(message.into_string()?);
   438         messages.push(message.into_string()?);
   460     }
   439     }
   461 
   440 
   462     Some(Demo {
   441     Some(Replay {
       
   442         config,
   463         teams,
   443         teams,
   464         config,
   444         message_log: messages,
   465         messages,
       
   466     })
   445     })
   467 }
   446 }
       
   447 
       
   448 impl Replay {
       
   449     pub fn save(self, filename: String) -> io::Result<()> {
       
   450         let text = format!("{}", replay_to_haskell(self));
       
   451         let mut file = fs::File::open(filename)?;
       
   452         file.write(text.as_bytes())?;
       
   453         Ok(())
       
   454     }
       
   455 
       
   456     pub fn load(filename: &str) -> io::Result<Self> {
       
   457         let mut file = fs::File::open(filename)?;
       
   458         let mut bytes = vec![];
       
   459         file.read_to_end(&mut bytes)?;
       
   460         match super::haskell::parse(&bytes[..]) {
       
   461             Ok((_, value)) => haskell_to_replay(value).ok_or(io::Error::new(
       
   462                 io::ErrorKind::InvalidData,
       
   463                 "Invalid replay structure",
       
   464             )),
       
   465             Err(_) => Err(io::Error::new(
       
   466                 io::ErrorKind::InvalidData,
       
   467                 "Unable to parse file",
       
   468             )),
       
   469         }
       
   470     }
       
   471 }