1use 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}