use std::{fmt, io::Read};
use mcproto_codec::error::{CodecError, CodecKind};
use crate::{
Boolean, Double, Float, Int, Position, PrefixedArray, PrefixedOptional, PrefixedString,
ProtocolEnum, TypeCodec, TypeStructCodec, VarInt,
basic::{decode_prefixed_string, encode_prefixed_string},
};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, ProtocolEnum)]
#[protocol_enum(repr = VarInt)]
pub enum DebugSubscriptionType {
DedicatedServerTickTime = 0,
Bee = 1,
VillagerBrain = 2,
Breeze = 3,
GoalSelector = 4,
EntityPath = 5,
EntityBlockIntersection = 6,
BeeHive = 7,
PointOfInterest = 8,
RedstoneWireOrientation = 9,
VillageSection = 10,
Raid = 11,
Structure = 12,
GameEventListener = 13,
NeighborUpdate = 14,
GameEvent = 15,
}
#[derive(Debug, Clone, PartialEq, TypeStructCodec)]
#[type_struct_codec(kind = DebugSubscriptionData)]
pub struct BeeDebugData {
pub hive_position: PrefixedOptional<Position>,
pub flower_position: PrefixedOptional<Position>,
pub travel_ticks: VarInt,
pub blacklisted_hives: PrefixedArray<Position>,
}
#[derive(Debug, Clone, PartialEq, TypeStructCodec)]
#[type_struct_codec(kind = DebugSubscriptionData)]
pub struct VillagerBrainDebugData {
pub name: PrefixedString,
pub profession: PrefixedString,
pub xp: Int,
pub health: Float,
pub max_health: Float,
pub inventory: PrefixedString,
pub wants_golem: Boolean,
pub anger_level: Int,
pub activities: PrefixedArray<PrefixedString>,
pub behaviors: PrefixedArray<PrefixedString>,
pub memories: PrefixedArray<PrefixedString>,
pub gossips: PrefixedArray<PrefixedString>,
pub pois: PrefixedArray<Position>,
pub potential_pois: PrefixedArray<Position>,
}
#[derive(Debug, Clone, PartialEq, TypeStructCodec)]
#[type_struct_codec(kind = DebugSubscriptionData)]
pub struct BreezeDebugData {
pub attack_target: PrefixedOptional<VarInt>,
pub jump_target: PrefixedOptional<Position>,
}
#[repr(transparent)]
#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)]
pub struct DebugGoalName(String);
impl DebugGoalName {
pub const MAX_UTF16_CODE_UNITS: usize = 255;
pub const MAX_BYTES: usize = Self::MAX_UTF16_CODE_UNITS * 3;
pub fn new(value: impl Into<String>) -> Result<Self, DebugGoalNameTooLong> {
let value = value.into();
let actual_code_units = value.encode_utf16().count();
if actual_code_units > Self::MAX_UTF16_CODE_UNITS {
return Err(DebugGoalNameTooLong { actual_code_units });
}
Ok(Self(value))
}
#[must_use]
pub fn as_str(&self) -> &str {
&self.0
}
#[must_use]
pub fn into_inner(self) -> String {
self.0
}
}
impl AsRef<str> for DebugGoalName {
fn as_ref(&self) -> &str {
self.as_str()
}
}
impl fmt::Display for DebugGoalName {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str(self.as_str())
}
}
impl TryFrom<String> for DebugGoalName {
type Error = DebugGoalNameTooLong;
fn try_from(value: String) -> Result<Self, Self::Error> {
Self::new(value)
}
}
impl TryFrom<&str> for DebugGoalName {
type Error = DebugGoalNameTooLong;
fn try_from(value: &str) -> Result<Self, Self::Error> {
Self::new(value)
}
}
impl From<DebugGoalName> for String {
fn from(value: DebugGoalName) -> Self {
value.into_inner()
}
}
impl TypeCodec for DebugGoalName {
fn encode(&self, writer: &mut impl std::io::Write) -> Result<(), CodecError> {
encode_prefixed_string(
&self.0,
writer,
CodecKind::String,
Self::MAX_BYTES,
Self::MAX_UTF16_CODE_UNITS,
)
}
fn decode(reader: &mut impl Read) -> Result<Self, CodecError> {
decode_prefixed_string(
reader,
CodecKind::String,
Self::MAX_BYTES,
Self::MAX_UTF16_CODE_UNITS,
)
.map(|(value, _)| Self(value))
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct DebugGoalNameTooLong {
pub actual_code_units: usize,
}
impl fmt::Display for DebugGoalNameTooLong {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
formatter,
"debug goal name contains {} UTF-16 code units; maximum is {}",
self.actual_code_units,
DebugGoalName::MAX_UTF16_CODE_UNITS
)
}
}
impl std::error::Error for DebugGoalNameTooLong {}
#[derive(Debug, Clone, PartialEq, TypeStructCodec)]
#[type_struct_codec(kind = DebugSubscriptionData)]
pub struct GoalSelectorDebugData {
pub priority: VarInt,
pub is_running: Boolean,
pub name: DebugGoalName,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, ProtocolEnum)]
#[protocol_enum(repr = VarInt)]
pub enum DebugPathNodeType {
Blocked = 0,
Open = 1,
Walkable = 2,
WalkableDoor = 3,
Trapdoor = 4,
PowderSnow = 5,
DangerPowderSnow = 6,
Fence = 7,
Lava = 8,
Water = 9,
WaterBorder = 10,
Rail = 11,
UnpassableRail = 12,
DangerFire = 13,
DamageFire = 14,
DangerOther = 15,
DamageOther = 16,
DoorOpen = 17,
DoorWoodClosed = 18,
DoorIronClosed = 19,
Breach = 20,
Leaves = 21,
StickyHoney = 22,
Cocoa = 23,
DamageCautious = 24,
DangerTrapdoor = 25,
}
#[derive(Debug, Clone, PartialEq, TypeStructCodec)]
#[type_struct_codec(kind = DebugPathNode)]
pub struct DebugPathNode {
pub x: Int,
pub y: Int,
pub z: Int,
pub walked_distance: Float,
pub cost_malus: Float,
pub closed: Boolean,
pub node_type: DebugPathNodeType,
pub f: Float,
}
#[derive(Debug, Clone, PartialEq, TypeStructCodec)]
#[type_struct_codec(kind = DebugSubscriptionData)]
pub struct EntityPathDebugData {
pub reached: Boolean,
pub next_block_index: Int,
pub block_position: Position,
pub nodes: PrefixedArray<DebugPathNode>,
pub target_nodes: PrefixedArray<DebugPathNode>,
pub open_set: PrefixedArray<DebugPathNode>,
pub closed_set: PrefixedArray<DebugPathNode>,
pub max_node_distance: Float,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, ProtocolEnum)]
#[protocol_enum(repr = VarInt)]
pub enum EntityBlockIntersectionState {
InBlock = 0,
InFluid = 1,
InAir = 2,
}
#[derive(Debug, Clone, PartialEq, TypeStructCodec)]
#[type_struct_codec(kind = DebugSubscriptionData)]
pub struct EntityBlockIntersectionDebugData {
pub state: EntityBlockIntersectionState,
}
#[derive(Debug, Clone, PartialEq, TypeStructCodec)]
#[type_struct_codec(kind = DebugSubscriptionData)]
pub struct BeeHiveDebugData {
pub block_type: VarInt,
pub occupant_count: VarInt,
pub honey_level: VarInt,
pub sedated: Boolean,
}
#[derive(Debug, Clone, PartialEq, TypeStructCodec)]
#[type_struct_codec(kind = DebugSubscriptionData)]
pub struct PointOfInterestDebugData {
pub position: Position,
pub poi_type: VarInt,
pub free_ticket_count: VarInt,
}
#[derive(Debug, Clone, PartialEq, TypeStructCodec)]
#[type_struct_codec(kind = DebugSubscriptionData)]
pub struct RedstoneWireOrientationDebugData {
pub id: VarInt,
}
#[derive(Debug, Clone, PartialEq, TypeStructCodec)]
#[type_struct_codec(kind = DebugSubscriptionData)]
pub struct RaidDebugData {
pub positions: PrefixedArray<Position>,
}
#[derive(Debug, Clone, PartialEq, TypeStructCodec)]
#[type_struct_codec(kind = DebugStructureInfo)]
pub struct DebugStructurePiece {
pub bounding_box_min: Position,
pub bounding_box_max: Position,
pub is_start: Boolean,
}
#[derive(Debug, Clone, PartialEq, TypeStructCodec)]
#[type_struct_codec(kind = DebugStructureInfo)]
pub struct DebugStructureInfo {
pub bounding_box_min: Position,
pub bounding_box_max: Position,
pub pieces: PrefixedArray<DebugStructurePiece>,
}
#[derive(Debug, Clone, PartialEq, TypeStructCodec)]
#[type_struct_codec(kind = DebugSubscriptionData)]
pub struct StructureDebugData {
pub structures: PrefixedArray<DebugStructureInfo>,
}
#[derive(Debug, Clone, PartialEq, TypeStructCodec)]
#[type_struct_codec(kind = DebugSubscriptionData)]
pub struct GameEventListenerDebugData {
pub listener_radius: VarInt,
}
#[derive(Debug, Clone, PartialEq, TypeStructCodec)]
#[type_struct_codec(kind = DebugSubscriptionData)]
pub struct NeighborUpdateDebugData {
pub position: Position,
}
#[derive(Debug, Clone, PartialEq, TypeStructCodec)]
#[type_struct_codec(kind = DebugSubscriptionData)]
pub struct GameEventDebugData {
pub event: VarInt,
pub x: Double,
pub y: Double,
pub z: Double,
}
#[derive(Debug, Clone, PartialEq)]
pub enum DebugSubscriptionData {
DedicatedServerTickTime,
Bee(BeeDebugData),
VillagerBrain(VillagerBrainDebugData),
Breeze(BreezeDebugData),
GoalSelector(GoalSelectorDebugData),
EntityPath(EntityPathDebugData),
EntityBlockIntersection(EntityBlockIntersectionDebugData),
BeeHive(BeeHiveDebugData),
PointOfInterest(PointOfInterestDebugData),
RedstoneWireOrientation(RedstoneWireOrientationDebugData),
VillageSection,
Raid(RaidDebugData),
Structure(StructureDebugData),
GameEventListener(GameEventListenerDebugData),
NeighborUpdate(NeighborUpdateDebugData),
GameEvent(GameEventDebugData),
}
impl DebugSubscriptionData {
#[must_use]
pub const fn subscription_type(&self) -> DebugSubscriptionType {
match self {
Self::DedicatedServerTickTime => DebugSubscriptionType::DedicatedServerTickTime,
Self::Bee(_) => DebugSubscriptionType::Bee,
Self::VillagerBrain(_) => DebugSubscriptionType::VillagerBrain,
Self::Breeze(_) => DebugSubscriptionType::Breeze,
Self::GoalSelector(_) => DebugSubscriptionType::GoalSelector,
Self::EntityPath(_) => DebugSubscriptionType::EntityPath,
Self::EntityBlockIntersection(_) => DebugSubscriptionType::EntityBlockIntersection,
Self::BeeHive(_) => DebugSubscriptionType::BeeHive,
Self::PointOfInterest(_) => DebugSubscriptionType::PointOfInterest,
Self::RedstoneWireOrientation(_) => DebugSubscriptionType::RedstoneWireOrientation,
Self::VillageSection => DebugSubscriptionType::VillageSection,
Self::Raid(_) => DebugSubscriptionType::Raid,
Self::Structure(_) => DebugSubscriptionType::Structure,
Self::GameEventListener(_) => DebugSubscriptionType::GameEventListener,
Self::NeighborUpdate(_) => DebugSubscriptionType::NeighborUpdate,
Self::GameEvent(_) => DebugSubscriptionType::GameEvent,
}
}
pub(crate) fn encode_payload(
&self,
writer: &mut impl std::io::Write,
) -> Result<(), CodecError> {
match self {
Self::DedicatedServerTickTime | Self::VillageSection => Ok(()),
Self::Bee(value) => value.encode(writer),
Self::VillagerBrain(value) => value.encode(writer),
Self::Breeze(value) => value.encode(writer),
Self::GoalSelector(value) => value.encode(writer),
Self::EntityPath(value) => value.encode(writer),
Self::EntityBlockIntersection(value) => value.encode(writer),
Self::BeeHive(value) => value.encode(writer),
Self::PointOfInterest(value) => value.encode(writer),
Self::RedstoneWireOrientation(value) => value.encode(writer),
Self::Raid(value) => value.encode(writer),
Self::Structure(value) => value.encode(writer),
Self::GameEventListener(value) => value.encode(writer),
Self::NeighborUpdate(value) => value.encode(writer),
Self::GameEvent(value) => value.encode(writer),
}
}
pub(crate) fn decode_payload(
subscription_type: DebugSubscriptionType,
reader: &mut impl Read,
) -> Result<Self, CodecError> {
match subscription_type {
DebugSubscriptionType::DedicatedServerTickTime => Ok(Self::DedicatedServerTickTime),
DebugSubscriptionType::Bee => BeeDebugData::decode(reader).map(Self::Bee),
DebugSubscriptionType::VillagerBrain => {
VillagerBrainDebugData::decode(reader).map(Self::VillagerBrain)
}
DebugSubscriptionType::Breeze => BreezeDebugData::decode(reader).map(Self::Breeze),
DebugSubscriptionType::GoalSelector => {
GoalSelectorDebugData::decode(reader).map(Self::GoalSelector)
}
DebugSubscriptionType::EntityPath => {
EntityPathDebugData::decode(reader).map(Self::EntityPath)
}
DebugSubscriptionType::EntityBlockIntersection => {
EntityBlockIntersectionDebugData::decode(reader).map(Self::EntityBlockIntersection)
}
DebugSubscriptionType::BeeHive => BeeHiveDebugData::decode(reader).map(Self::BeeHive),
DebugSubscriptionType::PointOfInterest => {
PointOfInterestDebugData::decode(reader).map(Self::PointOfInterest)
}
DebugSubscriptionType::RedstoneWireOrientation => {
RedstoneWireOrientationDebugData::decode(reader).map(Self::RedstoneWireOrientation)
}
DebugSubscriptionType::VillageSection => Ok(Self::VillageSection),
DebugSubscriptionType::Raid => RaidDebugData::decode(reader).map(Self::Raid),
DebugSubscriptionType::Structure => {
StructureDebugData::decode(reader).map(Self::Structure)
}
DebugSubscriptionType::GameEventListener => {
GameEventListenerDebugData::decode(reader).map(Self::GameEventListener)
}
DebugSubscriptionType::NeighborUpdate => {
NeighborUpdateDebugData::decode(reader).map(Self::NeighborUpdate)
}
DebugSubscriptionType::GameEvent => {
GameEventDebugData::decode(reader).map(Self::GameEvent)
}
}
}
}