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