# HG changeset patch # User alfadur # Date 1583532277 -10800 # Node ID 863059f61793dabba93dde88c22d8cd5d7f3a5ec # Parent 6b9655be1e7ffa4f121951b6904055eb8a235b54 add loading config from hwd to server diff -r 6b9655be1e7f -r 863059f61793 rust/hedgewars-server/src/core/types.rs --- a/rust/hedgewars-server/src/core/types.rs Tue Feb 11 01:33:31 2020 +0100 +++ b/rust/hedgewars-server/src/core/types.rs Sat Mar 07 01:04:37 2020 +0300 @@ -28,7 +28,7 @@ DrawnMap(String), } -#[derive(PartialEq, Eq, Clone, Debug)] +#[derive(PartialEq, Eq, Clone, Debug, Default)] pub struct TeamInfo { pub owner: String, pub name: String, @@ -42,7 +42,7 @@ pub hedgehogs: [HedgehogInfo; MAX_HEDGEHOGS_PER_TEAM as usize], } -#[derive(PartialEq, Eq, Clone, Debug)] +#[derive(PartialEq, Eq, Clone, Debug, Default)] pub struct HedgehogInfo { pub name: String, pub hat: String, diff -r 6b9655be1e7f -r 863059f61793 rust/hedgewars-server/src/server.rs --- a/rust/hedgewars-server/src/server.rs Tue Feb 11 01:33:31 2020 +0100 +++ b/rust/hedgewars-server/src/server.rs Sat Mar 07 01:04:37 2020 +0300 @@ -1,5 +1,6 @@ #[cfg(feature = "official-server")] mod database; +pub mod demo; #[cfg(feature = "official-server")] pub mod io; pub mod network; diff -r 6b9655be1e7f -r 863059f61793 rust/hedgewars-server/src/server/demo.rs --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rust/hedgewars-server/src/server/demo.rs Sat Mar 07 01:04:37 2020 +0300 @@ -0,0 +1,256 @@ +use crate::core::types::{GameCfg, TeamInfo}; + +use std::{ + fs, + io::{self, BufReader, Read, Write}, + str::FromStr, +}; + +struct Demo { + teams: Vec, + config: Vec, + messages: Vec, +} + +impl Demo { + fn save(&self, file: String) -> io::Result<()> { + Ok(unimplemented!()) + } + + fn load(file: String) -> io::Result { + Ok(unimplemented!()) + } + + fn load_hwd(file: String) -> io::Result { + let datum = fs::File::open(file)?; + let mut reader = io::BufReader::new(datum); + + #[inline] + fn error(cause: &str) -> io::Result { + Err(io::Error::new(io::ErrorKind::InvalidData, cause)) + } + + fn read_command<'a>( + reader: &mut BufReader, + buffer: &'a mut [u8], + ) -> io::Result> { + use io::BufRead; + + let mut size = [0u8; 1]; + if reader.read(&mut size)? == 0 { + Ok(None) + } else { + let text = &mut buffer[0..size[0] as _]; + + if reader.read(text)? < text.len() { + Err(io::Error::new( + io::ErrorKind::UnexpectedEof, + "Incomplete command", + )) + } else { + std::str::from_utf8(text).map(Some).map_err(|e| { + io::Error::new(io::ErrorKind::InvalidInput, "The string is not UTF8") + }) + } + } + } + + fn get_script_name(arg: &str) -> io::Result { + const PREFIX: &str = "Scripts/Multiplayer/"; + const SUFFIX: &str = ".lua"; + if arg.starts_with(PREFIX) && arg.ends_with(SUFFIX) { + let script = arg[PREFIX.len()..arg.len() - SUFFIX.len()].to_string(); + script.replace('_', " "); + Ok(script) + } else { + error("Script is not multiplayer") + } + } + + fn get_game_flags(arg: &str) -> io::Result> { + const FLAGS: &[u32] = &[ + 0x0000_1000, + 0x0000_0010, + 0x0000_0004, + 0x0000_0008, + 0x0000_0020, + 0x0000_0040, + 0x0000_0080, + 0x0000_0100, + 0x0000_0200, + 0x0000_0400, + 0x0000_0800, + 0x0000_2000, + 0x0000_4000, + 0x0000_8000, + 0x0001_0000, + 0x0002_0000, + 0x0004_0000, + 0x0008_0000, + 0x0010_0000, + 0x0020_0000, + 0x0040_0000, + 0x0080_0000, + 0x0100_0000, + 0x0200_0000, + 0x0400_0000, + ]; + + let flags = u32::from_str(arg).unwrap_or_default(); + let game_flags = FLAGS + .iter() + .map(|flag| (flag & flags != 0).to_string()) + .collect(); + + Ok(game_flags) + } + + let mut config = Vec::new(); + let mut buffer = [0u8; u8::max_value() as _]; + + let mut game_flags = vec![]; + let mut scheme_properties: Vec<_> = [ + "1", "1000", "100", "1", "1", "1000", "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", + "1", "", + ] + .iter() + .map(|p| p.to_string()) + .collect(); + const SCHEME_PROPERTY_NAMES: &[&str] = &[ + "$damagepct", + "$turntime", + "", + "$sd_turns", + "$casefreq", + "$minestime", + "$minesnum", + "$minedudpct", + "$explosives", + "$airmines", + "$healthprob", + "$hcaseamount", + "$waterrise", + "$healthdec", + "$ropepct", + "$getawaytime", + "$worldedge", + ]; + const AMMO_PROPERTY_NAMES: &[&str] = &["eammloadt", "eammprob", "eammdelay", "eammreinf"]; + let mut ammo_settings = vec![String::new(); AMMO_PROPERTY_NAMES.len()]; + let mut teams = vec![]; + let mut hog_index = 7usize; + + let mut messages = vec![]; + + while let Some(cmd) = read_command(&mut reader, &mut buffer)? { + if let Some(index) = cmd.find(' ') { + match cmd.chars().next().unwrap_or_default() { + 'T' => { + if cmd != "TD" { + let () = error("Not a demo file")?; + } + } + 'e' => { + if let Some(index) = cmd.find(' ') { + let (name, arg) = cmd.split_at(index); + match name { + "script" => config.push(GameCfg::Script(get_script_name(arg)?)), + "map" => config.push(GameCfg::MapType(arg.to_string())), + "theme" => config.push(GameCfg::Theme(arg.to_string())), + "seed" => config.push(GameCfg::Seed(arg.to_string())), + "$gmflags" => game_flags = get_game_flags(arg)?, + "$scriptparam" => { + *scheme_properties.last_mut().unwrap() = arg.to_string() + } + "$template_filter" => config.push(GameCfg::Template( + u32::from_str(arg).unwrap_or_default(), + )), + "$feature_size" => config.push(GameCfg::FeatureSize( + u32::from_str(arg).unwrap_or_default(), + )), + "$map_gen" => config.push(GameCfg::MapGenerator( + u32::from_str(arg).unwrap_or_default(), + )), + "$maze_size" => config.push(GameCfg::MazeSize( + u32::from_str(arg).unwrap_or_default(), + )), + "addteam" => { + if let parts = arg.splitn(3, ' ').collect::>() { + let color = parts.get(1).unwrap_or(&"1"); + let name = parts.get(2).unwrap_or(&"Unnamed"); + teams.push(TeamInfo { + color: (u32::from_str(color).unwrap_or(2113696) + / 2113696 + - 1) + as u8, + name: name.to_string(), + ..TeamInfo::default() + }) + }; + } + "fort" => teams + .last_mut() + .iter_mut() + .for_each(|t| t.fort = arg.to_string()), + "grave" => teams + .last_mut() + .iter_mut() + .for_each(|t| t.grave = arg.to_string()), + "addhh" => { + hog_index = (hog_index + 1) % 8; + if let parts = arg.splitn(3, ' ').collect::>() { + let health = parts.get(1).unwrap_or(&"100"); + teams.last_mut().iter_mut().for_each(|t| { + if let Some(difficulty) = parts.get(0) { + t.difficulty = + u8::from_str(difficulty).unwrap_or(0); + } + if let Some(init_health) = parts.get(1) { + scheme_properties[2] = init_health.to_string(); + } + t.hedgehogs_number = (hog_index + 1) as u8; + t.hedgehogs[hog_index].name = + parts.get(2).unwrap_or(&"Unnamed").to_string() + }); + } + } + "hat" => { + teams + .last_mut() + .iter_mut() + .for_each(|t| t.hedgehogs[hog_index].hat = arg.to_string()); + } + name => { + if let Some(index) = + SCHEME_PROPERTY_NAMES.iter().position(|n| *n == name) + { + scheme_properties[index] = arg.to_string(); + } else if let Some(index) = + AMMO_PROPERTY_NAMES.iter().position(|n| *n == name) + { + ammo_settings[index] = arg.to_string(); + } + } + } + } + } + '+' => {} + _ => (), + } + } + } + + game_flags.append(&mut scheme_properties); + config.push(GameCfg::Scheme("ADHOG_SCHEME".to_string(), game_flags)); + config.push(GameCfg::Ammo( + "ADHOG_AMMO".to_string(), + Some(ammo_settings.concat()), + )); + + Ok(Demo { + teams, + config, + messages, + }) + } +}