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