Skip to main content

nil_core/
player.rs

1// Copyright (C) Call of Nil contributors
2// SPDX-License-Identifier: AGPL-3.0-only
3
4use crate::error::{Error, Result};
5use crate::resources::Resources;
6use crate::world::World;
7use bon::Builder;
8use derive_more::{Display, From, Into};
9use serde::{Deserialize, Serialize};
10use std::borrow::{Borrow, Cow};
11use std::collections::HashMap;
12use std::ops::Deref;
13use std::sync::Arc;
14
15#[derive(Clone, Debug, Default, Deserialize, Serialize)]
16pub struct PlayerManager(HashMap<PlayerId, Player>);
17
18impl PlayerManager {
19  pub(crate) fn manage(&mut self, player: Player) {
20    debug_assert!(!self.0.contains_key(&player.id));
21    self.0.insert(player.id(), player);
22  }
23
24  pub fn player(&self, id: &PlayerId) -> Result<&Player> {
25    self
26      .0
27      .get(id)
28      .ok_or_else(|| Error::PlayerNotFound(id.clone()))
29  }
30
31  pub(crate) fn player_mut(&mut self, id: &PlayerId) -> Result<&mut Player> {
32    self
33      .0
34      .get_mut(id)
35      .ok_or_else(|| Error::PlayerNotFound(id.clone()))
36  }
37
38  pub fn players(&self) -> impl Iterator<Item = &Player> {
39    self.0.values()
40  }
41
42  pub(crate) fn players_mut(&mut self) -> impl Iterator<Item = &mut Player> {
43    self.0.values_mut()
44  }
45
46  pub fn active_players(&self) -> impl Iterator<Item = &Player> {
47    self
48      .players()
49      .filter(|player| player.is_active())
50  }
51
52  #[inline]
53  pub fn has(&self, id: &PlayerId) -> bool {
54    self.0.contains_key(id)
55  }
56}
57
58#[derive(Clone, Debug, Deserialize, Serialize)]
59#[serde(rename_all = "camelCase")]
60pub struct Player {
61  id: PlayerId,
62  status: PlayerStatus,
63  resources: Resources,
64}
65
66impl Player {
67  pub fn new(options: PlayerOptions) -> Self {
68    Self {
69      id: options.id,
70      status: PlayerStatus::Active,
71      resources: Resources::PLAYER.clone(),
72    }
73  }
74
75  pub fn spawn(self, world: &mut World) -> Result<()> {
76    world.spawn_player(self)
77  }
78
79  #[inline]
80  pub fn id(&self) -> PlayerId {
81    self.id.clone()
82  }
83
84  #[inline]
85  pub fn status(&self) -> PlayerStatus {
86    self.status
87  }
88
89  pub(crate) fn status_mut(&mut self) -> &mut PlayerStatus {
90    &mut self.status
91  }
92
93  #[inline]
94  pub fn resources(&self) -> &Resources {
95    &self.resources
96  }
97
98  pub(crate) fn resources_mut(&mut self) -> &mut Resources {
99    &mut self.resources
100  }
101
102  #[inline]
103  pub fn is_active(&self) -> bool {
104    matches!(self.status, PlayerStatus::Active)
105  }
106
107  #[inline]
108  pub fn is_inactive(&self) -> bool {
109    matches!(self.status, PlayerStatus::Inactive)
110  }
111}
112
113#[derive(Debug, Display, From, Into, PartialEq, Eq, Hash, Deserialize, Serialize)]
114#[from(String, &str, Arc<str>, Box<str>, Cow<'_, str>)]
115pub struct PlayerId(Arc<str>);
116
117impl Clone for PlayerId {
118  fn clone(&self) -> Self {
119    Self(Arc::clone(&self.0))
120  }
121}
122
123impl AsRef<str> for PlayerId {
124  fn as_ref(&self) -> &str {
125    self.0.as_str()
126  }
127}
128
129impl Deref for PlayerId {
130  type Target = str;
131
132  fn deref(&self) -> &Self::Target {
133    self.0.as_str()
134  }
135}
136
137impl Borrow<str> for PlayerId {
138  fn borrow(&self) -> &str {
139    self.0.as_str()
140  }
141}
142
143#[derive(Clone, Copy, Debug, PartialEq, Eq, Deserialize, Serialize)]
144#[serde(rename_all = "kebab-case")]
145pub enum PlayerStatus {
146  Active,
147  Inactive,
148}
149
150#[derive(Builder, Clone, Debug, Deserialize, Serialize)]
151#[serde(rename_all = "camelCase")]
152pub struct PlayerOptions {
153  #[builder(start_fn, into)]
154  pub id: PlayerId,
155}
156
157impl PlayerOptions {
158  #[inline]
159  pub fn into_player(self) -> Player {
160    Player::new(self)
161  }
162}
163
164#[derive(Clone, Debug, Deserialize, Serialize)]
165#[serde(rename_all = "camelCase")]
166pub struct PublicPlayer {
167  id: PlayerId,
168  status: PlayerStatus,
169}
170
171impl From<&Player> for PublicPlayer {
172  fn from(player: &Player) -> Self {
173    Self {
174      id: player.id.clone(),
175      status: player.status,
176    }
177  }
178}