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