rust/hedgewars-server/src/server/demo.rs
changeset 15796 2939d8599418
parent 15581 ab095fc0256c
child 15804 747278149393
--- a/rust/hedgewars-server/src/server/demo.rs	Sat Jun 19 17:48:10 2021 +0300
+++ b/rust/hedgewars-server/src/server/demo.rs	Sun Jun 20 01:45:18 2021 +0300
@@ -1,5 +1,5 @@
 use crate::{
-    core::types::{GameCfg, HedgehogInfo, TeamInfo},
+    core::types::{Ammo, GameCfg, HedgehogInfo, Replay, RoomConfig, Scheme, TeamInfo},
     server::haskell::HaskellValue,
 };
 use std::{
@@ -17,29 +17,6 @@
 }
 
 impl Demo {
-    fn save(self, filename: String) -> io::Result<()> {
-        let text = format!("{}", demo_to_haskell(self));
-        let mut file = fs::File::open(filename)?;
-        file.write(text.as_bytes())?;
-        Ok(())
-    }
-
-    fn load(filename: String) -> io::Result<Self> {
-        let mut file = fs::File::open(filename)?;
-        let mut bytes = vec![];
-        file.read_to_end(&mut bytes)?;
-        match super::haskell::parse(&bytes[..]) {
-            Ok((_, value)) => haskell_to_demo(value).ok_or(io::Error::new(
-                io::ErrorKind::InvalidData,
-                "Invalid demo structure",
-            )),
-            Err(_) => Err(io::Error::new(
-                io::ErrorKind::InvalidData,
-                "Unable to parse file",
-            )),
-        }
-    }
-
     fn load_hwd(filename: String) -> io::Result<Self> {
         let file = fs::File::open(filename)?;
         let mut reader = io::BufReader::new(file);
@@ -273,11 +250,11 @@
     }
 }
 
-fn demo_to_haskell(mut demo: Demo) -> HaskellValue {
+fn replay_to_haskell(mut replay: Replay) -> HaskellValue {
     use HaskellValue as Hs;
 
-    let mut teams = Vec::with_capacity(demo.teams.len());
-    for team in demo.teams {
+    let mut teams = Vec::with_capacity(replay.teams.len());
+    for team in replay.teams {
         let mut fields = HashMap::<String, HaskellValue>::new();
 
         fields.insert("teamowner".to_string(), Hs::String(team.owner));
@@ -316,17 +293,16 @@
         ]));
     };
 
-    for config_item in &demo.config {
-        match config_item {
-            GameCfg::FeatureSize(size) => save_map_config("FEATURE_SIZE", size.to_string()),
-            GameCfg::MapType(map_type) => save_map_config("MAP", map_type.clone()),
-            GameCfg::MapGenerator(generator) => save_map_config("MAPGEN", generator.to_string()),
-            GameCfg::MazeSize(size) => save_map_config("MAZE_SIZE", size.to_string()),
-            GameCfg::Seed(seed) => save_map_config("SEED", seed.clone()),
-            GameCfg::Template(template) => save_map_config("TEMPLATE", template.to_string()),
-            GameCfg::DrawnMap(map) => save_map_config("DRAWNMAP", map.clone()),
-            _ => (),
-        }
+    let config = replay.config;
+
+    save_map_config("FEATURE_SIZE", config.feature_size.to_string());
+    save_map_config("MAP", config.map_type);
+    save_map_config("MAPGEN", config.map_generator.to_string());
+    save_map_config("MAZE_SIZE", config.maze_size.to_string());
+    save_map_config("SEED", config.seed);
+    save_map_config("TEMPLATE", config.template.to_string());
+    if let Some(drawn_map) = config.drawn_map {
+        save_map_config("DRAWNMAP", drawn_map);
     }
 
     let mut save_game_config = |name: &str, mut value: Vec<String>| {
@@ -336,33 +312,36 @@
         ]));
     };
 
