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