diff -r b06b33cf0a89 -r 747278149393 rust/hedgewars-network-protocol/src/types.rs --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rust/hedgewars-network-protocol/src/types.rs Wed Jun 23 23:41:51 2021 +0200 @@ -0,0 +1,358 @@ +use serde_derive::{Deserialize, Serialize}; + +pub const MAX_HEDGEHOGS_PER_TEAM: u8 = 8; + +#[derive(PartialEq, Eq, Clone, Debug)] +pub enum ServerVar { + MOTDNew(String), + MOTDOld(String), + LatestProto(u16), +} + +#[derive(PartialEq, Eq, Clone, Debug)] +pub enum GameCfg { + FeatureSize(u32), + MapType(String), + MapGenerator(u32), + MazeSize(u32), + Seed(String), + Template(u32), + + Ammo(String, Option), + Scheme(String, Vec), + Script(String), + Theme(String), + DrawnMap(String), +} + +#[derive(PartialEq, Eq, Clone, Debug, Default)] +pub struct TeamInfo { + pub owner: String, + pub name: String, + pub color: u8, + pub grave: String, + pub fort: String, + pub voice_pack: String, + pub flag: String, + pub difficulty: u8, + pub hedgehogs_number: u8, + pub hedgehogs: [HedgehogInfo; MAX_HEDGEHOGS_PER_TEAM as usize], +} + +#[derive(PartialEq, Eq, Clone, Debug, Default)] +pub struct HedgehogInfo { + pub name: String, + pub hat: String, +} + +#[derive(Clone, Serialize, Deserialize, Debug)] +pub struct Ammo { + pub name: String, + pub settings: Option, +} + +#[derive(Clone, Serialize, Deserialize, Debug)] +pub struct Scheme { + pub name: String, + pub settings: Vec, +} + +#[derive(Clone, Serialize, Deserialize, Debug)] +pub struct RoomConfig { + pub feature_size: u32, + pub map_type: String, + pub map_generator: u32, + pub maze_size: u32, + pub seed: String, + pub template: u32, + + pub ammo: Ammo, + pub scheme: Scheme, + pub script: String, + pub theme: String, + pub drawn_map: Option, +} + +impl RoomConfig { + pub fn new() -> RoomConfig { + RoomConfig { + feature_size: 12, + map_type: "+rnd+".to_string(), + map_generator: 0, + maze_size: 0, + seed: "seed".to_string(), + template: 0, + + ammo: Ammo { + name: "Default".to_string(), + settings: None, + }, + scheme: Scheme { + name: "Default".to_string(), + settings: Vec::new(), + }, + script: "Normal".to_string(), + theme: "\u{1f994}".to_string(), + drawn_map: None, + } + } + + pub fn set_config(&mut self, cfg: GameCfg) { + match cfg { + GameCfg::FeatureSize(s) => self.feature_size = s, + GameCfg::MapType(t) => self.map_type = t, + GameCfg::MapGenerator(g) => self.map_generator = g, + GameCfg::MazeSize(s) => self.maze_size = s, + GameCfg::Seed(s) => self.seed = s, + GameCfg::Template(t) => self.template = t, + + GameCfg::Ammo(n, s) => { + self.ammo = Ammo { + name: n, + settings: s, + } + } + GameCfg::Scheme(n, s) => { + self.scheme = Scheme { + name: n, + settings: s, + } + } + GameCfg::Script(s) => self.script = s, + GameCfg::Theme(t) => self.theme = t, + GameCfg::DrawnMap(m) => self.drawn_map = Some(m), + }; + } + + pub fn to_map_config(&self) -> Vec { + vec![ + self.feature_size.to_string(), + self.map_type.to_string(), + self.map_generator.to_string(), + self.maze_size.to_string(), + self.seed.to_string(), + self.template.to_string(), + ] + } + + pub fn to_game_config(&self) -> Vec { + use GameCfg::*; + let mut v = vec![ + Ammo(self.ammo.name.to_string(), self.ammo.settings.clone()), + Scheme(self.scheme.name.to_string(), self.scheme.settings.clone()), + Script(self.script.to_string()), + Theme(self.theme.to_string()), + ]; + if let Some(ref m) = self.drawn_map { + v.push(DrawnMap(m.to_string())) + } + v + } +} + +#[derive(PartialEq, Eq, Clone, Debug)] +pub enum VoteType { + Kick(String), + Map(Option), + Pause, + NewSeed, + HedgehogsPerTeam(u8), +} + +pub struct Vote { + pub is_pro: bool, + pub is_forced: bool, +} + +//#[cfg(test)] +#[macro_use] +pub mod testing { + use crate::types::ServerVar::*; + use crate::types::*; + use proptest::{ + arbitrary::{any, Arbitrary}, + strategy::{BoxedStrategy, Just, Strategy}, + }; + + // Due to inability to define From between Options + pub trait Into2: Sized { + fn into2(self) -> T; + } + impl Into2 for T { + fn into2(self) -> T { + self + } + } + impl Into2> for Vec { + fn into2(self) -> Vec { + self.into_iter().map(|x| x.0).collect() + } + } + impl Into2 for Ascii { + fn into2(self) -> String { + self.0 + } + } + impl Into2> for Option { + fn into2(self) -> Option { + self.map(|x| x.0) + } + } + + #[macro_export] + macro_rules! proto_msg_case { + ($val: ident()) => { + Just($val) + }; + ($val: ident($arg: ty)) => { + any::<$arg>().prop_map(|v| $val(v.into2())) + }; + ($val: ident($arg1: ty, $arg2: ty)) => { + any::<($arg1, $arg2)>().prop_map(|v| $val(v.0.into2(), v.1.into2())) + }; + ($val: ident($arg1: ty, $arg2: ty, $arg3: ty)) => { + any::<($arg1, $arg2, $arg3)>().prop_map(|v| $val(v.0.into2(), v.1.into2(), v.2.into2())) + }; + } + + #[macro_export] + macro_rules! proto_msg_match { + ($var: expr, def = $default: expr, $($num: expr => $constr: ident $res: tt),*) => ( + match $var { + $($num => (proto_msg_case!($constr $res)).boxed()),*, + _ => Just($default).boxed() + } + ) +} + + /// Wrapper type for generating non-empty strings + #[derive(Debug)] + pub struct Ascii(String); + + impl Arbitrary for Ascii { + type Parameters = ::Parameters; + + fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy { + "[a-zA-Z0-9]+".prop_map(Ascii).boxed() + } + + type Strategy = BoxedStrategy; + } + + impl Arbitrary for GameCfg { + type Parameters = (); + + fn arbitrary_with(_args: ::Parameters) -> ::Strategy { + use crate::types::GameCfg::*; + (0..10) + .no_shrink() + .prop_flat_map(|i| { + proto_msg_match!(i, def = FeatureSize(0), + 0 => FeatureSize(u32), + 1 => MapType(Ascii), + 2 => MapGenerator(u32), + 3 => MazeSize(u32), + 4 => Seed(Ascii), + 5 => Template(u32), + 6 => Ammo(Ascii, Option), + 7 => Scheme(Ascii, Vec), + 8 => Script(Ascii), + 9 => Theme(Ascii), + 10 => DrawnMap(Ascii)) + }) + .boxed() + } + + type Strategy = BoxedStrategy; + } + + impl Arbitrary for TeamInfo { + type Parameters = (); + + fn arbitrary_with(_args: ::Parameters) -> ::Strategy { + ( + "[a-z]+", + 0u8..127u8, + "[a-z]+", + "[a-z]+", + "[a-z]+", + "[a-z]+", + 0u8..127u8, + ) + .prop_map(|(name, color, grave, fort, voice_pack, flag, difficulty)| { + fn hog(n: u8) -> HedgehogInfo { + HedgehogInfo { + name: format!("hog{}", n), + hat: format!("hat{}", n), + } + } + let hedgehogs = [ + hog(1), + hog(2), + hog(3), + hog(4), + hog(5), + hog(6), + hog(7), + hog(8), + ]; + TeamInfo { + owner: String::new(), + name, + color, + grave, + fort, + voice_pack, + flag, + difficulty, + hedgehogs, + hedgehogs_number: 0, + } + }) + .boxed() + } + + type Strategy = BoxedStrategy; + } + + impl Arbitrary for ServerVar { + type Parameters = (); + + fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy { + (0..=2) + .no_shrink() + .prop_flat_map(|i| { + proto_msg_match!(i, def = ServerVar::LatestProto(0), + 0 => MOTDNew(Ascii), + 1 => MOTDOld(Ascii), + 2 => LatestProto(u16) + ) + }) + .boxed() + } + + type Strategy = BoxedStrategy; + } + + impl Arbitrary for VoteType { + type Parameters = (); + + fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy { + use VoteType::*; + (0..=4) + .no_shrink() + .prop_flat_map(|i| { + proto_msg_match!(i, def = VoteType::Pause, + 0 => Kick(Ascii), + 1 => Map(Option), + 2 => Pause(), + 3 => NewSeed(), + 4 => HedgehogsPerTeam(u8) + ) + }) + .boxed() + } + + type Strategy = BoxedStrategy; + } +}