mirror of
https://github.com/mat-1/azalea.git
synced 2025-08-02 23:44:38 +00:00
162 lines
4.6 KiB
Rust
Executable file
162 lines
4.6 KiB
Rust
Executable file
//! Define some types needed for entity metadata.
|
|
|
|
use azalea_block::BlockState;
|
|
use azalea_buf::{
|
|
BufReadError, McBuf, McBufReadable, McBufVarReadable, McBufVarWritable, McBufWritable,
|
|
};
|
|
use azalea_chat::FormattedText;
|
|
use azalea_core::{BlockPos, Direction, GlobalPos, Particle, Slot};
|
|
use bevy_ecs::component::Component;
|
|
use derive_more::Deref;
|
|
use enum_as_inner::EnumAsInner;
|
|
use nohash_hasher::IntSet;
|
|
use std::io::{Cursor, Write};
|
|
use uuid::Uuid;
|
|
|
|
#[derive(Clone, Debug, Deref)]
|
|
pub struct EntityMetadataItems(Vec<EntityDataItem>);
|
|
|
|
#[derive(Clone, Debug)]
|
|
pub struct EntityDataItem {
|
|
// we can't identify what the index is for here because we don't know the
|
|
// entity type
|
|
pub index: u8,
|
|
pub value: EntityDataValue,
|
|
}
|
|
|
|
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(Slot),
|
|
Boolean(bool),
|
|
Rotations(Rotations),
|
|
BlockPos(BlockPos),
|
|
OptionalBlockPos(Option<BlockPos>),
|
|
Direction(Direction),
|
|
OptionalUuid(Option<Uuid>),
|
|
// 0 for absent (implies air); otherwise, a block state ID as per the global palette
|
|
// this is a varint
|
|
BlockState(BlockState),
|
|
CompoundTag(azalea_nbt::Tag),
|
|
Particle(Particle),
|
|
VillagerData(VillagerData),
|
|
// 0 for absent; 1 + actual value otherwise. Used for entity IDs.
|
|
OptionalUnsignedInt(OptionalUnsignedInt),
|
|
Pose(Pose),
|
|
CatVariant(azalea_registry::CatVariant),
|
|
FrogVariant(azalea_registry::FrogVariant),
|
|
OptionalGlobalPos(Option<GlobalPos>),
|
|
PaintingVariant(azalea_registry::PaintingVariant),
|
|
}
|
|
|
|
#[derive(Clone, Debug)]
|
|
pub struct OptionalUnsignedInt(pub Option<u32>);
|
|
|
|
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),
|
|
}
|
|
}
|
|
}
|
|
|
|
/// A set of x, y, and z rotations. This is used for armor stands.
|
|
#[derive(Clone, Debug, McBuf, Default)]
|
|
pub struct Rotations {
|
|
pub x: f32,
|
|
pub y: f32,
|
|
pub z: f32,
|
|
}
|
|
|
|
#[derive(Clone, Debug, Copy, McBuf, Default, Component)]
|
|
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();
|
|
// check to make sure it's valid, in vanilla this is guaranteed to pass
|
|
// but it's possible there's mods that mess with it so we want to make
|
|
// sure it's good
|
|
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)
|
|
}
|
|
}
|