use std::io::{Cursor, Write};
use azalea_buf::{
BufReadError, McBuf, McBufReadable, McBufVarReadable, McBufVarWritable, McBufWritable,
};
use azalea_chat::FormattedText;
use azalea_core::{
direction::Direction,
position::{BlockPos, GlobalPos, Vec3},
};
use azalea_inventory::ItemSlot;
use bevy_ecs::component::Component;
use derive_more::Deref;
use enum_as_inner::EnumAsInner;
use nohash_hasher::IntSet;
use uuid::Uuid;
use crate::particle::Particle;
#[derive(Clone, Debug, Deref)]
pub struct EntityMetadataItems(Vec<EntityDataItem>);
#[derive(Clone, Debug)]
pub struct EntityDataItem {
pub index: u8,
pub value: EntityDataValue,
}
impl EntityMetadataItems {
pub fn new(data: Vec<EntityDataItem>) -> Self {
EntityMetadataItems(data)
}
}
impl McBufReadable for EntityMetadataItems {
fn read_from(buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError> {
let mut metadata = Vec::new();
loop {
let id = u8::read_from(buf)?;
if id == 0xff {
break;
}
let value = EntityDataValue::read_from(buf)?;
metadata.push(EntityDataItem { index: id, value });
}
Ok(EntityMetadataItems(metadata))
}
}
impl McBufWritable for EntityMetadataItems {
fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
for item in &self.0 {
item.index.write_into(buf)?;
item.value.write_into(buf)?;
}
0xffu8.write_into(buf)?;
Ok(())
}
}
#[derive(Clone, Debug, EnumAsInner, McBuf)]
pub enum EntityDataValue {
Byte(u8),
Int(#[var] i32),
Long(i64),
Float(f32),
String(String),
FormattedText(FormattedText),
OptionalFormattedText(Option<FormattedText>),
ItemStack(ItemSlot),
Boolean(bool),
Rotations(Rotations),
BlockPos(BlockPos),
OptionalBlockPos(Option<BlockPos>),
Direction(Direction),
OptionalUuid(Option<Uuid>),
BlockState(azalea_block::BlockState),
OptionalBlockState(azalea_block::BlockState),
CompoundTag(simdnbt::owned::NbtCompound),
Particle(Particle),
Particles(Vec<Particle>),
VillagerData(VillagerData),
OptionalUnsignedInt(OptionalUnsignedInt),
Pose(Pose),
CatVariant(azalea_registry::CatVariant),
WolfVariant(azalea_registry::WolfVariant),
FrogVariant(azalea_registry::FrogVariant),
OptionalGlobalPos(Option<GlobalPos>),
PaintingVariant(azalea_registry::PaintingVariant),
SnifferState(SnifferState),
ArmadilloState(ArmadilloStateKind),
Vector3(Vec3),
Quaternion(Quaternion),
}
#[derive(Clone, Debug)]
pub struct OptionalUnsignedInt(pub Option<u32>);
#[derive(Clone, Debug, McBuf)]
pub struct Quaternion {
pub x: f32,
pub y: f32,
pub z: f32,
pub w: f32,
}
#[derive(Clone, Debug, Copy, Default, McBuf)]
pub enum ArmadilloStateKind {
#[default]
Idle,
Rolling,
Scared,
}
impl McBufReadable for OptionalUnsignedInt {
fn read_from(buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError> {
let val = u32::var_read_from(buf)?;
Ok(OptionalUnsignedInt(if val == 0 {
None
} else {
Some(val - 1)
}))
}
}
impl McBufWritable for OptionalUnsignedInt {
fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
match self.0 {
Some(val) => (val + 1).var_write_into(buf),
None => 0u32.var_write_into(buf),
}
}
}
#[derive(Clone, Debug, McBuf, Default)]
pub struct Rotations {
pub x: f32,
pub y: f32,
pub z: f32,
}
#[derive(Clone, Debug, Copy, McBuf, Default, Component, Eq, PartialEq)]
pub enum Pose {
#[default]
Standing = 0,
FallFlying,
Sleeping,
Swimming,
SpinAttack,
Sneaking,
LongJumping,
Dying,
}
#[derive(Debug, Clone, McBuf)]
pub struct VillagerData {
pub kind: azalea_registry::VillagerKind,
pub profession: azalea_registry::VillagerProfession,
#[var]
pub level: u32,
}
impl TryFrom<EntityMetadataItems> for Vec<EntityDataValue> {
type Error = String;
fn try_from(data: EntityMetadataItems) -> Result<Self, Self::Error> {
let mut data = data.0;
data.sort_by(|a, b| a.index.cmp(&b.index));
let mut prev_indexes = IntSet::default();
let len = data.len();
for item in &data {
if prev_indexes.contains(&item.index) {
return Err(format!("Index {} is duplicated", item.index));
}
if item.index as usize > len {
return Err(format!("Index {} is too big", item.index));
}
prev_indexes.insert(item.index);
}
let data = data.into_iter().map(|d| d.value).collect();
Ok(data)
}
}
#[derive(Debug, Copy, Clone, McBuf, Default)]
pub enum SnifferState {
#[default]
Idling,
FeelingHappy,
Scenting,
Sniffing,
Searching,
Digging,
Rising,
}