Skip to main content

nil_core/world/
mod.rs

1// Copyright (C) Call of Nil contributors
2// SPDX-License-Identifier: AGPL-3.0-only
3
4mod battle;
5mod capital;
6mod chat;
7mod city;
8mod continent;
9mod event;
10mod infrastructure;
11mod market;
12mod military;
13mod npc;
14mod player;
15mod ranking;
16mod report;
17mod resources;
18mod round;
19mod savedata;
20
21pub mod cheat;
22pub mod config;
23pub mod stats;
24
25use crate::chat::Chat;
26use crate::continent::Continent;
27use crate::continent::size::ContinentSize;
28use crate::error::{Error, Result};
29use crate::event::Emitter;
30use crate::hooks::OnNextRound;
31use crate::market::Market;
32use crate::market::fee::MarketFee;
33use crate::military::Military;
34use crate::npc::bot::BotManager;
35use crate::npc::precursor::PrecursorManager;
36use crate::player::PlayerManager;
37use crate::ranking::Ranking;
38use crate::round::Round;
39use crate::ruler::{Ruler, RulerRef, RulerRefMut};
40use crate::savedata::{SaveHandle, Savedata};
41use crate::world::config::{WorldSpeed, WorldUnitSpeed};
42use bon::Builder;
43use config::{BotAdvancedStartRatio, BotDensity, Locale, WorldConfig, WorldId, WorldName};
44use serde::{Deserialize, Serialize};
45use stats::WorldStats;
46use std::sync::Arc;
47
48#[derive(Debug)]
49pub struct World {
50  round: Round,
51  continent: Continent,
52  player_manager: PlayerManager,
53  bot_manager: BotManager,
54  precursor_manager: PrecursorManager,
55  military: Military,
56  market: Market,
57  ranking: Ranking,
58  chat: Chat,
59
60  config: Arc<WorldConfig>,
61  stats: WorldStats,
62
63  // These are not included in the savedata.
64  emitter: Emitter,
65  save_handle: Option<SaveHandle>,
66  on_next_round: Option<OnNextRound>,
67}
68
69impl World {
70  pub fn new(mut options: WorldOptions) -> Result<Self> {
71    WorldOptions::clamp(&mut options);
72    options.name = options.name.trim().into();
73
74    let config = WorldConfig::new(&options);
75    let stats = WorldStats::new(&config);
76    let continent = Continent::new(options.size.unwrap_or_default());
77    let precursor_manager = PrecursorManager::new(continent.size());
78    let military = Military::new(continent.size());
79    let market = Market::new(options.market_fee.unwrap_or_default());
80
81    let mut world = Self {
82      round: Round::default(),
83      continent,
84      player_manager: PlayerManager::default(),
85      bot_manager: BotManager::default(),
86      precursor_manager,
87      military,
88      market,
89      ranking: Ranking::default(),
90      config: Arc::new(config),
91      stats,
92      chat: Chat::default(),
93
94      emitter: Emitter::default(),
95      save_handle: None,
96      on_next_round: None,
97    };
98
99    world.spawn_precursors()?;
100    world.spawn_bots()?;
101    world.update_ranking()?;
102
103    Ok(world)
104  }
105
106  #[inline]
107  pub fn with_savedata(savedata: Savedata) -> Self {
108    Self::from(savedata)
109  }
110
111  pub fn load(bytes: &[u8]) -> Result<Self> {
112    let savedata = Savedata::read(bytes)?;
113    Ok(Self::with_savedata(savedata))
114  }
115
116  #[inline]
117  pub fn id(&self) -> WorldId {
118    self.config.id()
119  }
120
121  #[inline]
122  pub fn config(&self) -> Arc<WorldConfig> {
123    Arc::clone(&self.config)
124  }
125
126  #[inline]
127  pub fn stats(&self) -> WorldStats {
128    self.stats.clone()
129  }
130
131  pub fn ruler(&self, ruler: &Ruler) -> Result<RulerRef<'_>> {
132    let ruler = match ruler {
133      Ruler::Bot { id } => RulerRef::Bot(self.bot(id)?),
134      Ruler::Player { id } => RulerRef::Player(self.player(id)?),
135      Ruler::Precursor { id } => RulerRef::Precursor(self.precursor(*id)),
136    };
137
138    Ok(ruler)
139  }
140
141  fn ruler_mut(&mut self, ruler: &Ruler) -> Result<RulerRefMut<'_>> {
142    let ruler = match ruler {
143      Ruler::Bot { id } => RulerRefMut::Bot(self.bot_mut(id)?),
144      Ruler::Player { id } => RulerRefMut::Player(self.player_mut(id)?),
145      Ruler::Precursor { id } => RulerRefMut::Precursor(self.precursor_mut(*id)),
146    };
147
148    Ok(ruler)
149  }
150
151  pub fn rulers(&self) -> impl Iterator<Item = RulerRef<'_>> {
152    self
153      .players()
154      .map(RulerRef::from)
155      .chain(self.bots().map(RulerRef::from))
156      .chain(self.precursors().map(RulerRef::from))
157  }
158
159  /// Schedules a save to be performed at the end of the current round.
160  /// If a save is already scheduled, it will be overwritten.
161  pub fn save<F>(&mut self, f: F)
162  where
163    F: FnOnce(Vec<u8>) + Send + Sync + 'static,
164  {
165    self.save_handle = Some(SaveHandle::new(f));
166  }
167
168  /// Registers a hook to be called once a new round is about to start.
169  pub fn on_next_round<F>(&mut self, f: F)
170  where
171    F: Fn(&mut World) + Send + Sync + 'static,
172  {
173    self.on_next_round = Some(OnNextRound::new(f));
174  }
175}
176
177impl Drop for World {
178  fn drop(&mut self) {
179    let _ = self.emit_drop();
180  }
181}
182
183impl TryFrom<WorldOptions> for World {
184  type Error = Error;
185
186  fn try_from(options: WorldOptions) -> Result<Self> {
187    Self::new(options)
188  }
189}
190
191#[derive(Builder, Clone, Debug, Deserialize, Serialize)]
192#[serde(rename_all = "camelCase")]
193#[cfg_attr(feature = "typescript", derive(ts_rs::TS))]
194#[cfg_attr(feature = "typescript", ts(optional_fields = nullable))]
195pub struct WorldOptions {
196  #[builder(start_fn, into)]
197  pub name: WorldName,
198
199  #[serde(default)]
200  #[builder(into)]
201  pub size: Option<ContinentSize>,
202
203  #[serde(default)]
204  pub locale: Option<Locale>,
205
206  #[serde(default)]
207  pub allow_cheats: Option<bool>,
208
209  #[serde(default)]
210  #[builder(into)]
211  pub speed: Option<WorldSpeed>,
212
213  #[serde(default)]
214  #[builder(into)]
215  pub unit_speed: Option<WorldUnitSpeed>,
216
217  #[serde(default)]
218  #[builder(into)]
219  pub bot_density: Option<BotDensity>,
220
221  #[serde(default)]
222  #[builder(into)]
223  pub bot_advanced_start_ratio: Option<BotAdvancedStartRatio>,
224
225  #[serde(default)]
226  #[builder(into)]
227  pub market_fee: Option<MarketFee>,
228}
229
230impl WorldOptions {
231  pub fn clamp(&mut self) {
232    macro_rules! clamp {
233      ($($field_ty:ident => $field:ident),* $(,)?) => {
234        $(
235          if let Some(value) = self.$field.as_mut() {
236            $field_ty::clamp(value);
237          }
238        )*
239      };
240    }
241
242    clamp!(
243      ContinentSize => size,
244      WorldSpeed => speed,
245      WorldUnitSpeed => unit_speed,
246      BotDensity => bot_density,
247      BotAdvancedStartRatio => bot_advanced_start_ratio,
248      MarketFee => market_fee,
249    );
250  }
251}