veloren_common/comp/
player.rs

1use serde::{Deserialize, Serialize};
2use specs::{Component, DerefFlaggedStorage, NullStorage};
3use uuid::Uuid;
4
5use crate::resources::{BattleMode, Time};
6
7pub const MAX_ALIAS_LEN: usize = 32;
8
9#[derive(Debug)]
10pub enum DisconnectReason {
11    Kicked,
12    NewerLogin,
13    NetworkError,
14    Timeout,
15    ClientRequested,
16    InvalidClientType,
17}
18
19#[derive(Clone, Debug, Serialize, Deserialize)]
20pub struct Player {
21    pub alias: String,
22    pub battle_mode: BattleMode,
23    pub last_battlemode_change: Option<Time>,
24    uuid: Uuid,
25}
26
27impl BattleMode {
28    pub fn may_harm(self, other: Self) -> bool {
29        matches!((self, other), (BattleMode::PvP, BattleMode::PvP))
30    }
31}
32
33impl Player {
34    pub fn new(
35        alias: String,
36        battle_mode: BattleMode,
37        uuid: Uuid,
38        last_battlemode_change: Option<Time>,
39    ) -> Self {
40        Self {
41            alias,
42            battle_mode,
43            last_battlemode_change,
44            uuid,
45        }
46    }
47
48    /// Currently we allow attacking only if both players are opt-in to PvP.
49    ///
50    /// Simple as tea, if they don't want the tea, don't make them drink the
51    /// tea.
52    /// You can make tea for yourself though.
53    pub fn may_harm(&self, other: &Player) -> bool { self.battle_mode.may_harm(other.battle_mode) }
54
55    pub fn is_valid(&self) -> bool { Self::alias_validate(&self.alias).is_ok() }
56
57    pub fn alias_validate(alias: &str) -> Result<(), AliasError> {
58        // TODO: Expose auth name validation and use it here.
59        // See https://gitlab.com/veloren/auth/-/blob/master/server/src/web.rs#L20
60        if !alias
61            .chars()
62            .all(|c| c.is_alphanumeric() || c == '_' || c == '-')
63        {
64            Err(AliasError::ForbiddenCharacters)
65        } else if alias.len() > MAX_ALIAS_LEN {
66            Err(AliasError::TooLong)
67        } else {
68            Ok(())
69        }
70    }
71
72    /// Not to be confused with uid
73    pub fn uuid(&self) -> Uuid { self.uuid }
74}
75
76impl Component for Player {
77    type Storage = DerefFlaggedStorage<Self, specs::DenseVecStorage<Self>>;
78}
79
80#[derive(Clone, Debug, Default)]
81pub struct Respawn;
82impl Component for Respawn {
83    type Storage = NullStorage<Self>;
84}
85
86pub enum AliasError {
87    ForbiddenCharacters,
88    TooLong,
89}
90
91#[expect(clippy::to_string_trait_impl)]
92impl ToString for AliasError {
93    fn to_string(&self) -> String {
94        match *self {
95            AliasError::ForbiddenCharacters => "Alias contains illegal characters.",
96            AliasError::TooLong => "Alias is too long.",
97        }
98        .to_string()
99    }
100}