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