1use super::{
2 ClientType, CompressedData, EcsCompPacket, PingMsg, QuadPngEncoding, TriPngEncoding,
3 WidePacking, WireChonk, world_msg::EconomyInfo,
4};
5use crate::sync;
6use common::{
7 calendar::Calendar,
8 character::{self, CharacterItem},
9 comp::{
10 self, AdminRole, Content, body::Gender, invite::InviteKind, item::MaterialStatManifest,
11 },
12 event::{PluginHash, UpdateCharacterMetadata},
13 lod,
14 outcome::Outcome,
15 recipe::{ComponentRecipeBook, RecipeBookManifest, RepairRecipeBook},
16 resources::{Time, TimeOfDay, TimeScale},
17 rtsim,
18 shared_server_config::ServerConstants,
19 terrain::{Block, TerrainChunk, TerrainChunkMeta, TerrainChunkSize},
20 trade::{PendingTrade, SitePrices, TradeId, TradeResult},
21 uid::Uid,
22 uuid::Uuid,
23 weather::SharedWeatherGrid,
24};
25use hashbrown::HashMap;
26use serde::{Deserialize, Serialize};
27use std::time::Duration;
28use tracing::warn;
29use vek::*;
30
31#[derive(Debug, Clone)]
34pub enum ServerMsg {
35 Info(ServerInfo),
37 Init(Box<ServerInit>),
39 RegisterAnswer(ServerRegisterAnswer),
41 General(ServerGeneral),
43 Ping(PingMsg),
44}
45
46#[derive(Debug, Clone, Serialize, Deserialize)]
51pub struct ServerInfo {
52 pub name: String,
53 pub git_hash: String,
54 pub git_date: String,
55 pub auth_provider: Option<String>,
56}
57
58#[derive(Debug, Clone, Serialize, Deserialize, Default)]
59pub struct ServerDescription {
60 pub motd: String,
61 pub rules: Option<String>,
62}
63
64#[derive(Debug, Clone, Serialize, Deserialize)]
66pub enum ServerInit {
67 GameSync {
68 entity_package: sync::EntityPackage<EcsCompPacket>,
69 role: Option<AdminRole>,
70 time_of_day: TimeOfDay,
71 max_group_size: u32,
72 client_timeout: Duration,
73 world_map: crate::msg::world_msg::WorldMapMsg,
74 recipe_book: RecipeBookManifest,
75 component_recipe_book: ComponentRecipeBook,
76 repair_recipe_book: RepairRecipeBook,
77 material_stats: MaterialStatManifest,
78 ability_map: comp::item::tool::AbilityMap,
79 server_constants: ServerConstants,
80 description: ServerDescription,
81 active_plugins: Vec<PluginHash>,
82 },
83}
84
85pub type ServerRegisterAnswer = Result<(), RegisterError>;
86
87#[derive(Debug, Clone, Serialize, Deserialize)]
88pub enum SerializedTerrainChunk {
89 DeflatedChonk(CompressedData<TerrainChunk>),
90 QuadPng(WireChonk<QuadPngEncoding<4>, WidePacking<true>, TerrainChunkMeta, TerrainChunkSize>),
91 TriPng(WireChonk<TriPngEncoding<false>, WidePacking<true>, TerrainChunkMeta, TerrainChunkSize>),
92}
93
94impl SerializedTerrainChunk {
95 pub fn approx_len(&self) -> usize {
96 match self {
97 SerializedTerrainChunk::DeflatedChonk(data) => data.data.len(),
98 SerializedTerrainChunk::QuadPng(data) => data.data.data.len(),
99 SerializedTerrainChunk::TriPng(data) => data.data.data.len(),
100 }
101 }
102
103 pub fn via_heuristic(chunk: &TerrainChunk, lossy_compression: bool) -> Self {
104 if lossy_compression && (chunk.get_max_z() - chunk.get_min_z() <= 128) {
105 Self::quadpng(chunk)
106 } else {
107 Self::deflate(chunk)
108 }
109 }
110
111 pub fn deflate(chunk: &TerrainChunk) -> Self {
112 Self::DeflatedChonk(CompressedData::compress(chunk, 1))
113 }
114
115 pub fn quadpng(chunk: &TerrainChunk) -> Self {
116 if let Some(wc) = WireChonk::from_chonk(QuadPngEncoding(), WidePacking(), chunk) {
117 Self::QuadPng(wc)
118 } else {
119 warn!("Image encoding failure occurred, falling back to deflate");
120 Self::deflate(chunk)
121 }
122 }
123
124 pub fn tripng(chunk: &TerrainChunk) -> Self {
125 if let Some(wc) = WireChonk::from_chonk(TriPngEncoding(), WidePacking(), chunk) {
126 Self::TriPng(wc)
127 } else {
128 warn!("Image encoding failure occurred, falling back to deflate");
129 Self::deflate(chunk)
130 }
131 }
132
133 pub fn to_chunk(&self) -> Option<TerrainChunk> {
134 match self {
135 Self::DeflatedChonk(chonk) => chonk.decompress(),
136 Self::QuadPng(wc) => wc.to_chonk(),
137 Self::TriPng(wc) => wc.to_chonk(),
138 }
139 }
140}
141
142#[derive(Debug, Clone, Serialize, Deserialize)]
144pub enum ServerGeneral {
145 CharacterDataLoadResult(Result<UpdateCharacterMetadata, String>),
148 CharacterListUpdate(Vec<CharacterItem>),
150 CharacterActionError(String),
152 CharacterCreated(character::CharacterId),
154 CharacterEdited(character::CharacterId),
155 CharacterSuccess,
156 SpectatorSuccess(Vec3<f32>),
157 GroupUpdate(comp::group::ChangeNotification<Uid>),
159 Invite {
161 inviter: Uid,
162 timeout: Duration,
163 kind: InviteKind,
164 },
165 InvitePending(Uid),
168 GroupInventoryUpdate(comp::FrontendItem, Uid),
170 InviteComplete {
176 target: Uid,
177 answer: InviteAnswer,
178 kind: InviteKind,
179 },
180 ExitInGameSuccess,
183 InventoryUpdate(comp::Inventory, Vec<comp::InventoryUpdateEvent>),
184 Dialogue(Uid, rtsim::Dialogue<true>),
185 SetViewDistance(u32),
190 Outcomes(Vec<Outcome>),
191 Knockback(Vec3<f32>),
192 TerrainChunkUpdate {
194 key: Vec2<i32>,
195 chunk: Result<SerializedTerrainChunk, ()>,
196 },
197 LodZoneUpdate {
198 key: Vec2<i32>,
199 zone: lod::Zone,
200 },
201 TerrainBlockUpdates(CompressedData<HashMap<Vec3<i32>, Block>>),
202 PlayerListUpdate(PlayerListUpdate),
204 ChatMsg(comp::ChatMsg),
207 ChatMode(comp::ChatMode),
208 SetPlayerEntity(Uid),
209 TimeOfDay(TimeOfDay, Calendar, Time, TimeScale),
210 EntitySync(sync::EntitySyncPackage),
211 CompSync(sync::CompSyncPackage<EcsCompPacket>, u64),
212 CreateEntity(sync::EntityPackage<EcsCompPacket>),
213 DeleteEntity(Uid),
214 Disconnect(DisconnectReason),
215 Notification(Notification),
217 UpdatePendingTrade(TradeId, PendingTrade, Option<SitePrices>),
218 FinishedTrade(TradeResult),
219 SiteEconomy(EconomyInfo),
221 MapMarker(comp::MapMarkerUpdate),
222 WeatherUpdate(SharedWeatherGrid),
223 LocalWindUpdate(Vec2<f32>),
224 SpectatePosition(Vec3<f32>),
227 PluginData(Vec<u8>),
229 UpdateRecipes,
232 SetPlayerRole(Option<AdminRole>),
233}
234
235impl ServerGeneral {
236 pub fn server_msg(chat_type: comp::ChatType<String>, content: impl Into<Content>) -> Self {
239 ServerGeneral::ChatMsg(chat_type.into_msg(content.into()))
240 }
241}
242
243#[derive(Debug, Clone, Serialize, Deserialize)]
252pub enum PlayerListUpdate {
253 Init(HashMap<Uid, PlayerInfo>),
254 Add(Uid, PlayerInfo),
255 SelectedCharacter(Uid, CharacterInfo),
256 ExitCharacter(Uid),
257 Moderator(Uid, bool),
258 Remove(Uid),
259 Alias(Uid, String),
260}
261
262#[derive(Debug, Clone, Serialize, Deserialize)]
263pub struct PlayerInfo {
264 pub is_moderator: bool,
265 pub is_online: bool,
266 pub player_alias: String,
267 pub character: Option<CharacterInfo>,
268 pub uuid: Uuid,
269}
270
271pub struct ChatTypeContext {
273 pub you: Uid,
274 pub player_info: HashMap<Uid, PlayerInfo>,
275 pub entity_name: HashMap<Uid, String>,
276}
277
278#[derive(Debug, Clone, Serialize, Deserialize)]
279pub struct CharacterInfo {
280 pub name: String,
281 pub gender: Option<Gender>,
282}
283
284#[derive(Debug, Clone, Serialize, Deserialize)]
285pub enum InviteAnswer {
286 Accepted,
287 Declined,
288 TimedOut,
289}
290
291#[derive(Debug, Clone, Serialize, Deserialize)]
292pub enum Notification {
293 WaypointSaved,
294}
295
296#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
297pub struct BanInfo {
298 pub reason: String,
299 pub until: Option<i64>,
301}
302
303#[derive(Debug, Clone, Serialize, Deserialize)]
304pub enum DisconnectReason {
305 Shutdown,
307 Kicked(String),
309 Banned(BanInfo),
310}
311
312#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
313pub enum RegisterError {
314 AuthError(String),
315 Banned(BanInfo),
316 Kicked(String),
317 InvalidCharacter,
318 NotOnWhitelist,
319 TooManyPlayers,
320 }
322
323impl ServerMsg {
324 pub fn verify(
325 &self,
326 c_type: ClientType,
327 registered: bool,
328 presence: Option<comp::PresenceKind>,
329 ) -> bool {
330 match self {
331 ServerMsg::Info(_) | ServerMsg::Init(_) | ServerMsg::RegisterAnswer(_) => {
332 !registered && presence.is_none()
333 },
334 ServerMsg::General(g) => {
335 registered
336 && match g {
337 ServerGeneral::CharacterDataLoadResult(_)
339 | ServerGeneral::CharacterListUpdate(_)
340 | ServerGeneral::CharacterActionError(_)
341 | ServerGeneral::CharacterEdited(_)
342 | ServerGeneral::CharacterCreated(_) => {
343 c_type != ClientType::ChatOnly && presence.is_none()
344 },
345 ServerGeneral::CharacterSuccess | ServerGeneral::SpectatorSuccess(_) => {
346 c_type == ClientType::Game && presence.is_none()
347 },
348 ServerGeneral::GroupUpdate(_)
350 | ServerGeneral::Invite { .. }
351 | ServerGeneral::InvitePending(_)
352 | ServerGeneral::InviteComplete { .. }
353 | ServerGeneral::ExitInGameSuccess
354 | ServerGeneral::InventoryUpdate(_, _)
355 | ServerGeneral::GroupInventoryUpdate(_, _)
356 | ServerGeneral::Dialogue(_, _)
357 | ServerGeneral::TerrainChunkUpdate { .. }
358 | ServerGeneral::TerrainBlockUpdates(_)
359 | ServerGeneral::SetViewDistance(_)
360 | ServerGeneral::Outcomes(_)
361 | ServerGeneral::Knockback(_)
362 | ServerGeneral::UpdatePendingTrade(_, _, _)
363 | ServerGeneral::FinishedTrade(_)
364 | ServerGeneral::SiteEconomy(_)
365 | ServerGeneral::MapMarker(_)
366 | ServerGeneral::WeatherUpdate(_)
367 | ServerGeneral::LocalWindUpdate(_)
368 | ServerGeneral::SpectatePosition(_)
369 | ServerGeneral::UpdateRecipes => {
370 c_type == ClientType::Game && presence.is_some()
371 },
372 ServerGeneral::PlayerListUpdate(_)
374 | ServerGeneral::ChatMsg(_)
375 | ServerGeneral::ChatMode(_)
376 | ServerGeneral::SetPlayerEntity(_)
377 | ServerGeneral::TimeOfDay(_, _, _, _)
378 | ServerGeneral::EntitySync(_)
379 | ServerGeneral::CompSync(_, _)
380 | ServerGeneral::CreateEntity(_)
381 | ServerGeneral::DeleteEntity(_)
382 | ServerGeneral::Disconnect(_)
383 | ServerGeneral::Notification(_)
384 | ServerGeneral::SetPlayerRole(_)
385 | ServerGeneral::LodZoneUpdate { .. } => true,
386 ServerGeneral::PluginData(_) => true,
387 }
388 },
389 ServerMsg::Ping(_) => true,
390 }
391 }
392}
393
394impl From<comp::ChatMsg> for ServerGeneral {
395 fn from(v: comp::ChatMsg) -> Self { ServerGeneral::ChatMsg(v) }
396}
397
398impl From<ServerInfo> for ServerMsg {
399 fn from(o: ServerInfo) -> ServerMsg { ServerMsg::Info(o) }
400}
401
402impl From<ServerInit> for ServerMsg {
403 fn from(o: ServerInit) -> ServerMsg { ServerMsg::Init(Box::new(o)) }
404}
405
406impl From<ServerRegisterAnswer> for ServerMsg {
407 fn from(o: ServerRegisterAnswer) -> ServerMsg { ServerMsg::RegisterAnswer(o) }
408}
409
410impl From<ServerGeneral> for ServerMsg {
411 fn from(o: ServerGeneral) -> ServerMsg { ServerMsg::General(o) }
412}
413
414impl From<PingMsg> for ServerMsg {
415 fn from(o: PingMsg) -> ServerMsg { ServerMsg::Ping(o) }
416}