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