Skip to main content

nil_core/world/
config.rs

1// Copyright (C) Call of Nil contributors
2// SPDX-License-Identifier: AGPL-3.0-only
3
4use crate::error::AnyResult;
5use crate::world::WorldOptions;
6use bon::Builder;
7use nil_util::{ConstDeref, F64Math};
8use serde::{Deserialize, Serialize};
9use std::ops::Deref;
10use std::sync::Arc;
11use strum::EnumString;
12use uuid::Uuid;
13
14#[derive(Builder, Clone, Debug, Deserialize, Serialize)]
15#[serde(rename_all = "camelCase")]
16#[builder(builder_type(vis = "pub(crate)"))]
17#[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
18pub struct WorldConfig {
19  #[builder(start_fn, into)]
20  name: WorldName,
21
22  #[builder(skip)]
23  id: WorldId,
24
25  #[serde(default)]
26  #[builder(default)]
27  locale: Locale,
28
29  #[serde(default)]
30  #[builder(default)]
31  allow_cheats: bool,
32
33  #[serde(default)]
34  #[builder(default, into)]
35  speed: WorldSpeed,
36
37  #[serde(default)]
38  #[builder(default, into)]
39  unit_speed: WorldUnitSpeed,
40
41  #[serde(default)]
42  #[builder(default, into)]
43  bot_density: BotDensity,
44
45  #[serde(default)]
46  #[builder(default, into)]
47  bot_advanced_start_ratio: BotAdvancedStartRatio,
48}
49
50impl WorldConfig {
51  pub fn new(options: &WorldOptions) -> Self {
52    Self {
53      id: WorldId::new(),
54      name: options.name.clone(),
55      locale: options.locale.unwrap_or_default(),
56      allow_cheats: options.allow_cheats.unwrap_or_default(),
57      speed: options.speed.unwrap_or_default(),
58      unit_speed: options.unit_speed.unwrap_or_default(),
59      bot_density: options.bot_density.unwrap_or_default(),
60      bot_advanced_start_ratio: options
61        .bot_advanced_start_ratio
62        .unwrap_or_default(),
63    }
64  }
65
66  #[inline]
67  pub fn id(&self) -> WorldId {
68    self.id
69  }
70
71  #[inline]
72  pub fn name(&self) -> WorldName {
73    self.name.clone()
74  }
75
76  #[inline]
77  pub fn locale(&self) -> Locale {
78    self.locale
79  }
80
81  #[inline]
82  pub fn are_cheats_allowed(&self) -> bool {
83    self.allow_cheats
84  }
85
86  #[inline]
87  pub fn speed(&self) -> WorldSpeed {
88    self.speed
89  }
90
91  #[inline]
92  pub fn unit_speed(&self) -> WorldUnitSpeed {
93    self.unit_speed
94  }
95
96  #[inline]
97  pub fn bot_density(&self) -> BotDensity {
98    self.bot_density
99  }
100
101  #[inline]
102  pub fn bot_advanced_start_ratio(&self) -> BotAdvancedStartRatio {
103    self.bot_advanced_start_ratio
104  }
105}
106
107#[derive(
108  Clone,
109  Copy,
110  Debug,
111  derive_more::Deref,
112  derive_more::Display,
113  PartialEq,
114  Eq,
115  PartialOrd,
116  Ord,
117  Hash,
118  Deserialize,
119  Serialize,
120)]
121#[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
122pub struct WorldId(Uuid);
123
124impl WorldId {
125  #[must_use]
126  pub fn new() -> Self {
127    Self(Uuid::now_v7())
128  }
129}
130
131impl Default for WorldId {
132  fn default() -> Self {
133    Self::new()
134  }
135}
136
137impl TryFrom<&str> for WorldId {
138  type Error = anyhow::Error;
139
140  fn try_from(value: &str) -> AnyResult<Self> {
141    Ok(Self(Uuid::try_parse(value)?))
142  }
143}
144
145#[derive(Clone, Debug, derive_more::Display, PartialEq, Eq, Deserialize, Serialize)]
146#[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
147pub struct WorldName(Arc<str>);
148
149impl Deref for WorldName {
150  type Target = str;
151
152  fn deref(&self) -> &Self::Target {
153    self.0.as_str()
154  }
155}
156
157impl<T: AsRef<str>> From<T> for WorldName {
158  fn from(value: T) -> Self {
159    Self(Arc::from(value.as_ref()))
160  }
161}
162
163#[derive(Copy, Debug, strum::Display, EnumString, Deserialize, Serialize)]
164#[derive_const(Clone, Default)]
165#[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
166pub enum Locale {
167  #[default]
168  #[serde(rename = "en-US")]
169  #[strum(serialize = "en-US")]
170  English,
171
172  #[serde(rename = "pt-BR")]
173  #[strum(serialize = "pt-BR")]
174  Portuguese,
175}
176
177macro_rules! impl_f64_newtype {
178  ($name:ident, min = $min:expr, max = $max:expr) => {
179    impl $name {
180      pub const MIN: Self = $name($min);
181      pub const MAX: Self = $name($max);
182
183      #[inline]
184      pub const fn new(value: f64) -> Self {
185        debug_assert!(value.is_finite());
186        debug_assert!(!value.is_subnormal());
187        Self(value.clamp(Self::MIN.0, Self::MAX.0))
188      }
189
190      pub const fn clamp(&mut self) {
191        *self = Self(self.0.clamp(Self::MIN.0, Self::MAX.0));
192      }
193    }
194
195    const impl From<f64> for $name {
196      fn from(value: f64) -> Self {
197        Self::new(value)
198      }
199    }
200
201    const impl From<$name> for f64 {
202      fn from(value: $name) -> Self {
203        value.0
204      }
205    }
206  };
207  ($name:ident, min = $min:expr, max = $max:expr, default = $default:expr) => {
208    impl_f64_newtype!($name, min = $min, max = $max);
209
210    const impl Default for $name {
211      fn default() -> Self {
212        Self::new($default)
213      }
214    }
215  };
216}
217
218#[derive(Copy, Debug, derive_more::Display, Deserialize, Serialize, ConstDeref, F64Math)]
219#[derive_const(Clone, PartialEq, PartialOrd)]
220#[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
221pub struct WorldSpeed(f64);
222
223impl_f64_newtype!(WorldSpeed, min = 0.1, max = 10.0, default = 1.0);
224
225#[derive(Copy, Debug, derive_more::Display, Deserialize, Serialize, ConstDeref, F64Math)]
226#[derive_const(Clone, PartialEq, PartialOrd)]
227#[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
228pub struct WorldUnitSpeed(f64);
229
230impl_f64_newtype!(WorldUnitSpeed, min = 0.1, max = 10.0, default = 1.0);
231
232#[derive(Copy, Debug, derive_more::Display, Deserialize, Serialize, ConstDeref, F64Math)]
233#[derive_const(Clone, PartialEq, PartialOrd)]
234#[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
235pub struct BotDensity(f64);
236
237impl_f64_newtype!(
238  BotDensity,
239  min = 0.0,
240  max = 3.0,
241  default = if cfg!(target_os = "android") { 1.0 } else { 2.0 }
242);
243
244/// Proportion of bots that will have an advanced start with higher level infrastructure.
245#[derive(Copy, Debug, derive_more::Display, Deserialize, Serialize, ConstDeref, F64Math)]
246#[derive_const(Clone, PartialEq, PartialOrd)]
247#[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
248pub struct BotAdvancedStartRatio(f64);
249
250impl_f64_newtype!(BotAdvancedStartRatio, min = 0.0, max = 1.0, default = 0.2);