veloren_common_net/msg/
client.rs

1use super::{PingMsg, world_msg::SiteId};
2use common::{
3    ViewDistances,
4    character::CharacterId,
5    comp::{self, AdminRole, Skill},
6    event::PluginHash,
7    terrain::block::Block,
8};
9use serde::{Deserialize, Serialize};
10use vek::*;
11
12///This struct contains all messages the client might send (on different
13/// streams though). It's used to verify the correctness of the state in
14/// debug_assertions
15#[derive(Debug, Clone)]
16pub enum ClientMsg {
17    ///Send on the first connection ONCE to identify client intention for
18    /// server
19    Type(ClientType),
20    ///Send ONCE to register/auth to the server
21    Register(ClientRegister),
22    ///Msg that can be send ALWAYS as soon as we are registered, e.g. `Chat`
23    General(ClientGeneral),
24    Ping(PingMsg),
25}
26
27/*
282nd Level Enums
29*/
30
31#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
32pub enum ClientType {
33    /// Regular Client like Voxygen who plays the game
34    Game,
35    /// A Chat-only client, which doesn't want to connect via its character
36    ChatOnly,
37    /// A client that is only allowed to use spectator, does not emit
38    /// login/logout and player list events, and cannot use chat.
39    ///
40    /// Can only be used by moderators.
41    SilentSpectator,
42    /// A unprivileged bot, e.g. to request world information
43    /// Or a privileged bot, e.g. to run admin commands used by server-cli
44    Bot { privileged: bool },
45}
46
47impl ClientType {
48    pub fn is_valid_for_role(&self, role: Option<AdminRole>) -> bool {
49        match self {
50            Self::SilentSpectator => role.is_some(),
51            Self::Bot { privileged } => !privileged || role.is_some(),
52            _ => true,
53        }
54    }
55
56    pub fn emit_login_events(&self) -> bool { !matches!(self, Self::SilentSpectator) }
57
58    pub fn can_spectate(&self) -> bool { matches!(self, Self::Game | Self::SilentSpectator) }
59
60    pub fn can_enter_character(&self) -> bool { *self == Self::Game }
61
62    pub fn can_send_message(&self) -> bool { !matches!(self, Self::SilentSpectator) }
63}
64
65#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
66pub struct ClientRegister {
67    pub token_or_username: String,
68    pub locale: Option<String>,
69}
70
71/// Messages sent from the client to the server
72#[derive(Debug, Clone, Serialize, Deserialize)]
73pub enum ClientGeneral {
74    //Only in Character Screen
75    RequestCharacterList,
76    CreateCharacter {
77        alias: String,
78        mainhand: Option<String>,
79        offhand: Option<String>,
80        body: comp::Body,
81        // Character will be deleted upon death if true
82        hardcore: bool,
83        start_site: Option<SiteId>,
84    },
85    DeleteCharacter(CharacterId),
86    EditCharacter {
87        id: CharacterId,
88        alias: String,
89        body: comp::Body,
90    },
91    Character(CharacterId, ViewDistances),
92    Spectate(ViewDistances),
93    //Only in game
94    ControllerInputs(Box<comp::ControllerInputs>),
95    ControlEvent(comp::ControlEvent),
96    ControlAction(comp::ControlAction),
97    SetViewDistance(ViewDistances),
98    BreakBlock(Vec3<i32>),
99    PlaceBlock(Vec3<i32>, Block),
100    ExitInGame,
101    PlayerPhysics {
102        pos: comp::Pos,
103        vel: comp::Vel,
104        ori: comp::Ori,
105        force_counter: u64,
106    },
107    UnlockSkill(Skill),
108    RequestSiteInfo(SiteId),
109    UpdateMapMarker(comp::MapMarkerChange),
110
111    SpectatePosition(Vec3<f32>),
112    //Only in Game, via terrain stream
113    TerrainChunkRequest {
114        key: Vec2<i32>,
115    },
116    LodZoneRequest {
117        key: Vec2<i32>,
118    },
119    //Always possible
120    ChatMsg(comp::Content),
121    Command(String, Vec<String>),
122    Terminate,
123    RequestPlayerPhysics {
124        server_authoritative: bool,
125    },
126    RequestLossyTerrainCompression {
127        lossy_terrain_compression: bool,
128    },
129    RequestPlugins(Vec<PluginHash>),
130}
131
132impl ClientMsg {
133    pub fn verify(
134        &self,
135        c_type: ClientType,
136        registered: bool,
137        presence: Option<comp::PresenceKind>,
138    ) -> bool {
139        match self {
140            ClientMsg::Type(t) => c_type == *t,
141            ClientMsg::Register(_) => !registered && presence.is_none(),
142            ClientMsg::General(g) => {
143                registered
144                    && match g {
145                        ClientGeneral::RequestCharacterList
146                        | ClientGeneral::CreateCharacter { .. }
147                        | ClientGeneral::EditCharacter { .. }
148                        | ClientGeneral::DeleteCharacter(_) => {
149                            c_type != ClientType::ChatOnly && presence.is_none()
150                        },
151                        ClientGeneral::Character(_, _) => {
152                            c_type == ClientType::Game && presence.is_none()
153                        },
154                        ClientGeneral::Spectate(_) => {
155                            c_type.can_spectate() && presence.is_none()
156                        },
157                        //Only in game
158                        ClientGeneral::ControllerInputs(_)
159                        | ClientGeneral::ControlEvent(_)
160                        | ClientGeneral::ControlAction(_)
161                        | ClientGeneral::SetViewDistance(_)
162                        | ClientGeneral::BreakBlock(_)
163                        | ClientGeneral::PlaceBlock(_, _)
164                        | ClientGeneral::ExitInGame
165                        | ClientGeneral::PlayerPhysics { .. }
166                        | ClientGeneral::TerrainChunkRequest { .. }
167                        | ClientGeneral::UnlockSkill(_)
168                        | ClientGeneral::RequestSiteInfo(_)
169                        | ClientGeneral::RequestPlayerPhysics { .. }
170                        | ClientGeneral::RequestLossyTerrainCompression { .. }
171                        | ClientGeneral::UpdateMapMarker(_) => {
172                            c_type == ClientType::Game && presence.is_some()
173                        },
174                        ClientGeneral::SpectatePosition(_) => {
175                            c_type.can_spectate() && presence.is_some()
176                        },
177                        ClientGeneral::ChatMsg(_) => {
178                            c_type.can_send_message()
179                        },
180                        //Always possible
181                        ClientGeneral::Command(_, _)
182                        | ClientGeneral::Terminate
183                        // LodZoneRequest is required by the char select screen
184                        | ClientGeneral::LodZoneRequest { .. } => true,
185                        | ClientGeneral::RequestPlugins(_) => true,
186                    }
187            },
188            ClientMsg::Ping(_) => true,
189        }
190    }
191}
192
193/*
194end of 2nd level Enums
195*/
196
197impl From<ClientType> for ClientMsg {
198    fn from(other: ClientType) -> ClientMsg { ClientMsg::Type(other) }
199}
200
201impl From<ClientRegister> for ClientMsg {
202    fn from(other: ClientRegister) -> ClientMsg { ClientMsg::Register(other) }
203}
204
205impl From<ClientGeneral> for ClientMsg {
206    fn from(other: ClientGeneral) -> ClientMsg { ClientMsg::General(other) }
207}
208
209impl From<PingMsg> for ClientMsg {
210    fn from(other: PingMsg) -> ClientMsg { ClientMsg::Ping(other) }
211}