|
1 use serde_derive::{Deserialize, Serialize}; |
|
2 |
|
3 pub const MAX_HEDGEHOGS_PER_TEAM: u8 = 8; |
|
4 |
|
5 #[derive(PartialEq, Eq, Clone, Debug)] |
|
6 pub enum ServerVar { |
|
7 MOTDNew(String), |
|
8 MOTDOld(String), |
|
9 LatestProto(u16), |
|
10 } |
|
11 |
|
12 #[derive(PartialEq, Eq, Clone, Debug)] |
|
13 pub enum GameCfg { |
|
14 FeatureSize(u32), |
|
15 MapType(String), |
|
16 MapGenerator(u32), |
|
17 MazeSize(u32), |
|
18 Seed(String), |
|
19 Template(u32), |
|
20 |
|
21 Ammo(String, Option<String>), |
|
22 Scheme(String, Vec<String>), |
|
23 Script(String), |
|
24 Theme(String), |
|
25 DrawnMap(String), |
|
26 } |
|
27 |
|
28 #[derive(PartialEq, Eq, Clone, Debug, Default)] |
|
29 pub struct TeamInfo { |
|
30 pub owner: String, |
|
31 pub name: String, |
|
32 pub color: u8, |
|
33 pub grave: String, |
|
34 pub fort: String, |
|
35 pub voice_pack: String, |
|
36 pub flag: String, |
|
37 pub difficulty: u8, |
|
38 pub hedgehogs_number: u8, |
|
39 pub hedgehogs: [HedgehogInfo; MAX_HEDGEHOGS_PER_TEAM as usize], |
|
40 } |
|
41 |
|
42 #[derive(PartialEq, Eq, Clone, Debug, Default)] |
|
43 pub struct HedgehogInfo { |
|
44 pub name: String, |
|
45 pub hat: String, |
|
46 } |
|
47 |
|
48 #[derive(Clone, Serialize, Deserialize, Debug)] |
|
49 pub struct Ammo { |
|
50 pub name: String, |
|
51 pub settings: Option<String>, |
|
52 } |
|
53 |
|
54 #[derive(Clone, Serialize, Deserialize, Debug)] |
|
55 pub struct Scheme { |
|
56 pub name: String, |
|
57 pub settings: Vec<String>, |
|
58 } |
|
59 |
|
60 #[derive(Clone, Serialize, Deserialize, Debug)] |
|
61 pub struct RoomConfig { |
|
62 pub feature_size: u32, |
|
63 pub map_type: String, |
|
64 pub map_generator: u32, |
|
65 pub maze_size: u32, |
|
66 pub seed: String, |
|
67 pub template: u32, |
|
68 |
|
69 pub ammo: Ammo, |
|
70 pub scheme: Scheme, |
|
71 pub script: String, |
|
72 pub theme: String, |
|
73 pub drawn_map: Option<String>, |
|
74 } |
|
75 |
|
76 impl RoomConfig { |
|
77 pub fn new() -> RoomConfig { |
|
78 RoomConfig { |
|
79 feature_size: 12, |
|
80 map_type: "+rnd+".to_string(), |
|
81 map_generator: 0, |
|
82 maze_size: 0, |
|
83 seed: "seed".to_string(), |
|
84 template: 0, |
|
85 |
|
86 ammo: Ammo { |
|
87 name: "Default".to_string(), |
|
88 settings: None, |
|
89 }, |
|
90 scheme: Scheme { |
|
91 name: "Default".to_string(), |
|
92 settings: Vec::new(), |
|
93 }, |
|
94 script: "Normal".to_string(), |
|
95 theme: "\u{1f994}".to_string(), |
|
96 drawn_map: None, |
|
97 } |
|
98 } |
|
99 |
|
100 pub fn set_config(&mut self, cfg: GameCfg) { |
|
101 match cfg { |
|
102 GameCfg::FeatureSize(s) => self.feature_size = s, |
|
103 GameCfg::MapType(t) => self.map_type = t, |
|
104 GameCfg::MapGenerator(g) => self.map_generator = g, |
|
105 GameCfg::MazeSize(s) => self.maze_size = s, |
|
106 GameCfg::Seed(s) => self.seed = s, |
|
107 GameCfg::Template(t) => self.template = t, |
|
108 |
|
109 GameCfg::Ammo(n, s) => { |
|
110 self.ammo = Ammo { |
|
111 name: n, |
|
112 settings: s, |
|
113 } |
|
114 } |
|
115 GameCfg::Scheme(n, s) => { |
|
116 self.scheme = Scheme { |
|
117 name: n, |
|
118 settings: s, |
|
119 } |
|
120 } |
|
121 GameCfg::Script(s) => self.script = s, |
|
122 GameCfg::Theme(t) => self.theme = t, |
|
123 GameCfg::DrawnMap(m) => self.drawn_map = Some(m), |
|
124 }; |
|
125 } |
|
126 |
|
127 pub fn to_map_config(&self) -> Vec<String> { |
|
128 vec![ |
|
129 self.feature_size.to_string(), |
|
130 self.map_type.to_string(), |
|
131 self.map_generator.to_string(), |
|
132 self.maze_size.to_string(), |
|
133 self.seed.to_string(), |
|
134 self.template.to_string(), |
|
135 ] |
|
136 } |
|
137 |
|
138 pub fn to_game_config(&self) -> Vec<GameCfg> { |
|
139 use GameCfg::*; |
|
140 let mut v = vec![ |
|
141 Ammo(self.ammo.name.to_string(), self.ammo.settings.clone()), |
|
142 Scheme(self.scheme.name.to_string(), self.scheme.settings.clone()), |
|
143 Script(self.script.to_string()), |
|
144 Theme(self.theme.to_string()), |
|
145 ]; |
|
146 if let Some(ref m) = self.drawn_map { |
|
147 v.push(DrawnMap(m.to_string())) |
|
148 } |
|
149 v |
|
150 } |
|
151 } |
|
152 |
|
153 #[derive(PartialEq, Eq, Clone, Debug)] |
|
154 pub enum VoteType { |
|
155 Kick(String), |
|
156 Map(Option<String>), |
|
157 Pause, |
|
158 NewSeed, |
|
159 HedgehogsPerTeam(u8), |
|
160 } |
|
161 |
|
162 pub struct Vote { |
|
163 pub is_pro: bool, |
|
164 pub is_forced: bool, |
|
165 } |
|
166 |
|
167 //#[cfg(test)] |
|
168 #[macro_use] |
|
169 pub mod testing { |
|
170 use crate::types::ServerVar::*; |
|
171 use crate::types::*; |
|
172 use proptest::{ |
|
173 arbitrary::{any, Arbitrary}, |
|
174 strategy::{BoxedStrategy, Just, Strategy}, |
|
175 }; |
|
176 |
|
177 // Due to inability to define From between Options |
|
178 pub trait Into2<T>: Sized { |
|
179 fn into2(self) -> T; |
|
180 } |
|
181 impl<T> Into2<T> for T { |
|
182 fn into2(self) -> T { |
|
183 self |
|
184 } |
|
185 } |
|
186 impl Into2<Vec<String>> for Vec<Ascii> { |
|
187 fn into2(self) -> Vec<String> { |
|
188 self.into_iter().map(|x| x.0).collect() |
|
189 } |
|
190 } |
|
191 impl Into2<String> for Ascii { |
|
192 fn into2(self) -> String { |
|
193 self.0 |
|
194 } |
|
195 } |
|
196 impl Into2<Option<String>> for Option<Ascii> { |
|
197 fn into2(self) -> Option<String> { |
|
198 self.map(|x| x.0) |
|
199 } |
|
200 } |
|
201 |
|
202 #[macro_export] |
|
203 macro_rules! proto_msg_case { |
|
204 ($val: ident()) => { |
|
205 Just($val) |
|
206 }; |
|
207 ($val: ident($arg: ty)) => { |
|
208 any::<$arg>().prop_map(|v| $val(v.into2())) |
|
209 }; |
|
210 ($val: ident($arg1: ty, $arg2: ty)) => { |
|
211 any::<($arg1, $arg2)>().prop_map(|v| $val(v.0.into2(), v.1.into2())) |
|
212 }; |
|
213 ($val: ident($arg1: ty, $arg2: ty, $arg3: ty)) => { |
|
214 any::<($arg1, $arg2, $arg3)>().prop_map(|v| $val(v.0.into2(), v.1.into2(), v.2.into2())) |
|
215 }; |
|
216 } |
|
217 |
|
218 #[macro_export] |
|
219 macro_rules! proto_msg_match { |
|
220 ($var: expr, def = $default: expr, $($num: expr => $constr: ident $res: tt),*) => ( |
|
221 match $var { |
|
222 $($num => (proto_msg_case!($constr $res)).boxed()),*, |
|
223 _ => Just($default).boxed() |
|
224 } |
|
225 ) |
|
226 } |
|
227 |
|
228 /// Wrapper type for generating non-empty strings |
|
229 #[derive(Debug)] |
|
230 pub struct Ascii(String); |
|
231 |
|
232 impl Arbitrary for Ascii { |
|
233 type Parameters = <String as Arbitrary>::Parameters; |
|
234 |
|
235 fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy { |
|
236 "[a-zA-Z0-9]+".prop_map(Ascii).boxed() |
|
237 } |
|
238 |
|
239 type Strategy = BoxedStrategy<Ascii>; |
|
240 } |
|
241 |
|
242 impl Arbitrary for GameCfg { |
|
243 type Parameters = (); |
|
244 |
|
245 fn arbitrary_with(_args: <Self as Arbitrary>::Parameters) -> <Self as Arbitrary>::Strategy { |
|
246 use crate::types::GameCfg::*; |
|
247 (0..10) |
|
248 .no_shrink() |
|
249 .prop_flat_map(|i| { |
|
250 proto_msg_match!(i, def = FeatureSize(0), |
|
251 0 => FeatureSize(u32), |
|
252 1 => MapType(Ascii), |
|
253 2 => MapGenerator(u32), |
|
254 3 => MazeSize(u32), |
|
255 4 => Seed(Ascii), |
|
256 5 => Template(u32), |
|
257 6 => Ammo(Ascii, Option<Ascii>), |
|
258 7 => Scheme(Ascii, Vec<Ascii>), |
|
259 8 => Script(Ascii), |
|
260 9 => Theme(Ascii), |
|
261 10 => DrawnMap(Ascii)) |
|
262 }) |
|
263 .boxed() |
|
264 } |
|
265 |
|
266 type Strategy = BoxedStrategy<GameCfg>; |
|
267 } |
|
268 |
|
269 impl Arbitrary for TeamInfo { |
|
270 type Parameters = (); |
|
271 |
|
272 fn arbitrary_with(_args: <Self as Arbitrary>::Parameters) -> <Self as Arbitrary>::Strategy { |
|
273 ( |
|
274 "[a-z]+", |
|
275 0u8..127u8, |
|
276 "[a-z]+", |
|
277 "[a-z]+", |
|
278 "[a-z]+", |
|
279 "[a-z]+", |
|
280 0u8..127u8, |
|
281 ) |
|
282 .prop_map(|(name, color, grave, fort, voice_pack, flag, difficulty)| { |
|
283 fn hog(n: u8) -> HedgehogInfo { |
|
284 HedgehogInfo { |
|
285 name: format!("hog{}", n), |
|
286 hat: format!("hat{}", n), |
|
287 } |
|
288 } |
|
289 let hedgehogs = [ |
|
290 hog(1), |
|
291 hog(2), |
|
292 hog(3), |
|
293 hog(4), |
|
294 hog(5), |
|
295 hog(6), |
|
296 hog(7), |
|
297 hog(8), |
|
298 ]; |
|
299 TeamInfo { |
|
300 owner: String::new(), |
|
301 name, |
|
302 color, |
|
303 grave, |
|
304 fort, |
|
305 voice_pack, |
|
306 flag, |
|
307 difficulty, |
|
308 hedgehogs, |
|
309 hedgehogs_number: 0, |
|
310 } |
|
311 }) |
|
312 .boxed() |
|
313 } |
|
314 |
|
315 type Strategy = BoxedStrategy<TeamInfo>; |
|
316 } |
|
317 |
|
318 impl Arbitrary for ServerVar { |
|
319 type Parameters = (); |
|
320 |
|
321 fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy { |
|
322 (0..=2) |
|
323 .no_shrink() |
|
324 .prop_flat_map(|i| { |
|
325 proto_msg_match!(i, def = ServerVar::LatestProto(0), |
|
326 0 => MOTDNew(Ascii), |
|
327 1 => MOTDOld(Ascii), |
|
328 2 => LatestProto(u16) |
|
329 ) |
|
330 }) |
|
331 .boxed() |
|
332 } |
|
333 |
|
334 type Strategy = BoxedStrategy<ServerVar>; |
|
335 } |
|
336 |
|
337 impl Arbitrary for VoteType { |
|
338 type Parameters = (); |
|
339 |
|
340 fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy { |
|
341 use VoteType::*; |
|
342 (0..=4) |
|
343 .no_shrink() |
|
344 .prop_flat_map(|i| { |
|
345 proto_msg_match!(i, def = VoteType::Pause, |
|
346 0 => Kick(Ascii), |
|
347 1 => Map(Option<Ascii>), |
|
348 2 => Pause(), |
|
349 3 => NewSeed(), |
|
350 4 => HedgehogsPerTeam(u8) |
|
351 ) |
|
352 }) |
|
353 .boxed() |
|
354 } |
|
355 |
|
356 type Strategy = BoxedStrategy<VoteType>; |
|
357 } |
|
358 } |