-    for config_item in &demo.config {
-        match config_item {
-            GameCfg::Ammo(name, Some(ammo)) => {
-                save_game_config("AMMO", vec![name.clone(), ammo.clone()])
-            }
-            GameCfg::Ammo(name, None) => save_game_config("AMMO", vec![name.clone()]),
-            GameCfg::Scheme(name, scheme) => {
-                let mut values = vec![name.clone()];
-                values.extend_from_slice(&scheme);
-                save_game_config("SCHEME", values);
-            }
-            GameCfg::Script(script) => save_game_config("SCRIPT", vec![script.clone()]),
-            GameCfg::Theme(theme) => save_game_config("THEME", vec![theme.clone()]),
-            _ => (),
+    match config.ammo {
+        Ammo {
+            name,
+            settings: Some(settings),
+        } => save_game_config("AMMO", vec![name, settings.clone()]),
+        Ammo { name, .. } => save_game_config("AMMO", vec![name.clone()]),
+    }
+
+    match config.scheme {
+        Scheme { name, settings } => {
+            let mut values = vec![name];
+            values.extend_from_slice(&settings);
+            save_game_config("SCHEME", values);
         }
     }
 
+    save_game_config("SCRIPT", vec![config.script]);
+    save_game_config("THEME", vec![config.theme]);
+
     Hs::Tuple(vec![
         Hs::List(teams),
         Hs::List(map_config),
         Hs::List(game_config),
-        Hs::List(demo.messages.drain(..).map(Hs::String).collect()),
+        Hs::List(replay.message_log.drain(..).map(Hs::String).collect()),
     ])
 }
 
-fn haskell_to_demo(value: HaskellValue) -> Option<Demo> {
+fn haskell_to_replay(value: HaskellValue) -> Option<Replay> {
     use HaskellValue::*;
+    let mut config = RoomConfig::new();
     let mut lists = value.into_tuple()?;
     let mut lists_iter = lists.drain(..);
 
@@ -409,25 +388,22 @@
         teams.push(team_info)
     }
 
-    let mut config = Vec::with_capacity(map_config.len() + game_config.len());
-
     for item in map_config {
         let mut tuple = item.into_tuple()?;
         let mut tuple_iter = tuple.drain(..);
         let name = tuple_iter.next()?.into_string()?;
         let value = tuple_iter.next()?.into_string()?;
 
-        let config_item = match &name[..] {
-            "FEATURE_SIZE" => GameCfg::FeatureSize(u32::from_str(&value).ok()?),
-            "MAP" => GameCfg::MapType(value),
-            "MAPGEN" => GameCfg::MapGenerator(u32::from_str(&value).ok()?),
-            "MAZE_SIZE" => GameCfg::MazeSize(u32::from_str(&value).ok()?),
-            "SEED" => GameCfg::Seed(value),
-            "TEMPLATE" => GameCfg::Template(u32::from_str(&value).ok()?),
-            "DRAWNMAP" => GameCfg::DrawnMap(value),
-            _ => None?,
+        match &name[..] {
+            "FEATURE_SIZE" => config.feature_size = u32::from_str(&value).ok()?,
+            "MAP" => config.map_type = value,
+            "MAPGEN" => config.map_generator = u32::from_str(&value).ok()?,
+            "MAZE_SIZE" => config.maze_size = u32::from_str(&value).ok()?,
+            "SEED" => config.seed = value,
+            "TEMPLATE" => config.template = u32::from_str(&value).ok()?,
+            "DRAWNMAP" => config.drawn_map = Some(value),
+            _ => {}
         };
-        config.push(config_item);
     }
 
     for item in game_config {
@@ -438,19 +414,22 @@
         let mut value_iter = value.drain(..);
 
         let config_item = match &name[..] {
-            "AMMO" => GameCfg::Ammo(
-                value_iter.next()?.into_string()?,
-                value_iter.next().and_then(|v| v.into_string()),
-            ),
-            "SCHEME" => GameCfg::Scheme(
-                value_iter.next()?.into_string()?,
-                value_iter.filter_map(|v| v.into_string()).collect(),
-            ),
-            "SCRIPT" => GameCfg::Script(value_iter.next()?.into_string()?),
-            "THEME" => GameCfg::Theme(value_iter.next()?.into_string()?),
+            "AMMO" => {
+                config.ammo = Ammo {
+                    name: value_iter.next()?.into_string()?,
+                    settings: value_iter.next().and_then(|v| v.into_string()),
+                }
+            }
+            "SCHEME" => {
+                config.scheme = Scheme {
+                    name: value_iter.next()?.into_string()?,
+                    settings: value_iter.filter_map(|v| v.into_string()).collect(),
+                }
+            }
+            "SCRIPT" => config.script = value_iter.next()?.into_string()?,
+            "THEME" => config.theme = value_iter.next()?.into_string()?,
             _ => None?,
         };
-        config.push(config_item);
     }
 
     let mut messages = Vec::with_capacity(engine_messages.len());
@@ -459,9 +438,34 @@
         messages.push(message.into_string()?);
     }
 
-    Some(Demo {
+    Some(Replay {
+        config,
         teams,
-        config,
-        messages,
+        message_log: messages,
     })
 }
+
+impl Replay {
+    pub fn save(self, filename: String) -> io::Result<()> {
+        let text = format!("{}", replay_to_haskell(self));
+        let mut file = fs::File::open(filename)?;
+        file.write(text.as_bytes())?;
+        Ok(())
+    }
+
+    pub fn load(filename: &str) -> io::Result<Self> {
+        let mut file = fs::File::open(filename)?;
+        let mut bytes = vec![];
+        file.read_to_end(&mut bytes)?;
+        match super::haskell::parse(&bytes[..]) {
+            Ok((_, value)) => haskell_to_replay(value).ok_or(io::Error::new(
+                io::ErrorKind::InvalidData,
+                "Invalid replay structure",
+            )),
+            Err(_) => Err(io::Error::new(
+                io::ErrorKind::InvalidData,
+                "Unable to parse file",
+            )),
+        }
+    }
+}