use super::{
world_msg::EconomyInfo, ClientType, CompressedData, EcsCompPacket, PingMsg, QuadPngEncoding,
TriPngEncoding, WidePacking, WireChonk,
};
use crate::sync;
use common::{
calendar::Calendar,
character::{self, CharacterItem},
comp::{
self, body::Gender, invite::InviteKind, item::MaterialStatManifest, AdminRole, Content,
},
event::{PluginHash, UpdateCharacterMetadata},
lod,
outcome::Outcome,
recipe::{ComponentRecipeBook, RecipeBookManifest, RepairRecipeBook},
resources::{Time, TimeOfDay, TimeScale},
shared_server_config::ServerConstants,
terrain::{Block, TerrainChunk, TerrainChunkMeta, TerrainChunkSize},
trade::{PendingTrade, SitePrices, TradeId, TradeResult},
uid::Uid,
uuid::Uuid,
weather::SharedWeatherGrid,
};
use hashbrown::HashMap;
use serde::{Deserialize, Serialize};
use std::time::Duration;
use tracing::warn;
use vek::*;
#[derive(Debug, Clone)]
pub enum ServerMsg {
Info(ServerInfo),
Init(Box<ServerInit>),
RegisterAnswer(ServerRegisterAnswer),
General(ServerGeneral),
Ping(PingMsg),
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ServerInfo {
pub name: String,
pub git_hash: String,
pub git_date: String,
pub auth_provider: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct ServerDescription {
pub motd: String,
pub rules: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[allow(clippy::large_enum_variant)]
pub enum ServerInit {
GameSync {
entity_package: sync::EntityPackage<EcsCompPacket>,
role: Option<AdminRole>,
time_of_day: TimeOfDay,
max_group_size: u32,
client_timeout: Duration,
world_map: crate::msg::world_msg::WorldMapMsg,
recipe_book: RecipeBookManifest,
component_recipe_book: ComponentRecipeBook,
repair_recipe_book: RepairRecipeBook,
material_stats: MaterialStatManifest,
ability_map: comp::item::tool::AbilityMap,
server_constants: ServerConstants,
description: ServerDescription,
active_plugins: Vec<PluginHash>,
},
}
pub type ServerRegisterAnswer = Result<(), RegisterError>;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum SerializedTerrainChunk {
DeflatedChonk(CompressedData<TerrainChunk>),
QuadPng(WireChonk<QuadPngEncoding<4>, WidePacking<true>, TerrainChunkMeta, TerrainChunkSize>),
TriPng(WireChonk<TriPngEncoding<false>, WidePacking<true>, TerrainChunkMeta, TerrainChunkSize>),
}
impl SerializedTerrainChunk {
pub fn approx_len(&self) -> usize {
match self {
SerializedTerrainChunk::DeflatedChonk(data) => data.data.len(),
SerializedTerrainChunk::QuadPng(data) => data.data.data.len(),
SerializedTerrainChunk::TriPng(data) => data.data.data.len(),
}
}
pub fn via_heuristic(chunk: &TerrainChunk, lossy_compression: bool) -> Self {
if lossy_compression && (chunk.get_max_z() - chunk.get_min_z() <= 128) {
Self::quadpng(chunk)
} else {
Self::deflate(chunk)
}
}
pub fn deflate(chunk: &TerrainChunk) -> Self {
Self::DeflatedChonk(CompressedData::compress(chunk, 1))
}
pub fn quadpng(chunk: &TerrainChunk) -> Self {
if let Some(wc) = WireChonk::from_chonk(QuadPngEncoding(), WidePacking(), chunk) {
Self::QuadPng(wc)
} else {
warn!("Image encoding failure occurred, falling back to deflate");
Self::deflate(chunk)
}
}
pub fn tripng(chunk: &TerrainChunk) -> Self {
if let Some(wc) = WireChonk::from_chonk(TriPngEncoding(), WidePacking(), chunk) {
Self::TriPng(wc)
} else {
warn!("Image encoding failure occurred, falling back to deflate");
Self::deflate(chunk)
}
}
pub fn to_chunk(&self) -> Option<TerrainChunk> {
match self {
Self::DeflatedChonk(chonk) => chonk.decompress(),
Self::QuadPng(wc) => wc.to_chonk(),
Self::TriPng(wc) => wc.to_chonk(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum ServerGeneral {
CharacterDataLoadResult(Result<UpdateCharacterMetadata, String>),
CharacterListUpdate(Vec<CharacterItem>),
CharacterActionError(String),
CharacterCreated(character::CharacterId),
CharacterEdited(character::CharacterId),
CharacterSuccess,
SpectatorSuccess(Vec3<f32>),
GroupUpdate(comp::group::ChangeNotification<Uid>),
Invite {
inviter: Uid,
timeout: Duration,
kind: InviteKind,
},
InvitePending(Uid),
GroupInventoryUpdate(comp::FrontendItem, Uid),
InviteComplete {
target: Uid,
answer: InviteAnswer,
kind: InviteKind,
},
ExitInGameSuccess,
InventoryUpdate(comp::Inventory, Vec<comp::InventoryUpdateEvent>),
SetViewDistance(u32),
Outcomes(Vec<Outcome>),
Knockback(Vec3<f32>),
TerrainChunkUpdate {
key: Vec2<i32>,
chunk: Result<SerializedTerrainChunk, ()>,
},
LodZoneUpdate {
key: Vec2<i32>,
zone: lod::Zone,
},
TerrainBlockUpdates(CompressedData<HashMap<Vec3<i32>, Block>>),
PlayerListUpdate(PlayerListUpdate),
ChatMsg(comp::ChatMsg),
ChatMode(comp::ChatMode),
SetPlayerEntity(Uid),
TimeOfDay(TimeOfDay, Calendar, Time, TimeScale),
EntitySync(sync::EntitySyncPackage),
CompSync(sync::CompSyncPackage<EcsCompPacket>, u64),
CreateEntity(sync::EntityPackage<EcsCompPacket>),
DeleteEntity(Uid),
Disconnect(DisconnectReason),
Notification(Notification),
UpdatePendingTrade(TradeId, PendingTrade, Option<SitePrices>),
FinishedTrade(TradeResult),
SiteEconomy(EconomyInfo),
MapMarker(comp::MapMarkerUpdate),
WeatherUpdate(SharedWeatherGrid),
LocalWindUpdate(Vec2<f32>),
SpectatePosition(Vec3<f32>),
PluginData(Vec<u8>),
UpdateRecipes,
SetPlayerRole(Option<AdminRole>),
}
impl ServerGeneral {
pub fn server_msg(chat_type: comp::ChatType<String>, content: impl Into<Content>) -> Self {
ServerGeneral::ChatMsg(chat_type.into_msg(content.into()))
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum PlayerListUpdate {
Init(HashMap<Uid, PlayerInfo>),
Add(Uid, PlayerInfo),
SelectedCharacter(Uid, CharacterInfo),
ExitCharacter(Uid),
Moderator(Uid, bool),
Remove(Uid),
Alias(Uid, String),
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PlayerInfo {
pub is_moderator: bool,
pub is_online: bool,
pub player_alias: String,
pub character: Option<CharacterInfo>,
pub uuid: Uuid,
}
pub struct ChatTypeContext {
pub you: Uid,
pub player_info: HashMap<Uid, PlayerInfo>,
pub entity_name: HashMap<Uid, String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CharacterInfo {
pub name: String,
pub gender: Option<Gender>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum InviteAnswer {
Accepted,
Declined,
TimedOut,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum Notification {
WaypointSaved,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct BanInfo {
pub reason: String,
pub until: Option<i64>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum DisconnectReason {
Shutdown,
Kicked(String),
Banned(BanInfo),
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub enum RegisterError {
AuthError(String),
Banned(BanInfo),
Kicked(String),
InvalidCharacter,
NotOnWhitelist,
TooManyPlayers,
}
impl ServerMsg {
pub fn verify(
&self,
c_type: ClientType,
registered: bool,
presence: Option<comp::PresenceKind>,
) -> bool {
match self {
ServerMsg::Info(_) | ServerMsg::Init(_) | ServerMsg::RegisterAnswer(_) => {
!registered && presence.is_none()
},
ServerMsg::General(g) => {
registered
&& match g {
ServerGeneral::CharacterDataLoadResult(_)
| ServerGeneral::CharacterListUpdate(_)
| ServerGeneral::CharacterActionError(_)
| ServerGeneral::CharacterEdited(_)
| ServerGeneral::CharacterCreated(_) => {
c_type != ClientType::ChatOnly && presence.is_none()
},
ServerGeneral::CharacterSuccess | ServerGeneral::SpectatorSuccess(_) => {
c_type == ClientType::Game && presence.is_none()
},
ServerGeneral::GroupUpdate(_)
| ServerGeneral::Invite { .. }
| ServerGeneral::InvitePending(_)
| ServerGeneral::InviteComplete { .. }
| ServerGeneral::ExitInGameSuccess
| ServerGeneral::InventoryUpdate(_, _)
| ServerGeneral::GroupInventoryUpdate(_, _)
| ServerGeneral::TerrainChunkUpdate { .. }
| ServerGeneral::TerrainBlockUpdates(_)
| ServerGeneral::SetViewDistance(_)
| ServerGeneral::Outcomes(_)
| ServerGeneral::Knockback(_)
| ServerGeneral::UpdatePendingTrade(_, _, _)
| ServerGeneral::FinishedTrade(_)
| ServerGeneral::SiteEconomy(_)
| ServerGeneral::MapMarker(_)
| ServerGeneral::WeatherUpdate(_)
| ServerGeneral::LocalWindUpdate(_)
| ServerGeneral::SpectatePosition(_)
| ServerGeneral::UpdateRecipes => {
c_type == ClientType::Game && presence.is_some()
},
ServerGeneral::PlayerListUpdate(_)
| ServerGeneral::ChatMsg(_)
| ServerGeneral::ChatMode(_)
| ServerGeneral::SetPlayerEntity(_)
| ServerGeneral::TimeOfDay(_, _, _, _)
| ServerGeneral::EntitySync(_)
| ServerGeneral::CompSync(_, _)
| ServerGeneral::CreateEntity(_)
| ServerGeneral::DeleteEntity(_)
| ServerGeneral::Disconnect(_)
| ServerGeneral::Notification(_)
| ServerGeneral::SetPlayerRole(_)
| ServerGeneral::LodZoneUpdate { .. } => true,
ServerGeneral::PluginData(_) => true,
}
},
ServerMsg::Ping(_) => true,
}
}
}
impl From<comp::ChatMsg> for ServerGeneral {
fn from(v: comp::ChatMsg) -> Self { ServerGeneral::ChatMsg(v) }
}
impl From<ServerInfo> for ServerMsg {
fn from(o: ServerInfo) -> ServerMsg { ServerMsg::Info(o) }
}
impl From<ServerInit> for ServerMsg {
fn from(o: ServerInit) -> ServerMsg { ServerMsg::Init(Box::new(o)) }
}
impl From<ServerRegisterAnswer> for ServerMsg {
fn from(o: ServerRegisterAnswer) -> ServerMsg { ServerMsg::RegisterAnswer(o) }
}
impl From<ServerGeneral> for ServerMsg {
fn from(o: ServerGeneral) -> ServerMsg { ServerMsg::General(o) }
}
impl From<PingMsg> for ServerMsg {
fn from(o: PingMsg) -> ServerMsg { ServerMsg::Ping(o) }
}