Skip to main content

weasel/
player.rs

1//! Player-oriented features.
2
3use crate::battle::BattleRules;
4use crate::error::{WeaselError, WeaselResult};
5use crate::team::TeamId;
6
7/// Type to uniquely identify players.
8///
9/// PlayerId may be used to authorize client's events. In such case you must make sure that
10/// malicious clients can't easily fabricate the id of another player.\
11/// One way is to assign
12/// a randomly generated PlayerId to each client. Another solution is to assign PlayerId in
13/// the server itself when an new event is received from a secure socket.
14pub type PlayerId = u64;
15
16/// Manages players' rights to initiate events on behalf of a given team.
17pub(crate) struct Rights<R: BattleRules> {
18    data: Vec<(PlayerId, Vec<TeamId<R>>)>,
19}
20
21impl<R: BattleRules> Rights<R> {
22    pub(crate) fn new() -> Self {
23        Self { data: Vec::new() }
24    }
25
26    /// Removes all players without any rights.
27    fn cleanup_players(&mut self) {
28        self.data.retain(|(_, rights)| !rights.is_empty());
29    }
30
31    /// Add rights for `team` to `player`.
32    fn add(&mut self, player: PlayerId, team: &TeamId<R>) {
33        if let Some((_, rights)) = self.data.iter_mut().find(|(e, _)| *e == player) {
34            if rights.iter_mut().find(|e| *e == team).is_none() {
35                rights.push(team.clone());
36            }
37        } else {
38            self.data.push((player, vec![team.clone()]));
39        }
40    }
41
42    /// Remove rights for `team` to `player`.
43    fn remove(&mut self, player: PlayerId, team: &TeamId<R>) {
44        if let Some((_, rights)) = self.data.iter_mut().find(|(e, _)| *e == player) {
45            let index = rights.iter().position(|e| e == team);
46            if let Some(index) = index {
47                rights.remove(index);
48            }
49        }
50        self.cleanup_players();
51    }
52
53    /// Removes all stored rights.
54    fn clear(&mut self) {
55        self.data.clear();
56    }
57
58    /// Returns an iterator over all players' rights.
59    fn get(&self) -> impl Iterator<Item = (PlayerId, &[TeamId<R>])> {
60        self.data.iter().map(|(player, vec)| (*player, &vec[..]))
61    }
62
63    /// Returns `true` if `player` has rights for `team`.
64    fn check(&self, player: PlayerId, team: &TeamId<R>) -> bool {
65        if let Some((_, rights)) = self.data.iter().find(|(e, _)| *e == player) {
66            return rights.iter().any(|e| e == team);
67        }
68        false
69    }
70
71    /// Remove all occurrences of a team from all players' rights.
72    fn remove_team(&mut self, team: &TeamId<R>) {
73        for (_, rights) in &mut self.data {
74            let index = rights.iter().position(|e| e == team);
75            if let Some(index) = index {
76                rights.remove(index);
77            }
78        }
79        self.cleanup_players();
80    }
81
82    /// Remove all rights of a player.
83    fn remove_player(&mut self, player: PlayerId) {
84        let index = self.data.iter().position(|(e, _)| *e == player);
85        if let Some(index) = index {
86            self.data.remove(index);
87        }
88    }
89}
90
91/// A structure to access player's rights.
92/// Rights are used to control which players can act on behalf of what teams.
93pub struct RightsHandle<'a, R>
94where
95    R: BattleRules,
96{
97    rights: &'a Rights<R>,
98}
99
100impl<'a, R> RightsHandle<'a, R>
101where
102    R: BattleRules,
103{
104    pub(crate) fn new(rights: &'a Rights<R>) -> Self {
105        Self { rights }
106    }
107
108    /// Returns an iterator over all players' rights.
109    pub fn get(&self) -> impl Iterator<Item = (PlayerId, &[TeamId<R>])> {
110        self.rights.get()
111    }
112
113    /// Returns `true` if `player` has rights to control `team`.
114    pub fn check(&self, player: PlayerId, team: &TeamId<R>) -> bool {
115        self.rights.check(player, team)
116    }
117}
118
119/// A structure to access and manipulate player's rights.
120/// Rights are used to control which players can act on behalf of what teams.
121pub struct RightsHandleMut<'a, R, I>
122where
123    R: BattleRules,
124    I: Iterator<Item = &'a TeamId<R>>,
125{
126    rights: &'a mut Rights<R>,
127    teams: I,
128}
129
130impl<'a, R, I> RightsHandleMut<'a, R, I>
131where
132    R: BattleRules,
133    I: Iterator<Item = &'a TeamId<R>>,
134{
135    pub(crate) fn new(rights: &'a mut Rights<R>, teams: I) -> Self {
136        Self { rights, teams }
137    }
138
139    /// Add rights to control the team with the given id to `player`. The team must exist.
140    pub fn add(&mut self, player: PlayerId, team: &TeamId<R>) -> WeaselResult<(), R> {
141        if !self.teams.any(|x| x == team) {
142            return Err(WeaselError::TeamNotFound(team.clone()));
143        }
144        self.rights.add(player, team);
145        Ok(())
146    }
147
148    /// Remove player rights to control the team with the given id.
149    pub fn remove(&mut self, player: PlayerId, team: &TeamId<R>) {
150        self.rights.remove(player, team);
151    }
152
153    /// Removes all stored rights.
154    pub fn clear(&mut self) {
155        self.rights.clear();
156    }
157
158    /// Remove all occurrences of a team from all players' rights.
159    pub fn remove_team(&mut self, team: &TeamId<R>) {
160        self.rights.remove_team(team);
161    }
162
163    /// Remove all rights of a player.
164    pub fn remove_player(&mut self, player: PlayerId) {
165        self.rights.remove_player(player);
166    }
167
168    /// Returns an iterator over all players' rights.
169    pub fn get(&self) -> impl Iterator<Item = (PlayerId, &[TeamId<R>])> {
170        self.rights.get()
171    }
172
173    /// Returns `true` if `player` has rights to control `team`.
174    pub fn check(&self, player: PlayerId, team: &TeamId<R>) -> bool {
175        self.rights.check(player, team)
176    }
177}
178
179#[cfg(test)]
180mod tests {
181    use super::*;
182    use crate::battle::Battle;
183    use crate::{battle_rules, rules::empty::*};
184
185    const PLAYER_1_ID: PlayerId = 1;
186    const PLAYER_2_ID: PlayerId = 2;
187    const TEAM_1_ID: u32 = 1;
188    const TEAM_2_ID: u32 = 2;
189
190    battle_rules! {}
191
192    #[test]
193    fn change_rights() {
194        let mut rights: Rights<CustomRules> = Rights::new();
195        // Add rights for team 1 to player 1.
196        rights.add(PLAYER_1_ID, &TEAM_1_ID);
197        assert_eq!(rights.data.len(), 1);
198        // Add rights for team 2 to player 1.
199        rights.add(PLAYER_1_ID, &TEAM_2_ID);
200        assert_eq!(rights.data.len(), 1);
201        // Add rights for team 1 to player 2.
202        rights.add(PLAYER_2_ID, &TEAM_1_ID);
203        assert_eq!(rights.data.len(), 2);
204        assert_eq!(rights.check(PLAYER_1_ID, &TEAM_1_ID), true);
205        assert_eq!(rights.check(PLAYER_1_ID, &TEAM_2_ID), true);
206        assert_eq!(rights.check(PLAYER_2_ID, &TEAM_1_ID), true);
207        assert_eq!(rights.check(PLAYER_2_ID, &TEAM_2_ID), false);
208        // Remove rights for team 2 to player 1.
209        rights.remove(PLAYER_1_ID, &TEAM_2_ID);
210        assert_eq!(rights.check(PLAYER_1_ID, &TEAM_2_ID), false);
211        // Remove rights for team 1 to player 2.
212        rights.remove(PLAYER_2_ID, &TEAM_1_ID);
213        assert_eq!(rights.check(PLAYER_2_ID, &TEAM_1_ID), false);
214        assert_eq!(rights.data.len(), 1);
215        // Clear.
216        assert_eq!(rights.check(PLAYER_1_ID, &TEAM_1_ID), true);
217        rights.clear();
218        assert_eq!(rights.data.len(), 0);
219        assert_eq!(rights.check(PLAYER_1_ID, &TEAM_1_ID), false);
220    }
221
222    #[test]
223    fn remove_team() {
224        let mut rights: Rights<CustomRules> = Rights::new();
225        // Add rights for team 1 to player 1 and player 2.
226        rights.add(PLAYER_1_ID, &TEAM_1_ID);
227        rights.add(PLAYER_2_ID, &TEAM_1_ID);
228        assert_eq!(rights.check(PLAYER_1_ID, &TEAM_1_ID), true);
229        assert_eq!(rights.check(PLAYER_2_ID, &TEAM_1_ID), true);
230        // Add rights for team 2 to player 1.
231        rights.add(PLAYER_1_ID, &TEAM_2_ID);
232        assert_eq!(rights.check(PLAYER_1_ID, &TEAM_2_ID), true);
233        assert_eq!(rights.data.len(), 2);
234        // Remove team 1.
235        rights.remove_team(&TEAM_1_ID);
236        assert_eq!(rights.check(PLAYER_1_ID, &TEAM_1_ID), false);
237        assert_eq!(rights.check(PLAYER_2_ID, &TEAM_1_ID), false);
238        assert_eq!(rights.data.len(), 1);
239    }
240
241    #[test]
242    fn remove_player() {
243        let mut rights: Rights<CustomRules> = Rights::new();
244        // Add rights for team 1 to player 1 and player 2.
245        rights.add(PLAYER_1_ID, &TEAM_1_ID);
246        rights.add(PLAYER_2_ID, &TEAM_1_ID);
247        assert_eq!(rights.check(PLAYER_1_ID, &TEAM_1_ID), true);
248        assert_eq!(rights.check(PLAYER_2_ID, &TEAM_1_ID), true);
249        assert_eq!(rights.data.len(), 2);
250        // Remove player 1.
251        rights.remove_player(PLAYER_1_ID);
252        assert_eq!(rights.check(PLAYER_1_ID, &TEAM_1_ID), false);
253        assert_eq!(rights.data.len(), 1);
254    }
255
256    #[test]
257    fn handle() {
258        let mut battle = Battle::builder(CustomRules::new()).build();
259        // Check that add() verifies team's existence.
260        assert_eq!(
261            battle.rights_mut().add(PLAYER_1_ID, &TEAM_1_ID).err(),
262            Some(WeaselError::TeamNotFound(TEAM_1_ID))
263        );
264        assert_eq!(battle.rights().get().count(), 0);
265    }
266}