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::{BattleMode, 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 UpdateBattleMode(Uid, BattleMode),
261}
262
263#[derive(Debug, Clone, Serialize, Deserialize)]
264pub struct PlayerInfo {
265 pub is_moderator: bool,
266 pub is_online: bool,
267 pub player_alias: String,
268 pub character: Option<CharacterInfo>,
269 pub uuid: Uuid,
270}
271
272pub struct ChatTypeContext {
274 pub you: Uid,
275 pub player_info: HashMap<Uid, PlayerInfo>,
276 pub entity_name: HashMap<Uid, Content>,
277}
278
279#[derive(Debug, Clone, Serialize, Deserialize)]
280pub struct CharacterInfo {
281 pub name: Content,
286 pub gender: Option<Gender>,
287 pub battle_mode: BattleMode,
288}
289
290#[derive(Debug, Clone, Serialize, Deserialize)]
291pub enum InviteAnswer {
292 Accepted,
293 Declined,
294 TimedOut,
295}
296
297#[derive(Debug, Clone, Serialize, Deserialize)]
304pub enum Notification {
305 WaypointSaved { location_name: String },
306}
307
308#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
309pub struct BanInfo {
310 pub reason: String,
311 pub until: Option<i64>,
313}
314
315#[derive(Debug, Clone, Serialize, Deserialize)]
316pub enum DisconnectReason {
317 Shutdown,
319 Kicked(String),
321 Banned(BanInfo),
322}
323
324#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
325pub enum RegisterError {
326 AuthError(String),
327 Banned(BanInfo),
328 Kicked(String),
329 InvalidCharacter,
330 NotOnWhitelist,
331 TooManyPlayers,
332 }
334
335impl ServerMsg {
336 pub fn verify(
337 &self,
338 c_type: ClientType,
339 registered: bool,
340 presence: Option<comp::PresenceKind>,
341 ) -> bool {
342 match self {
343 ServerMsg::Info(_) | ServerMsg::Init(_) | ServerMsg::RegisterAnswer(_) => {
344 !registered && presence.is_none()
345 },
346 ServerMsg::General(g) => {
347 registered
348 && match g {
349 ServerGeneral::CharacterDataLoadResult(_)
351 | ServerGeneral::CharacterListUpdate(_)
352 | ServerGeneral::CharacterActionError(_)
353 | ServerGeneral::CharacterEdited(_)
354 | ServerGeneral::CharacterCreated(_) => {
355 c_type != ClientType::ChatOnly && presence.is_none()
356 },
357 ServerGeneral::CharacterSuccess | ServerGeneral::SpectatorSuccess(_) => {
358 c_type == ClientType::Game && presence.is_none()
359 },
360 ServerGeneral::GroupUpdate(_)
362 | ServerGeneral::Invite { .. }
363 | ServerGeneral::InvitePending(_)
364 | ServerGeneral::InviteComplete { .. }
365 | ServerGeneral::ExitInGameSuccess
366 | ServerGeneral::InventoryUpdate(_, _)
367 | ServerGeneral::GroupInventoryUpdate(_, _)
368 | ServerGeneral::Dialogue(_, _)
369 | ServerGeneral::TerrainChunkUpdate { .. }
370 | ServerGeneral::TerrainBlockUpdates(_)
371 | ServerGeneral::SetViewDistance(_)
372 | ServerGeneral::Outcomes(_)
373 | ServerGeneral::Knockback(_)
374 | ServerGeneral::UpdatePendingTrade(_, _, _)
375 | ServerGeneral::FinishedTrade(_)
376 | ServerGeneral::SiteEconomy(_)
377 | ServerGeneral::MapMarker(_)
378 | ServerGeneral::WeatherUpdate(_)
379 | ServerGeneral::LocalWindUpdate(_)
380 | ServerGeneral::SpectatePosition(_)
381 | ServerGeneral::UpdateRecipes => {
382 c_type == ClientType::Game && presence.is_some()
383 },
384 ServerGeneral::PlayerListUpdate(_)
386 | ServerGeneral::ChatMsg(_)
387 | ServerGeneral::ChatMode(_)
388 | ServerGeneral::SetPlayerEntity(_)
389 | ServerGeneral::TimeOfDay(_, _, _, _)
390 | ServerGeneral::EntitySync(_)
391 | ServerGeneral::CompSync(_, _)
392 | ServerGeneral::CreateEntity(_)
393 | ServerGeneral::DeleteEntity(_)
394 | ServerGeneral::Disconnect(_)
395 | ServerGeneral::Notification(_)
396 | ServerGeneral::SetPlayerRole(_)
397 | ServerGeneral::LodZoneUpdate { .. } => true,
398 ServerGeneral::PluginData(_) => true,
399 }
400 },
401 ServerMsg::Ping(_) => true,
402 }
403 }
404}
405
406impl From<comp::ChatMsg> for ServerGeneral {
407 fn from(v: comp::ChatMsg) -> Self { ServerGeneral::ChatMsg(v) }
408}
409
410impl From<ServerInfo> for ServerMsg {
411 fn from(o: ServerInfo) -> ServerMsg { ServerMsg::Info(o) }
412}
413
414impl From<ServerInit> for ServerMsg {
415 fn from(o: ServerInit) -> ServerMsg { ServerMsg::Init(Box::new(o)) }
416}
417
418impl From<ServerRegisterAnswer> for ServerMsg {
419 fn from(o: ServerRegisterAnswer) -> ServerMsg { ServerMsg::RegisterAnswer(o) }
420}
421
422impl From<ServerGeneral> for ServerMsg {
423 fn from(o: ServerGeneral) -> ServerMsg { ServerMsg::General(o) }
424}
425
426impl From<PingMsg> for ServerMsg {
427 fn from(o: PingMsg) -> ServerMsg { ServerMsg::Ping(o) }
428}