diff --git a/build/block.rs b/build/block.rs index 4b68106..44bf1ba 100644 --- a/build/block.rs +++ b/build/block.rs @@ -7,9 +7,9 @@ use proc_macro2::TokenStream; use quote::quote; use serde::Deserialize; -use crate::{ident, write_to_out_path}; +use crate::ident; -pub fn build() -> anyhow::Result<()> { +pub fn build() -> anyhow::Result { let blocks = parse_blocks_json()?; let max_block_state = blocks.iter().map(|b| b.max_state_id).max().unwrap(); @@ -297,7 +297,7 @@ pub fn build() -> anyhow::Result<()> { let property_name_count = prop_values.len(); - let finished = quote! { + Ok(quote! { /// Represents the state of a block, not including block entity data such as /// the text on a sign, the design on a banner, or the content of a spawner. #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Default, Hash)] @@ -551,9 +551,7 @@ pub fn build() -> anyhow::Result<()> { Self::from_bool(b) } } - }; - - write_to_out_path("block.rs", &finished.to_string()) + }) } struct Block { diff --git a/build/entity.rs b/build/entity.rs index 8d48f4a..f396ed9 100644 --- a/build/entity.rs +++ b/build/entity.rs @@ -1,2497 +1,473 @@ -//! See: and +use std::collections::BTreeMap; -use std::collections::{BTreeMap, HashMap}; - -use anyhow::Context; -use heck::{ToPascalCase, ToSnakeCase}; -use proc_macro2::TokenStream; +use heck::ToPascalCase; +use proc_macro2::{Ident, TokenStream}; use quote::quote; use serde::Deserialize; -use crate::{ident, write_to_out_path}; +use crate::ident; -struct Class { - name: &'static str, - inherit: Option<&'static Class>, - fields: &'static [Field], - events: &'static [Event], +#[derive(Deserialize, Clone, Debug)] +struct Entity { + #[serde(rename = "type")] + typ: Option, + translation_key: Option, + fields: Vec, + parent: Option, } -impl Class { - pub fn collect_fields(&self, fields: &mut Vec<&'static Field>) { - if let Some(parent) = self.inherit { - parent.collect_fields(fields); - } - fields.extend(self.fields); - } - - pub fn collect_events(&self, events: &mut Vec) { - if let Some(parent) = self.inherit { - parent.collect_events(events); - } - events.extend(self.events); - } +#[derive(Deserialize, Clone, Debug)] +struct EntityData { + types: BTreeMap, } +#[derive(Deserialize, Clone, Debug)] struct Field { - name: &'static str, - typ: Type, + name: String, + index: u8, + #[serde(flatten)] + default_value: Value, + bits: Vec, } -macro_rules! def_events { - ( - $( - $variant:ident $(= $val:expr)? - ),* $(,)? - ) => { - #[derive(Clone, Copy, PartialEq, Eq)] - #[allow(dead_code)] - enum Event { - $($variant $(= $val)*,)* - } - - impl Event { - pub fn snake_case_name(self) -> String { - match self { - $( - Self::$variant => stringify!($variant).to_snake_case(), - )* - } - } - } - } -} - -def_events! { - // Entity events - Jump = 1, - Hurt, - Death, - StartAttacking, - StopAttacking, - TamingFailed, - TamingSucceeded, - ShakeWetness, - UseItemComplete, - EatGrass, - OfferFlower, - LoveHearts, - VillagerAngry, - VillagerHappy, - WitchHatMagic, - ZombieConverting, - FireworksExplode, - InLoveHearts, - SquidAnimSynch, - SilverfishMergeAnim, - GuardianAttackSound, - ReducedDebugInfo, - FullDebugInfo, - PermissionLevelAll, - PermissionLevelModerators, - PermissionLevelGamemasters, - PermissionLevelAdmins, - PermissionLevelOwners, - AttackBlocked, - ShieldDisabled, - FishingRodReelIn, - ArmorstandWobble, - Thorned, - StopOfferFlower, - TalismanActivate, - Drowned, - Burned, - DolphinLookingForTreasure, - RavagerStunned, - TrustingFailed, - TrustingSucceeded, - VillagerSweat, - BadOmenTriggered, - Poked, - FoxEat, - Teleport, - MainhandBreak, - OffhandBreak, - HeadBreak, - ChestBreak, - LegsBreak, - FeetBreak, - HoneySlide, - HoneyJump, - SwapHands, - CancelShakeWetness, - Frozen, - StartRam, - EndRam, - Poof, - TendrilsShiver, - SonicCharge, - // Animations - SwingMainArm, - TakeDamage, - LeaveBed, - SwingOffhand, - CriticalEffect, - MagicCriticalEffect, -} - -/// Each variant contains the default value for the field. -enum Type { - BitFields(&'static [BitField]), +#[derive(Deserialize, Clone, Debug)] +#[serde(tag = "type", content = "default_value", rename_all = "snake_case")] +enum Value { Byte(u8), - VarInt(i32), + Integer(i32), Float(f32), - String(&'static str), - Text, - OptText(Option<&'static str>), - Slot, - Bool(bool), - ArmorStandRotations(f32, f32, f32), - BlockPos(i32, i32, i32), - OptBlockPos(Option<(i32, i32, i32)>), - Direction, - OptUuid, - BlockState, - Nbt, - Particle, - VillagerData, - /// Also known as OptVarInt - OptEntityId, - Pose, - CatKind, - FrogKind, - OptGlobalPosition, - PaintingKind, - // ==== Specialized ==== // - BoatKind, - MainHand, + String(String), + TextComponent(String), + OptionalTextComponent(Option), + ItemStack(String), + Boolean(bool), + Rotation { + pitch: f32, + yaw: f32, + roll: f32, + }, + BlockPos(BlockPos), + OptionalBlockPos(Option), + Facing(String), + OptionalUuid(Option), + OptionalBlockState(Option), + NbtCompound(String), + Particle(String), + VillagerData { + #[serde(rename = "type")] + typ: String, + profession: String, + level: i32, + }, + OptionalInt(Option), + EntityPose(String), + CatVariant(String), + FrogVariant(String), + OptionalGlobalPos(Option<()>), // TODO + PaintingVariant(String), } -impl Type { - pub fn default_expr(&self) -> TokenStream { - match self { - Type::BitFields(bfs) => { - let mut default = 0; - for bf in *bfs { - default = (bf.default as u8) << bf.offset; - } - quote! { #default } - } - Type::Byte(d) => quote! { #d }, - Type::VarInt(d) => quote! { VarInt(#d) }, - Type::Float(d) => quote! { #d }, - Type::String(d) => quote! { #d.into() }, - Type::Text => quote! { Default::default() }, - Type::OptText(d) => match d { - Some(d) => quote! { Some(Box::new(Text::from(#d))) }, - None => quote! { None }, - }, - Type::Slot => quote! { () }, // TODO - Type::Bool(d) => quote! { #d }, - Type::ArmorStandRotations(x, y, z) => { - quote! { ArmorStandRotations::new(#x, #y, #z) } - } - Type::BlockPos(x, y, z) => quote! { BlockPos::new(#x, #y, #z) }, - Type::OptBlockPos(d) => match d { - Some((x, y, z)) => quote! { Some(BlockPos::new(#x, #y, #z)) }, - None => quote! { None }, - }, - Type::Direction => quote! { Direction::Down }, - Type::OptUuid => quote! { None }, - Type::BlockState => quote! { BlockState::AIR }, - Type::Nbt => quote! { nbt::Blob::new() }, - Type::Particle => quote! { () }, // TODO - Type::VillagerData => quote! { VillagerData::default() }, - Type::OptEntityId => quote! { None }, - Type::Pose => quote! { Pose::default() }, - Type::CatKind => quote! { CatKind::default() }, - Type::FrogKind => quote! { FrogKind::default() }, - Type::OptGlobalPosition => quote! { () }, // TODO - Type::PaintingKind => quote! { PaintingKind::default() }, - Type::BoatKind => quote! { BoatKind::default() }, - Type::MainHand => quote! { MainHand::default() }, - } - } +#[derive(Deserialize, Debug, Clone, Copy)] +struct BlockPos { + x: i32, + y: i32, + z: i32, +} +#[derive(Deserialize, Clone, Debug)] +struct Bit { + name: String, + index: u8, +} + +impl Value { pub fn type_id(&self) -> i32 { match self { - Type::BitFields(_) => 0, - Type::Byte(_) => 0, - Type::VarInt(_) => 1, - Type::Float(_) => 2, - Type::String(_) => 3, - Type::Text => 4, - Type::OptText(_) => 5, - Type::Slot => 6, - Type::Bool(_) => 7, - Type::ArmorStandRotations(_, _, _) => 8, - Type::BlockPos(_, _, _) => 9, - Type::OptBlockPos(_) => 10, - Type::Direction => 11, - Type::OptUuid => 12, - Type::BlockState => 13, - Type::Nbt => 14, - Type::Particle => 15, - Type::VillagerData => 16, - Type::OptEntityId => 17, - Type::Pose => 18, - Type::CatKind => 19, - Type::FrogKind => 20, - Type::OptGlobalPosition => 21, - Type::PaintingKind => 22, - Type::BoatKind => 1, - Type::MainHand => 0, + Value::Byte(_) => 0, + Value::Integer(_) => 1, + Value::Float(_) => 2, + Value::String(_) => 3, + Value::TextComponent(_) => 4, + Value::OptionalTextComponent(_) => 5, + Value::ItemStack(_) => 6, + Value::Boolean(_) => 7, + Value::Rotation { .. } => 8, + Value::BlockPos(_) => 9, + Value::OptionalBlockPos(_) => 10, + Value::Facing(_) => 11, + Value::OptionalUuid(_) => 12, + Value::OptionalBlockState(_) => 13, + Value::NbtCompound(_) => 14, + Value::Particle(_) => 15, + Value::VillagerData { .. } => 16, + Value::OptionalInt(_) => 17, + Value::EntityPose(_) => 18, + Value::CatVariant(_) => 19, + Value::FrogVariant(_) => 20, + Value::OptionalGlobalPos(_) => 21, + Value::PaintingVariant(_) => 22, } } -} -struct BitField { - name: &'static str, - offset: u8, - default: bool, -} - -const BASE_ENTITY: Class = Class { - name: "base_entity", - inherit: None, - fields: &[ - Field { - name: "base_entity_flags", - typ: Type::BitFields(&[ - BitField { - name: "on_fire", - offset: 0, - default: false, - }, - BitField { - name: "crouching", - offset: 1, - default: false, - }, - BitField { - name: "sprinting", - offset: 3, // Skipping unused - default: false, - }, - BitField { - name: "swimming", - offset: 4, - default: false, - }, - BitField { - name: "invisible", - offset: 5, - default: false, - }, - BitField { - name: "glowing", - offset: 6, - default: false, - }, - BitField { - name: "elytra_flying", - offset: 7, - default: false, - }, - ]), - }, - Field { - name: "air_ticks", - typ: Type::VarInt(300), - }, - Field { - name: "custom_name", - typ: Type::OptText(None), - }, - Field { - name: "custom_name_visible", - typ: Type::Bool(false), - }, - Field { - name: "silent", - typ: Type::Bool(false), - }, - Field { - name: "no_gravity", - typ: Type::Bool(false), - }, - Field { - name: "pose", - typ: Type::Pose, - }, - Field { - name: "frozen_ticks", - typ: Type::VarInt(0), - }, - ], - events: &[], -}; - -const ABSTRACT_ARROW: Class = Class { - name: "abstract_arrow", - inherit: Some(&BASE_ENTITY), - fields: &[ - Field { - name: "abstract_arrow_flags", - typ: Type::BitFields(&[ - BitField { - name: "critical", - offset: 0, - default: false, - }, - BitField { - name: "noclip", - offset: 1, - default: false, - }, - ]), - }, - Field { - name: "piercing_level", - typ: Type::Byte(0), - }, - ], - events: &[], -}; - -const ITEM_FRAME: Class = Class { - name: "item_frame", - inherit: Some(&BASE_ENTITY), - fields: &[ - Field { - name: "item", - typ: Type::Slot, - }, - Field { - name: "rotation", - typ: Type::VarInt(0), // TODO: Direction enum? - }, - ], - events: &[], -}; - -const BOAT: Class = Class { - name: "boat", - inherit: Some(&BASE_ENTITY), - fields: &[ - Field { - name: "last_hit_ticks", - typ: Type::VarInt(0), - }, - Field { - name: "forward_direction", - typ: Type::VarInt(1), // TODO: direction enum? - }, - Field { - name: "damage_taken", - typ: Type::Float(0.0), - }, - Field { - name: "kind", - typ: Type::BoatKind, - }, - Field { - name: "left_paddle_turning", - typ: Type::Bool(false), - }, - Field { - name: "right_paddle_turning", - typ: Type::Bool(false), - }, - Field { - name: "splash_timer", - typ: Type::VarInt(0), - }, - ], - events: &[], -}; - -const LIVING_ENTITY: Class = Class { - name: "living_entity", - inherit: Some(&BASE_ENTITY), - fields: &[ - Field { - name: "living_entity_flags", - typ: Type::BitFields(&[ - BitField { - name: "hand_active", - offset: 0, - default: false, - }, - BitField { - name: "active_hand", - offset: 1, - default: false, - }, - BitField { - name: "riptide_spin_attack", - offset: 2, - default: false, - }, - ]), - }, - Field { - name: "health", - typ: Type::Float(1.0), - }, - Field { - name: "potion_effect_color", - typ: Type::VarInt(0), // TODO: potion effect color type - }, - Field { - name: "potion_effect_ambient", - typ: Type::Bool(false), - }, - Field { - name: "arrow_count", - typ: Type::VarInt(0), - }, - Field { - name: "bee_stinger_count", - typ: Type::VarInt(0), - }, - Field { - name: "bed_sleeping_position", - typ: Type::OptBlockPos(None), - }, - ], - events: &[Event::Hurt], -}; - -const MOB: Class = Class { - name: "mob", - inherit: Some(&LIVING_ENTITY), - fields: &[Field { - name: "mob_flags", - typ: Type::BitFields(&[ - BitField { - name: "ai_disabled", - offset: 0, - default: false, - }, - BitField { - name: "left_handed", - offset: 1, - default: false, - }, - BitField { - name: "aggressive", - offset: 2, - default: false, - }, - ]), - }], - events: &[], -}; - -const AMBIENT_CREATURE: Class = Class { - name: "ambient_creature", - inherit: Some(&MOB), - fields: &[], - events: &[], -}; - -const PATHFINDER_MOB: Class = Class { - name: "pathfinder_mob", - inherit: Some(&MOB), - fields: &[], - events: &[], -}; - -const WATER_ANIMAL: Class = Class { - name: "water_animal", - inherit: Some(&PATHFINDER_MOB), - fields: &[], - events: &[], -}; - -const ABSTRACT_FISH: Class = Class { - name: "abstract_fish", - inherit: Some(&WATER_ANIMAL), - fields: &[Field { - name: "from_bucket", - typ: Type::Bool(false), - }], - events: &[], -}; - -const AGEABLE_MOB: Class = Class { - name: "ageable_mob", - inherit: Some(&PATHFINDER_MOB), - fields: &[Field { - name: "is_baby", - typ: Type::Bool(false), - }], - events: &[], -}; - -const ANIMAL: Class = Class { - name: "animal", - inherit: Some(&AGEABLE_MOB), - fields: &[], - events: &[], -}; - -const ABSTRACT_HORSE: Class = Class { - name: "abstract_horse", - inherit: Some(&ANIMAL), - fields: &[ - Field { - name: "horse_flags", - typ: Type::BitFields(&[ - BitField { - name: "tame", - offset: 1, // Skip unused - default: false, - }, - BitField { - name: "saddled", - offset: 2, - default: false, - }, - BitField { - name: "bred", - offset: 3, - default: false, - }, - BitField { - name: "eating", - offset: 4, - default: false, - }, - BitField { - name: "rearing", - offset: 5, - default: false, - }, - BitField { - name: "mouth_open", - offset: 6, - default: false, - }, - ]), - }, - Field { - name: "owner", - typ: Type::OptUuid, - }, - ], - events: &[], -}; - -const CHESTED_HORSE: Class = Class { - name: "chested_horse", - inherit: Some(&ABSTRACT_HORSE), - fields: &[Field { - name: "has_chest", - typ: Type::Bool(false), - }], - events: &[], -}; - -const COW: Class = Class { - name: "cow", - inherit: Some(&ANIMAL), - fields: &[], - events: &[], -}; - -const TAMEABLE_ANIMAL: Class = Class { - name: "tameable_animal", - inherit: Some(&ANIMAL), - fields: &[Field { - name: "tameable_animal_flags", - typ: Type::BitFields(&[ - BitField { - name: "sitting", - offset: 0, - default: false, - }, - BitField { - name: "tamed", - offset: 2, // Skip unused. - default: false, - }, - ]), - }], - events: &[], -}; - -const ABSTRACT_VILLAGER: Class = Class { - name: "abstract_villager", - inherit: Some(&AGEABLE_MOB), - fields: &[Field { - name: "head_shake_timer", - typ: Type::VarInt(0), - }], - events: &[], -}; - -const ABSTRACT_GOLEM: Class = Class { - name: "abstract_golem", - inherit: Some(&PATHFINDER_MOB), - fields: &[], - events: &[], -}; - -const MONSTER: Class = Class { - name: "monster", - inherit: Some(&PATHFINDER_MOB), - fields: &[], - events: &[], -}; - -const BASE_PIGLIN: Class = Class { - name: "base_piglin", - inherit: Some(&MONSTER), - fields: &[Field { - name: "zombification_immune", - typ: Type::Bool(false), - }], - events: &[], -}; - -const GUARDIAN: Class = Class { - name: "guardian", - inherit: Some(&MONSTER), - fields: &[ - Field { - name: "retracting_spikes", - typ: Type::Bool(false), - }, - Field { - name: "target", - typ: Type::OptEntityId, - }, - ], - events: &[], -}; - -const RAIDER: Class = Class { - name: "raider", - inherit: Some(&MONSTER), - fields: &[Field { - name: "celebrating", - typ: Type::Bool(false), - }], - events: &[], -}; - -const ABSTRACT_ILLAGER: Class = Class { - name: "abstract_illager", - inherit: Some(&RAIDER), - fields: &[], - events: &[], -}; - -const SPELLCASTER_ILLAGER: Class = Class { - name: "spellcaster_illager", - inherit: Some(&ABSTRACT_ILLAGER), - fields: &[Field { - name: "spellcaster_state", - typ: Type::Byte(0), /* TODO: Spell (0: none, 1: summon vex, 2: attack, 3: wololo, 4: - * disappear, 5: blindness) */ - }], - events: &[], -}; - -const ABSTRACT_SKELETON: Class = Class { - name: "abstract_skeleton", - inherit: Some(&MONSTER), - fields: &[], - events: &[], -}; - -const SPIDER: Class = Class { - name: "spider", - inherit: Some(&MONSTER), - fields: &[Field { - name: "spider_flags", - typ: Type::BitFields(&[BitField { - name: "climbing", - offset: 0, - default: false, - }]), - }], - events: &[], -}; - -const ZOMBIE: Class = Class { - name: "zombie", - inherit: Some(&MONSTER), - fields: &[Field { - name: "baby", - typ: Type::Bool(false), - }], - events: &[], -}; - -const FLYING: Class = Class { - name: "flying", - inherit: Some(&MOB), - fields: &[], - events: &[], -}; - -const ABSTRACT_MINECART: Class = Class { - name: "abstract minecart", - inherit: Some(&BASE_ENTITY), - fields: &[ - Field { - name: "shaking_power", - typ: Type::VarInt(0), - }, - Field { - name: "shaking_direction", - typ: Type::VarInt(1), // TODO: Refined type? - }, - Field { - name: "shaking_multiplier", - typ: Type::Float(0.0), - }, - Field { - name: "custom_block_id", - typ: Type::VarInt(0), // TODO: is this a BlockState? - }, - Field { - name: "custom_block_y_pos", - typ: Type::VarInt(6), // TODO: measured in 16ths of a block. Refined type? - }, - Field { - name: "show_custom_block", - typ: Type::Bool(false), - }, - ], - events: &[], -}; - -const ABSTRACT_MINECART_CONTAINER: Class = Class { - name: "abstract_minecart_container", - inherit: Some(&ABSTRACT_MINECART), - fields: &[], - events: &[], -}; - -const ENTITIES: &[Class] = &[ - Class { - name: "allay", - inherit: Some(&PATHFINDER_MOB), - fields: &[], // TODO: fields? - events: &[], - }, - Class { - // TODO: how is this defined? - name: "leash_knot", - inherit: None, - fields: &[], - events: &[], - }, - Class { - // TODO: how is this defined? - name: "lightning_bolt", - inherit: None, - fields: &[], - events: &[], - }, - Class { - name: "experience_orb", - inherit: None, - fields: &[], - events: &[], - }, - Class { - name: "marker", - inherit: None, - fields: &[], - events: &[], - }, - Class { - name: "item", - inherit: Some(&BASE_ENTITY), - fields: &[], // TODO: what are the fields? - events: &[], - }, - Class { - name: "egg", - inherit: Some(&BASE_ENTITY), - fields: &[Field { - name: "item", - typ: Type::Slot, - }], - events: &[], - }, - Class { - name: "ender_pearl", - inherit: Some(&BASE_ENTITY), - fields: &[Field { - name: "item", - typ: Type::Slot, - }], - events: &[], - }, - Class { - name: "experience_bottle", - inherit: Some(&BASE_ENTITY), - fields: &[Field { - name: "item", - typ: Type::Slot, - }], - events: &[], - }, - Class { - name: "potion", - inherit: Some(&BASE_ENTITY), - fields: &[Field { - name: "potion", - typ: Type::Slot, - }], - events: &[], - }, - Class { - name: "snowball", - inherit: Some(&BASE_ENTITY), - fields: &[Field { - name: "item", - typ: Type::Slot, - }], - events: &[], - }, - Class { - name: "eye_of_ender", - inherit: Some(&BASE_ENTITY), - fields: &[Field { - name: "item", - typ: Type::Slot, - }], - events: &[], - }, - Class { - name: "falling_block", - inherit: Some(&BASE_ENTITY), - fields: &[Field { - name: "spawn_position", - typ: Type::BlockPos(0, 0, 0), - }], - events: &[], - }, - Class { - name: "area_effect_cloud", - inherit: Some(&BASE_ENTITY), - fields: &[ - Field { - name: "radius", - typ: Type::Float(0.5), - }, - Field { - name: "color", - typ: Type::VarInt(0), - }, - Field { - name: "ignore_radius", - typ: Type::Bool(false), - }, - Field { - name: "particle", - typ: Type::Particle, - }, - ], - events: &[], - }, - Class { - name: "fishing_bobber", - inherit: Some(&BASE_ENTITY), - fields: &[ - Field { - name: "hooked_entity", - typ: Type::OptEntityId, - }, - Field { - name: "catchable", - typ: Type::Bool(false), - }, - ], - events: &[], - }, - Class { - name: "arrow", - inherit: Some(&ABSTRACT_ARROW), - fields: &[Field { - name: "color", - typ: Type::VarInt(-1), // TODO: custom type - }], - events: &[], - }, - Class { - name: "spectral_arrow", - inherit: Some(&ABSTRACT_ARROW), - fields: &[], - events: &[], - }, - Class { - name: "trident", - inherit: Some(&ABSTRACT_ARROW), - fields: &[ - Field { - name: "loyalty_level", - typ: Type::VarInt(0), - }, - Field { - name: "enchantment_glint", - typ: Type::Bool(false), - }, - ], - events: &[], - }, - BOAT, - Class { - name: "chest_boat", - inherit: Some(&BOAT), - fields: &[], - events: &[], - }, - Class { - name: "tadpole", - inherit: Some(&ABSTRACT_FISH), - fields: &[], - events: &[], - }, - Class { - name: "warden", - inherit: Some(&MONSTER), - fields: &[], // TODO: warden anger - events: &[], - }, - Class { - name: "end_crystal", - inherit: Some(&BASE_ENTITY), - fields: &[ - Field { - name: "beam_target", - typ: Type::OptBlockPos(None), - }, - Field { - name: "show_bottom", - typ: Type::Bool(true), - }, - ], - events: &[], - }, - Class { - name: "dragon_fireball", - inherit: Some(&BASE_ENTITY), - fields: &[], - events: &[], - }, - Class { - name: "small_fireball", - inherit: Some(&BASE_ENTITY), - fields: &[Field { - name: "item", - typ: Type::Slot, - }], - events: &[], - }, - Class { - name: "fireball", - inherit: Some(&BASE_ENTITY), - fields: &[Field { - name: "item", - typ: Type::Slot, - }], - events: &[], - }, - Class { - name: "wither_skull", - inherit: Some(&BASE_ENTITY), - fields: &[Field { - name: "invulnerable", - typ: Type::Bool(false), - }], - events: &[], - }, - Class { - name: "firework_rocket", - inherit: Some(&BASE_ENTITY), - fields: &[ - Field { - name: "info", - typ: Type::Slot, - }, - Field { - name: "used_by", - typ: Type::OptEntityId, - }, - Field { - name: "shot_at_angle", - typ: Type::Bool(false), - }, - ], - events: &[], - }, - ITEM_FRAME, - Class { - name: "glow_item_frame", - inherit: Some(&ITEM_FRAME), - fields: &[], - events: &[], - }, - Class { - name: "painting", - inherit: Some(&BASE_ENTITY), - fields: &[Field { - name: "variant", - typ: Type::PaintingKind, - }], - events: &[], - }, - Class { - name: "player", - inherit: Some(&LIVING_ENTITY), - fields: &[ - Field { - name: "additional_hearts", - typ: Type::Float(0.0), - }, - Field { - name: "score", - typ: Type::VarInt(0), - }, - Field { - name: "displayed_skin_parts", - typ: Type::BitFields(&[ - BitField { - name: "cape_enabled", - offset: 0, - default: false, - }, - BitField { - name: "jacket_enabled", - offset: 1, - default: false, - }, - BitField { - name: "left_sleeve_enabled", - offset: 2, - default: false, - }, - BitField { - name: "right_sleeve_enabled", - offset: 3, - default: false, - }, - BitField { - name: "left_pants_leg_enabled", - offset: 4, - default: false, - }, - BitField { - name: "right_pants_leg_enabled", - offset: 5, - default: false, - }, - BitField { - name: "hat_enabled", - offset: 6, - default: false, - }, - ]), - }, - Field { - name: "main_hand", - typ: Type::MainHand, - }, - Field { - name: "left_shoulder_entity_data", - typ: Type::Nbt, - }, - Field { - name: "right_shoulder_entity_data", - typ: Type::Nbt, - }, - Field { - name: "global_position", - typ: Type::OptGlobalPosition, - }, - ], - events: &[ - Event::SwingMainArm, - Event::TakeDamage, - Event::LeaveBed, - Event::SwingOffhand, - Event::CriticalEffect, - Event::MagicCriticalEffect, - ], - }, - Class { - name: "armor_stand", - inherit: Some(&LIVING_ENTITY), - fields: &[ - Field { - name: "armor_stand_flags", - typ: Type::BitFields(&[ - BitField { - name: "small", - offset: 0, - default: false, - }, - BitField { - name: "arms", - offset: 1, - default: false, - }, - BitField { - name: "no_baseplate", - offset: 2, - default: false, - }, - BitField { - name: "marker", - offset: 3, - default: false, - }, - ]), - }, - Field { - name: "head_rotation", - typ: Type::ArmorStandRotations(0.0, 0.0, 0.0), - }, - Field { - name: "body_rotation", - typ: Type::ArmorStandRotations(0.0, 0.0, 0.0), - }, - Field { - name: "left_arm_rotation", - typ: Type::ArmorStandRotations(-10.0, 0.0, -10.0), - }, - Field { - name: "right_arm_rotation", - typ: Type::ArmorStandRotations(-15.0, 0.0, -10.0), - }, - Field { - name: "left_leg_rotation", - typ: Type::ArmorStandRotations(-1.0, 0.0, -1.0), - }, - Field { - name: "right_leg_rotation", - typ: Type::ArmorStandRotations(1.0, 0.0, 1.0), - }, - ], - events: &[], - }, - Class { - name: "bat", - inherit: Some(&AMBIENT_CREATURE), - fields: &[Field { - name: "bat_flags", - typ: Type::BitFields(&[BitField { - name: "hanging", - offset: 0, - default: false, - }]), - }], - events: &[], - }, - Class { - name: "squid", - inherit: Some(&WATER_ANIMAL), - fields: &[], - events: &[], - }, - Class { - // TODO: How is glow squid defined? This is a guess. - name: "glow_squid", - inherit: Some(&WATER_ANIMAL), - fields: &[], - events: &[], - }, - Class { - name: "dolphin", - inherit: Some(&WATER_ANIMAL), - fields: &[ - Field { - name: "treasure_position", - typ: Type::BlockPos(0, 0, 0), - }, - Field { - name: "has_fish", - typ: Type::Bool(false), - }, - Field { - name: "moisture_level", - typ: Type::VarInt(2400), - }, - ], - events: &[], - }, - Class { - name: "cod", - inherit: Some(&ABSTRACT_FISH), - fields: &[], - events: &[], - }, - Class { - name: "pufferfish", - inherit: Some(&ABSTRACT_FISH), - fields: &[Field { - name: "puff_state", - typ: Type::VarInt(0), // TODO: PuffState in the range [0, 2]. (Bounded int?) - }], - events: &[], - }, - Class { - name: "salmon", - inherit: Some(&ABSTRACT_FISH), - fields: &[], - events: &[], - }, - Class { - name: "tropical_fish", - inherit: Some(&ABSTRACT_FISH), - fields: &[Field { - name: "variant", - typ: Type::VarInt(0), // TODO: TropicalFishVariant enum - }], - events: &[], - }, - Class { - name: "horse", - inherit: Some(&ABSTRACT_HORSE), - fields: &[Field { - name: "variant", - typ: Type::VarInt(0), // TODO: HorseVariant enum - }], - events: &[], - }, - Class { - name: "zombie_horse", - inherit: Some(&ABSTRACT_HORSE), - fields: &[], - events: &[], - }, - Class { - name: "skeleton_horse", - inherit: Some(&ABSTRACT_HORSE), - fields: &[], - events: &[], - }, - Class { - name: "donkey", - inherit: Some(&CHESTED_HORSE), - fields: &[], - events: &[], - }, - Class { - name: "llama", - inherit: Some(&CHESTED_HORSE), - fields: &[ - Field { - name: "strength", - typ: Type::VarInt(0), // TODO: upper bound? - }, - Field { - name: "carpet_color", - typ: Type::VarInt(-1), // TODO: Carpet color enum. - }, - Field { - name: "variant", - typ: Type::VarInt(0), // TODO: Llama variant enum. - }, - ], - events: &[], - }, - Class { - name: "trader_llama", - inherit: None, // TODO: really? - fields: &[], - events: &[], - }, - Class { - name: "mule", - inherit: Some(&CHESTED_HORSE), - fields: &[], - events: &[], - }, - Class { - name: "axolotl", - inherit: Some(&ANIMAL), - fields: &[ - Field { - name: "variant", - typ: Type::VarInt(0), // TODO: AxolotlVariant enum. - }, - Field { - name: "playing_dead", - typ: Type::Bool(false), - }, - Field { - name: "from_bucket", - typ: Type::Bool(false), - }, - ], - events: &[], - }, - Class { - name: "bee", - inherit: Some(&ANIMAL), - fields: &[ - Field { - name: "bee_flags", - typ: Type::BitFields(&[ - BitField { - name: "angry", - offset: 1, // Skip unused. - default: false, - }, - BitField { - name: "stung", - offset: 2, - default: false, - }, - BitField { - name: "nectar", - offset: 3, - default: false, - }, - ]), - }, - Field { - name: "anger_ticks", - typ: Type::VarInt(0), - }, - ], - events: &[], - }, - Class { - name: "fox", - inherit: Some(&ANIMAL), - fields: &[ - Field { - name: "variant", - typ: Type::VarInt(0), // TODO: 0 for red, 1 for snow - }, - Field { - name: "fox_flags", - typ: Type::BitFields(&[ - BitField { - name: "sitting", - offset: 0, - default: false, - }, - BitField { - name: "fox_crouching", - offset: 2, // Skip unused - default: false, - }, - BitField { - name: "interested", - offset: 3, - default: false, - }, - BitField { - name: "pouncing", - offset: 4, - default: false, - }, - BitField { - name: "sleeping", - offset: 5, - default: false, - }, - BitField { - name: "faceplanted", - offset: 6, - default: false, - }, - BitField { - name: "defending", - offset: 7, - default: false, - }, - ]), - }, - // TODO: what are these UUIDs? - Field { - name: "first_uuid", - typ: Type::OptUuid, - }, - Field { - name: "second_uuid", - typ: Type::OptUuid, - }, - ], - events: &[], - }, - Class { - name: "frog", - inherit: Some(&ANIMAL), - fields: &[ - Field { - name: "variant", - typ: Type::FrogKind, - }, - Field { - name: "tongue_target", - typ: Type::VarInt(0), - }, - ], - events: &[], - }, - Class { - name: "ocelot", - inherit: Some(&ANIMAL), - fields: &[Field { - name: "trusting", - typ: Type::Bool(false), - }], - events: &[], - }, - Class { - name: "panda", - inherit: Some(&ANIMAL), - fields: &[ - Field { - name: "breed_timer", - typ: Type::VarInt(0), - }, - Field { - name: "sneeze_timer", - typ: Type::VarInt(0), - }, - Field { - name: "eat_timer", - typ: Type::VarInt(0), - }, - Field { - name: "main_gene", - typ: Type::Byte(0), - }, - Field { - name: "hidden_gene", - typ: Type::Byte(0), - }, - Field { - name: "panda_flags", - typ: Type::BitFields(&[ - BitField { - name: "sneezing", - offset: 1, // Skip unused. - default: false, - }, - BitField { - name: "rolling", - offset: 2, - default: false, - }, - BitField { - name: "sitting", - offset: 3, - default: false, - }, - BitField { - name: "on_back", - offset: 4, - default: false, - }, - ]), - }, - ], - events: &[], - }, - Class { - name: "pig", - inherit: Some(&ANIMAL), - fields: &[ - Field { - name: "has_saddle", - typ: Type::Bool(false), - }, - Field { - name: "boost_timer", - typ: Type::VarInt(0), - }, - ], - events: &[], - }, - Class { - name: "rabbit", - inherit: Some(&ANIMAL), - fields: &[Field { - name: "variant", - typ: Type::VarInt(0), // TODO: rabbit variant enum. - }], - events: &[], - }, - Class { - name: "turtle", - inherit: Some(&ANIMAL), - fields: &[ - Field { - name: "home_position", - typ: Type::BlockPos(0, 0, 0), - }, - Field { - name: "has_egg", - typ: Type::Bool(false), - }, - Field { - name: "laying_egg", - typ: Type::Bool(false), - }, - Field { - name: "travel_pos", - typ: Type::BlockPos(0, 0, 0), - }, - Field { - name: "going_home", - typ: Type::Bool(false), - }, - Field { - name: "travelling", - typ: Type::Bool(false), - }, - ], - events: &[], - }, - Class { - name: "polar_bear", - inherit: Some(&ANIMAL), - fields: &[Field { - name: "standing_up", - typ: Type::Bool(true), - }], - events: &[], - }, - Class { - name: "chicken", - inherit: Some(&ANIMAL), - fields: &[], - events: &[], - }, - COW, - Class { - name: "hoglin", - inherit: Some(&ANIMAL), - fields: &[Field { - name: "zombification_immune", - typ: Type::Bool(false), - }], - events: &[], - }, - Class { - name: "mooshroom", - inherit: Some(&COW), - fields: &[Field { - name: "variant", - typ: Type::String("red"), // TODO: "red" or "brown" enum. - }], - events: &[], - }, - Class { - name: "sheep", - inherit: Some(&ANIMAL), - fields: &[Field { - name: "sheep_state", - typ: Type::Byte(0), // TODO: sheep state type. - }], - events: &[], - }, - Class { - name: "goat", - inherit: Some(&ANIMAL), - fields: &[ - Field { - name: "screaming", - typ: Type::Bool(false), - }, - Field { - name: "left_horn", - typ: Type::Bool(true), - }, - Field { - name: "right_horn", - typ: Type::Bool(true), - }, - ], - events: &[], - }, - Class { - name: "strider", - inherit: Some(&ANIMAL), - fields: &[ - Field { - name: "boost_timer", - typ: Type::VarInt(0), - }, - Field { - name: "shaking", - typ: Type::Bool(false), - }, - Field { - name: "saddle", - typ: Type::Bool(false), - }, - ], - events: &[], - }, - Class { - name: "cat", - inherit: Some(&TAMEABLE_ANIMAL), - fields: &[ - Field { - name: "variant", - typ: Type::CatKind, - }, - Field { - name: "lying", - typ: Type::Bool(false), - }, - Field { - name: "relaxed", - typ: Type::Bool(false), - }, - Field { - name: "collar_color", - typ: Type::VarInt(14), // TODO: dye color enum. - }, - ], - events: &[], - }, - Class { - name: "wolf", - inherit: Some(&TAMEABLE_ANIMAL), - fields: &[ - Field { - name: "begging", - typ: Type::Bool(false), - }, - Field { - name: "collar_color", // TODO: dye color enum - typ: Type::VarInt(14), - }, - Field { - name: "anger_timer", - typ: Type::VarInt(0), - }, - ], - events: &[], - }, - Class { - name: "parrot", - inherit: Some(&TAMEABLE_ANIMAL), - fields: &[Field { - name: "variant", - typ: Type::VarInt(0), // TODO: parrot variant enum. - }], - events: &[], - }, - Class { - name: "villager", - inherit: Some(&ABSTRACT_VILLAGER), - fields: &[Field { - name: "villager_data", - typ: Type::VillagerData, - }], - events: &[], - }, - Class { - name: "wandering_trader", - inherit: Some(&ABSTRACT_VILLAGER), - fields: &[], - events: &[], - }, - Class { - name: "iron_golem", - inherit: Some(&ABSTRACT_GOLEM), - fields: &[Field { - name: "iron_golem_flags", - typ: Type::BitFields(&[BitField { - name: "player_created", - offset: 0, - default: false, - }]), - }], - events: &[], - }, - Class { - name: "snow_golem", - inherit: Some(&ABSTRACT_GOLEM), - fields: &[Field { - name: "snow_golem_flags", - typ: Type::BitFields(&[BitField { - name: "pumpkin_hat", - offset: 4, - default: true, - }]), - }], - events: &[], - }, - Class { - name: "shulker", - inherit: Some(&ABSTRACT_GOLEM), - fields: &[ - Field { - name: "attach_face", - typ: Type::Direction, - }, - Field { - name: "attachment_position", - typ: Type::OptBlockPos(None), - }, - Field { - name: "shield_height", - typ: Type::Byte(0), - }, - Field { - name: "color", - typ: Type::Byte(10), // TODO: dye color enum - }, - ], - events: &[], - }, - Class { - // TODO: how is this defined? - name: "shulker_bullet", - inherit: Some(&BASE_ENTITY), - fields: &[], - events: &[], - }, - Class { - name: "piglin", - inherit: Some(&BASE_PIGLIN), - fields: &[ - Field { - name: "baby", - typ: Type::Bool(false), - }, - Field { - name: "charging_crossbow", - typ: Type::Bool(false), - }, - Field { - name: "dancing", - typ: Type::Bool(false), - }, - ], - events: &[], - }, - Class { - name: "piglin_brute", - inherit: Some(&BASE_PIGLIN), - fields: &[], - events: &[], - }, - Class { - name: "blaze", - inherit: Some(&MONSTER), - fields: &[Field { - name: "blaze_flags", - typ: Type::BitFields(&[BitField { - name: "blaze_on_fire", // TODO: better name for this? - offset: 0, - default: false, - }]), - }], - events: &[], - }, - Class { - name: "creeper", - inherit: Some(&MONSTER), - fields: &[ - Field { - name: "creeper_state", - typ: Type::VarInt(-1), // TODO -1 for idle, +1 for fuse. - }, - Field { - name: "charged", - typ: Type::Bool(false), - }, - Field { - name: "ignited", - typ: Type::Bool(false), - }, - ], - events: &[], - }, - Class { - name: "endermite", - inherit: Some(&MONSTER), - fields: &[], - events: &[], - }, - Class { - name: "giant", - inherit: Some(&MONSTER), - fields: &[], - events: &[], - }, - GUARDIAN, - Class { - name: "elder_guardian", - inherit: Some(&GUARDIAN), - fields: &[], - events: &[], - }, - Class { - name: "silverfish", - inherit: Some(&MONSTER), - fields: &[], - events: &[], - }, - Class { - name: "vindicator", - inherit: Some(&ABSTRACT_ILLAGER), - fields: &[], - events: &[], - }, - Class { - name: "pillager", - inherit: Some(&ABSTRACT_ILLAGER), - fields: &[Field { - name: "charging", - typ: Type::Bool(false), - }], - events: &[], - }, - Class { - name: "evoker", - inherit: Some(&SPELLCASTER_ILLAGER), - fields: &[], - events: &[], - }, - Class { - name: "illusioner", - inherit: Some(&SPELLCASTER_ILLAGER), - fields: &[], - events: &[], - }, - Class { - name: "ravager", - inherit: Some(&RAIDER), - fields: &[], - events: &[], - }, - Class { - name: "evoker_fangs", - inherit: Some(&BASE_ENTITY), - fields: &[], - events: &[], - }, - Class { - name: "witch", - inherit: Some(&RAIDER), - fields: &[Field { - name: "drinking_potion", - typ: Type::Bool(false), - }], - events: &[], - }, - Class { - name: "vex", - inherit: Some(&MONSTER), - fields: &[Field { - name: "vex_flags", - typ: Type::BitFields(&[BitField { - name: "attacking", - offset: 0, - default: false, - }]), - }], - events: &[], - }, - Class { - name: "skeleton", - inherit: Some(&ABSTRACT_SKELETON), - fields: &[], - events: &[], - }, - Class { - name: "wither_skeleton", - inherit: Some(&ABSTRACT_SKELETON), - fields: &[], - events: &[], - }, - Class { - name: "stray", - inherit: Some(&ABSTRACT_SKELETON), - fields: &[], - events: &[], - }, - SPIDER, - Class { - name: "cave_spider", - inherit: Some(&SPIDER), // TODO: does cave_spider inherit from spider? - fields: &[], - events: &[], - }, - Class { - name: "wither", - inherit: Some(&MONSTER), - fields: &[ - // TODO: are these actually OptEntityId, or something else? - Field { - name: "center_head_target", - typ: Type::OptEntityId, - }, - Field { - name: "left_head_target", - typ: Type::OptEntityId, - }, - Field { - name: "right_head_target", - typ: Type::OptEntityId, - }, - Field { - name: "invulnerable_time", - typ: Type::VarInt(0), - }, - ], - events: &[], - }, - Class { - name: "zoglin", - inherit: Some(&MONSTER), - fields: &[Field { - name: "baby", - typ: Type::Bool(false), - }], - events: &[], - }, - ZOMBIE, - Class { - name: "zombie_villager", - inherit: Some(&ZOMBIE), - fields: &[ - Field { - name: "converting", - typ: Type::Bool(false), - }, - Field { - name: "villager_data", - typ: Type::VillagerData, - }, - ], - events: &[], - }, - Class { - name: "husk", - inherit: Some(&ZOMBIE), - fields: &[], - events: &[], - }, - Class { - name: "drowned", - inherit: Some(&ZOMBIE), - fields: &[], - events: &[], - }, - Class { - name: "zombified_piglin", - inherit: Some(&ZOMBIE), - fields: &[], - events: &[], - }, - Class { - name: "enderman", - inherit: Some(&MONSTER), - fields: &[ - Field { - name: "carried_block", - typ: Type::BlockState, - }, - Field { - name: "screaming", - typ: Type::Bool(false), - }, - Field { - name: "staring", - typ: Type::Bool(false), - }, - ], - events: &[], - }, - Class { - name: "ender_dragon", - inherit: Some(&MOB), - fields: &[Field { - name: "phase", - typ: Type::VarInt(10), // TODO: dragon phase enum - }], - events: &[], - }, - Class { - name: "ghast", - inherit: Some(&FLYING), - fields: &[Field { - name: "attacking", - typ: Type::Bool(false), - }], - events: &[], - }, - Class { - name: "phantom", - inherit: Some(&FLYING), - fields: &[Field { - name: "size", - typ: Type::VarInt(0), - }], - events: &[], - }, - Class { - name: "slime", - inherit: Some(&MOB), - fields: &[Field { - name: "size", - typ: Type::VarInt(1), // TODO: bounds? - }], - events: &[], - }, - Class { - name: "magma_cube", - inherit: Some(&MOB), - fields: &[Field { - name: "size", - typ: Type::VarInt(1), - }], - events: &[], - }, - Class { - name: "llama_spit", - inherit: Some(&BASE_ENTITY), - fields: &[], - events: &[], - }, - Class { - name: "minecart", - inherit: Some(&ABSTRACT_MINECART), - fields: &[], - events: &[], - }, - Class { - name: "hopper_minecart", - inherit: Some(&ABSTRACT_MINECART_CONTAINER), - fields: &[], - events: &[], - }, - Class { - name: "chest_minecart", - inherit: Some(&ABSTRACT_MINECART_CONTAINER), - fields: &[], - events: &[], - }, - Class { - name: "furnace_minecart", - inherit: Some(&ABSTRACT_MINECART), - fields: &[Field { - name: "has_fuel", - typ: Type::Bool(false), - }], - events: &[], - }, - Class { - name: "tnt_minecart", - inherit: Some(&ABSTRACT_MINECART), - fields: &[], - events: &[], - }, - Class { - name: "spawner_minecart", - inherit: Some(&ABSTRACT_MINECART), - fields: &[], - events: &[], - }, - Class { - name: "command_block_minecart", - inherit: Some(&ABSTRACT_MINECART), - fields: &[ - Field { - name: "command", - typ: Type::String(""), - }, - Field { - name: "last_output", - typ: Type::Text, - }, - ], - events: &[], - }, - Class { - name: "tnt", - inherit: Some(&BASE_ENTITY), - fields: &[Field { - name: "fuse_timer", - typ: Type::VarInt(80), - }], - events: &[], - }, -]; - -pub fn build() -> anyhow::Result<()> { - // Sort the entities in ID order, where the IDs are obtained from entities.json. - let entities = { - let entities: HashMap<_, _> = ENTITIES.iter().map(|c| (c.name, c)).collect(); - - #[derive(Deserialize)] - struct JsonEntity { - id: usize, - name: String, + pub fn field_type(&self) -> TokenStream { + match self { + Value::Byte(_) => quote!(u8), + Value::Integer(_) => quote!(i32), + Value::Float(_) => quote!(f32), + Value::String(_) => quote!(Box), + Value::TextComponent(_) => quote!(Text), + Value::OptionalTextComponent(_) => quote!(Option), + Value::ItemStack(_) => quote!(()), // TODO + Value::Boolean(_) => quote!(bool), + Value::Rotation { .. } => quote!(EulerAngle), + Value::BlockPos(_) => quote!(BlockPos), + Value::OptionalBlockPos(_) => quote!(Option), + Value::Facing(_) => quote!(Facing), + Value::OptionalUuid(_) => quote!(Option), + Value::OptionalBlockState(_) => quote!(BlockState), + Value::NbtCompound(_) => quote!(nbt::Blob), + Value::Particle(_) => quote!(Particle), + Value::VillagerData { .. } => quote!(VillagerData), + Value::OptionalInt(_) => quote!(OptionalInt), + Value::EntityPose(_) => quote!(Pose), + Value::CatVariant(_) => quote!(CatKind), + Value::FrogVariant(_) => quote!(FrogKind), + Value::OptionalGlobalPos(_) => quote!(()), // TODO + Value::PaintingVariant(_) => quote!(PaintingKind), } + } - let json_entities: Vec = - serde_json::from_str(include_str!("../data/entities.json"))?; - - let mut res = Vec::new(); - - for (i, e) in json_entities.iter().enumerate() { - assert_eq!(e.id, i); - - let name = e.name.as_str(); - - res.push( - *entities - .get(name) - .with_context(|| format!("entity \"{name}\" was not defined"))?, - ); + pub fn getter_return_type(&self) -> TokenStream { + match self { + Value::String(_) => quote!(&str), + Value::TextComponent(_) => quote!(&Text), + Value::OptionalTextComponent(_) => quote!(Option<&Text>), + Value::NbtCompound(_) => quote!(&nbt::Blob), + _ => self.field_type(), } + } - assert_eq!(json_entities.len(), entities.len()); + pub fn getter_return_expr(&self, field_name: &Ident) -> TokenStream { + match self { + Value::String(_) | Value::TextComponent(_) | Value::NbtCompound(_) => { + quote!(&self.#field_name) + } + Value::OptionalTextComponent(_) => quote!(self.#field_name.as_ref()), + _ => quote!(self.#field_name), + } + } - res - }; - - let mut all_classes = BTreeMap::new(); - for mut class in entities.iter().cloned() { - while all_classes.insert(class.name, class).is_none() { - match class.inherit { - Some(parent) => class = parent, - None => break, + pub fn default_expr(&self) -> TokenStream { + match self { + Value::Byte(b) => quote!(#b), + Value::Integer(i) => quote!(#i), + Value::Float(f) => quote!(#f), + Value::String(s) => quote!(#s.to_owned().into_boxed_str()), + Value::TextComponent(_) => quote!(Text::default()), // TODO + Value::OptionalTextComponent(t) => { + assert!(t.is_none()); + quote!(None) + } + Value::ItemStack(_) => quote!(()), // TODO + Value::Boolean(b) => quote!(#b), + Value::Rotation { pitch, yaw, roll } => quote! { + EulerAngle { + pitch: #pitch, + yaw: #yaw, + roll: #roll, + } + }, + Value::BlockPos(BlockPos { x, y, z }) => { + quote!(BlockPos { x: #x, y: #y, z: #z }) + } + Value::OptionalBlockPos(_) => quote!(None), // TODO + Value::Facing(f) => { + let variant = ident(f.to_pascal_case()); + quote!(Facing::#variant) + } + Value::OptionalUuid(_) => quote!(None), // TODO + Value::OptionalBlockState(_) => quote!(BlockState::default()), // TODO + Value::NbtCompound(_) => quote!(nbt::Blob::default()), // TODO + Value::Particle(p) => { + let variant = ident(p.to_pascal_case()); + quote!(Particle::#variant) + } + Value::VillagerData { + typ, + profession, + level, + } => { + let typ = ident(typ.to_pascal_case()); + let profession = ident(profession.to_pascal_case()); + quote!(VillagerData::new(VillagerKind::#typ, VillagerProfession::#profession, #level)) + } + Value::OptionalInt(i) => { + assert!(i.is_none()); + quote!(OptionalInt::default()) + } + Value::EntityPose(p) => { + let variant = ident(p.to_pascal_case()); + quote!(Pose::#variant) + } + Value::CatVariant(c) => { + let variant = ident(c.to_pascal_case()); + quote!(CatKind::#variant) + } + Value::FrogVariant(f) => { + let variant = ident(f.to_pascal_case()); + quote!(FrogKind::#variant) + } + Value::OptionalGlobalPos(_) => quote!(()), + Value::PaintingVariant(p) => { + let variant = ident(p.to_pascal_case()); + quote!(PaintingKind::#variant) } } } - let entity_kind_variants = entities + pub fn encodable_expr(&self, self_lvalue: TokenStream) -> TokenStream { + match self { + Value::Integer(_) => quote!(VarInt(#self_lvalue)), + _ => self_lvalue, + } + } +} + +type Entities = BTreeMap; + +pub fn build() -> anyhow::Result { + let entities = serde_json::from_str::(include_str!("../extracted/entities.json"))? + .into_iter() + .map(|(k, mut v)| { + let strip = |s: String| { + if let Some(stripped) = s.strip_suffix("Entity") { + if !stripped.is_empty() { + return stripped.to_owned(); + } + } + s + }; + v.parent = v.parent.map(strip); + (strip(k), v) + }) + .collect::(); + + let entity_types = + serde_json::from_str::(include_str!("../extracted/entity_data.json"))?.types; + + let concrete_entities = entities + .clone() + .into_iter() + .filter(|(_, v)| v.typ.is_some()) + .collect::(); + + let entity_kind_variants = concrete_entities.iter().map(|(name, e)| { + let name = ident(name); + let id = entity_types[e.typ.as_ref().unwrap()] as isize; + quote! { + #name = #id, + } + }); + + let concrete_entity_names = concrete_entities .iter() - .map(|c| ident(c.name.to_pascal_case())) + .map(|(k, _)| ident(k)) .collect::>(); - let entity_structs = entities.iter().map(|&class| { - let mut fields = Vec::new(); - class.collect_fields(&mut fields); + let concrete_entity_structs = concrete_entities.iter().map(|(struct_name, _)| { + let fields = collect_all_fields(struct_name, &entities); + let struct_name = ident(struct_name); - let name = ident(class.name.to_pascal_case()); - let struct_fields = fields.iter().map(|&f| { - let name = ident(f.name.to_snake_case()); - let typ = match f.typ { - Type::BitFields(_) => quote! { u8 }, - Type::Byte(_) => quote! { u8 }, - Type::VarInt(_) => quote! { VarInt }, - Type::Float(_) => quote! { f32 }, - Type::String(_) => quote! { Box }, - Type::Text => quote! { Box }, - Type::OptText(_) => quote! { Option> }, - Type::Slot => quote! { () }, // TODO - Type::Bool(_) => quote! { bool }, - Type::ArmorStandRotations(_, _, _) => quote! { ArmorStandRotations }, - Type::BlockPos(_, _, _) => quote! { BlockPos }, - Type::OptBlockPos(_) => quote! { Option }, - Type::Direction => quote! { Direction }, - Type::OptUuid => quote! { Option }, - Type::BlockState => quote! { BlockState }, - Type::Nbt => quote! { nbt::Blob }, - Type::Particle => quote! { () }, // TODO - Type::VillagerData => quote! { VillagerData }, - Type::OptEntityId => quote! { Option }, - Type::Pose => quote! { Pose }, - Type::CatKind => quote! { CatKind }, - Type::FrogKind => quote! { FrogKind }, - Type::OptGlobalPosition => quote! { () }, // TODO - Type::PaintingKind => quote! { PaintingKind }, - Type::BoatKind => quote! { BoatKind }, - Type::MainHand => quote! { MainHand }, - }; - quote! { - #name: #typ, - } - }); + let modified_flags_type = + ident("u".to_owned() + &fields.len().next_power_of_two().max(8).to_string()); - let constructor_fields = fields.iter().map(|field| { - let name = ident(field.name.to_snake_case()); - let val = field.typ.default_expr(); - quote! { - #name: #val, - } - }); - - let getter_setters = - fields - .iter() - .enumerate() - .map(|(field_offset, field)| { - let name = ident(field.name.to_snake_case()); - let getter_name = ident(format!("get_{name}")); - let setter_name = ident(format!("set_{name}")); + let struct_fields = fields.iter().map(|&field| { + let name = ident(&field.name); + let typ = field.default_value.field_type(); + quote! { + #name: #typ, + } + }); - let field_offset = field_offset as u32; + let field_initializers = fields.iter().map(|&field| { + let field_name = ident(&field.name); + let init = field.default_value.default_expr(); - // TODO: documentation on methods. + quote! { + #field_name: #init, + } + }); - let standard_getter_setter = |type_name: TokenStream| quote! { - pub fn #getter_name(&self) -> #type_name { - self.#name - } + let getter_setters = fields.iter().map(|&field| { + let field_name = ident(&field.name); + let field_type = field.default_value.field_type(); + let field_index = field.index; - pub fn #setter_name(&mut self, #name: #type_name) { - if self.#name != #name { - self.modified_flags |= 1 << #field_offset; - } + if !field.bits.is_empty() { + field + .bits + .iter() + .map(|bit| { + let bit_name = ident(&bit.name); + let bit_index = bit.index; + let getter_name = ident(format!("get_{}", &bit.name)); + let setter_name = ident(format!("set_{}", &bit.name)); - self.#name = #name; - } - }; + quote! { + pub fn #getter_name(&self) -> bool { + self.#field_name >> #bit_index as #field_type & 1 == 1 + } - match field.typ { - Type::BitFields(bfs) => bfs - .iter() - .map(|bf| { - let bit_name = ident(bf.name.to_snake_case()); + pub fn #setter_name(&mut self, #bit_name: bool) { + if self.#getter_name() != #bit_name { + self.#field_name = + (self.#field_name & !(1 << #bit_index as #field_type)) + | ((#bit_name as #field_type) << #bit_index); - let getter_name = ident(format!("get_{bit_name}")); - let setter_name = ident(format!("set_{bit_name}")); - - let offset = bf.offset; - - quote! { - pub fn #getter_name(&self) -> bool { - (self.#name >> #offset) & 1 == 1 - } - - pub fn #setter_name(&mut self, #bit_name: bool) { - if self.#getter_name() != #bit_name { - self.#name = (self.#name & !(1 << #offset)) | ((#bit_name as u8) << #offset); - self.modified_flags |= 1 << #field_offset; - } + self.__modified_flags |= 1 << #field_index } } - }) - .collect(), - Type::Byte(_) => standard_getter_setter(quote!(u8)), - Type::VarInt(_) => quote! { - pub fn #getter_name(&self) -> i32 { - self.#name.0 } + }) + .collect::() + } else { + let getter_name = ident(format!("get_{}", &field.name)); + let setter_name = ident(format!("set_{}", &field.name)); + let getter_return_type = field.default_value.getter_return_type(); + let getter_return_expr = field.default_value.getter_return_expr(&field_name); - pub fn #setter_name(&mut self, #name: i32) { - if self.#name.0 != #name { - self.modified_flags |= 1 << #field_offset; - } + quote! { + pub fn #getter_name(&self) -> #getter_return_type { + #getter_return_expr + } - self.#name = VarInt(#name); + pub fn #setter_name(&mut self, #field_name: impl Into<#field_type>) { + let #field_name = #field_name.into(); + if self.#field_name != #field_name { + self.__modified_flags |= 1 << #field_index as #modified_flags_type; + self.#field_name = #field_name; } - }, - Type::Float(_) => standard_getter_setter(quote!(f32)), - Type::String(_) => quote! { - pub fn #getter_name(&self) -> &str { - &self.#name - } - - pub fn #setter_name(&mut self, #name: impl Into>) { - let #name = #name.into(); - - if self.#name != #name { - self.modified_flags |= 1 << #field_offset; - } - - self.#name = #name; - } - }, - Type::Text => quote! { - pub fn #getter_name(&self) -> &Text { - &self.#name - } - - pub fn #setter_name(&mut self, #name: impl Into) { - let #name = Box::new(#name.into()); - - if self.#name != #name { - self.modified_flags |= 1 << #field_offset; - } - - self.#name = #name; - } - }, - Type::OptText(_) => quote! { - pub fn #getter_name(&self) -> Option<&Text> { - self.#name.as_deref() - } - - pub fn #setter_name(&mut self, #name: Option>) { - let #name = #name.map(|x| Box::new(x.into())); - - if self.#name != #name { - self.modified_flags |= 1 << #field_offset; - } - - self.#name = #name; - } - }, - Type::Slot => quote! {}, // TODO - Type::Bool(_) => standard_getter_setter(quote!(bool)), - Type::ArmorStandRotations(_, _, _) => standard_getter_setter(quote!(ArmorStandRotations)), - Type::BlockPos(_, _, _) => standard_getter_setter(quote!(BlockPos)), - Type::OptBlockPos(_) => standard_getter_setter(quote!(Option)), - Type::Direction => standard_getter_setter(quote!(Direction)), - Type::OptUuid => standard_getter_setter(quote!(Option)), - Type::BlockState => standard_getter_setter(quote!(BlockState)), - Type::Nbt => quote! { - pub fn #getter_name(&self) -> &nbt::Blob { - &self.#name - } - - pub fn #setter_name(&mut self, #name: nbt::Blob) { - if self.#name != #name { - self.modified_flags |= 1 << #field_offset; - } - - self.#name = #name; - } - }, - Type::Particle => quote! {}, // TODO - Type::VillagerData => standard_getter_setter(quote!(VillagerData)), - Type::OptEntityId => standard_getter_setter(quote!(Option)), - Type::Pose => standard_getter_setter(quote!(Pose)), - Type::CatKind => standard_getter_setter(quote!(CatKind)), - Type::FrogKind => standard_getter_setter(quote!(FrogKind)), - Type::OptGlobalPosition => quote! {}, // TODO - Type::PaintingKind => standard_getter_setter(quote!(PaintingKind)), - Type::BoatKind => standard_getter_setter(quote!(BoatKind)), - Type::MainHand => standard_getter_setter(quote!(MainHand)), - } - }) - .collect::(); - - let mut events = Vec::new(); - class.collect_events(&mut events); - - let trigger_methods = events.into_iter().map(|event| { - let name = ident("trigger_".to_owned() + &event.snake_case_name()); - let code = event as u8; - quote! { - pub fn #name(&mut self) { - self.events.push(#code); + } } } - }).collect::(); + }); + + let initial_tracked_data_stmts = fields.iter().map(|&field| { + let field_name = ident(&field.name); + let field_index = field.index; + let default_expr = field.default_value.default_expr(); + let type_id = field.default_value.type_id(); + let encodeable = field.default_value.encodable_expr(quote!(self.#field_name)); - let initial_metadata_fields = fields.iter().enumerate().map(|(idx, f)| { - let name = ident(f.name.to_snake_case()); - let default = f.typ.default_expr(); - let index: u8 = idx.try_into().unwrap(); - let type_id = f.typ.type_id(); quote! { - if self.#name != #default { - data.push(#index); + if self.#field_name != (#default_expr) { + data.push(#field_index); VarInt(#type_id).encode(data).unwrap(); - self.#name.encode(data).unwrap(); + #encodeable.encode(data).unwrap(); } } - }).collect::(); + }); + + let updated_tracked_data_stmts = fields.iter().map(|&field| { + let field_name = ident(&field.name); + let field_index = field.index; + let type_id = field.default_value.type_id(); + let encodeable = field.default_value.encodable_expr(quote!(self.#field_name)); - let updated_metadata_fields = fields.iter().enumerate().map(|(idx, f)| { - let name = ident(f.name.to_snake_case()); - let u8_index: u8 = idx.try_into().unwrap(); - let u32_index = idx as u32; - let type_id = f.typ.type_id(); quote! { - if (self.modified_flags >> #u32_index) & 1 == 1 { - data.push(#u8_index); + if (self.__modified_flags >> #field_index as #modified_flags_type) & 1 == 1 { + data.push(#field_index); VarInt(#type_id).encode(data).unwrap(); - self.#name.encode(data).unwrap(); + #encodeable.encode(data).unwrap(); } } - }).collect::(); + }); quote! { - pub struct #name { - events: Vec, - /// Contains a set bit for each modified metadata field. - modified_flags: u32, + pub struct #struct_name { + /// Contains a set bit for every modified field. + __modified_flags: #modified_flags_type, #(#struct_fields)* } - impl #name { + impl #struct_name { pub(crate) fn new() -> Self { Self { - events: Vec::new(), - modified_flags: 0, - #(#constructor_fields)* + __modified_flags: 0, + #(#field_initializers)* } } - #getter_setters - - #trigger_methods - - pub(crate) fn initial_metadata(&self, #[allow(unused)] data: &mut Vec) { - #initial_metadata_fields + pub(crate) fn initial_tracked_data(&self, data: &mut Vec) { + #(#initial_tracked_data_stmts)* } - pub(crate) fn updated_metadata(&self, #[allow(unused)] data: &mut Vec) { - if self.modified_flags == 0 { - return; + pub(crate) fn updated_tracked_data(&self, data: &mut Vec) { + if self.__modified_flags != 0 { + #(#updated_tracked_data_stmts)* } - - #updated_metadata_fields - } - - pub(crate) fn event_codes(&self) -> &[u8] { - &self.events } + // TODO: remove this + #[allow(unused)] pub(crate) fn clear_modifications(&mut self) { - self.events.clear(); - self.modified_flags = 0; + self.__modified_flags = 0; } + + #(#getter_setters)* } } }); - let finished = quote! { - /// Identifies a type of entity, such as `chicken`, `zombie` or `item`. - #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] + let translation_key_arms = concrete_entities.iter().map(|(k, v)| { + let name = ident(k); + let key = v + .translation_key + .as_ref() + .expect("translation key should be present for concrete entity"); + + quote! { + Self::#name => #key, + } + }); + + Ok(quote! { + /// Contains a variant for each concrete entity type. + #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] pub enum EntityKind { - #(#entity_kind_variants,)* + #(#entity_kind_variants)* } - impl Default for EntityKind { - fn default() -> Self { - Self::Marker + impl EntityKind { + pub fn translation_key(self) -> &'static str { + match self { + #(#translation_key_arms)* + } } } - #(#entity_structs)* - - /// An enum encoding the type of an entity along with any data specific to that entity type. - pub enum EntityState { - #(#entity_kind_variants(#entity_kind_variants),)* + pub enum EntityEnum { + #(#concrete_entity_names(#concrete_entity_names),)* } - impl EntityState { + impl EntityEnum { pub(super) fn new(kind: EntityKind) -> Self { match kind { - #(EntityKind::#entity_kind_variants => Self::#entity_kind_variants(#entity_kind_variants::new()),)* + #(EntityKind::#concrete_entity_names => Self::#concrete_entity_names(#concrete_entity_names::new()),)* } } - pub(super) fn kind(&self) -> EntityKind { + pub fn kind(&self) -> EntityKind { match self { - #(Self::#entity_kind_variants(_) => EntityKind::#entity_kind_variants,)* + #(Self::#concrete_entity_names(_) => EntityKind::#concrete_entity_names,)* } } - pub(super) fn initial_metadata(&self) -> Option> { + pub(super) fn initial_tracked_data(&self) -> Option> { let mut data = Vec::new(); match self { - #(Self::#entity_kind_variants(e) => e.initial_metadata(&mut data),)* + #(Self::#concrete_entity_names(e) => e.initial_tracked_data(&mut data),)* } if data.is_empty() { @@ -2502,11 +478,11 @@ pub fn build() -> anyhow::Result<()> { } } - pub(super) fn updated_metadata(&self) -> Option> { + pub(super) fn updated_tracked_data(&self) -> Option> { let mut data = Vec::new(); match self { - #(Self::#entity_kind_variants(e) => e.updated_metadata(&mut data),)* + #(Self::#concrete_entity_names(e) => e.updated_tracked_data(&mut data),)* } if data.is_empty() { @@ -2517,19 +493,32 @@ pub fn build() -> anyhow::Result<()> { } } - pub(crate) fn event_codes(&self) -> &[u8] { - match self { - #(Self::#entity_kind_variants(e) => e.event_codes(),)* - } - } - pub(super) fn clear_modifications(&mut self) { match self { - #(Self::#entity_kind_variants(e) => e.clear_modifications(),)* + #(Self::#concrete_entity_names(e) => e.__modified_flags = 0,)* } } } - }; - write_to_out_path("entity.rs", &finished.to_string()) + #(#concrete_entity_structs)* + }) +} + +fn collect_all_fields<'a>(entity_name: &str, entities: &'a Entities) -> Vec<&'a Field> { + fn rec<'a>(entity_name: &str, entities: &'a Entities, fields: &mut Vec<&'a Field>) { + dbg!(entity_name); + let e = &entities[entity_name]; + fields.extend(&e.fields); + + if let Some(parent) = &e.parent { + rec(parent, entities, fields); + } + } + + let mut fields = Vec::new(); + rec(entity_name, entities, &mut fields); + + fields.sort_by_key(|f| f.index); + + fields } diff --git a/build/entity_event.rs b/build/entity_event.rs new file mode 100644 index 0000000..db59eb8 --- /dev/null +++ b/build/entity_event.rs @@ -0,0 +1,66 @@ +use std::collections::BTreeMap; + +use heck::ToPascalCase; +use proc_macro2::TokenStream; +use quote::quote; +use serde::Deserialize; + +use crate::ident; + +#[derive(Deserialize, Clone, Debug)] +struct EntityData { + statuses: BTreeMap, + animations: BTreeMap, +} + +pub fn build() -> anyhow::Result { + let entity_data: EntityData = + serde_json::from_str(include_str!("../extracted/entity_data.json"))?; + + let mut statuses: Vec<_> = entity_data.statuses.into_iter().collect(); + statuses.sort_by_key(|(_, id)| *id); + + let mut animations: Vec<_> = entity_data.animations.into_iter().collect(); + animations.sort_by_key(|(_, id)| *id); + + let event_variants = statuses + .iter() + .chain(animations.iter()) + .map(|(name, _)| ident(name.to_pascal_case())); + + let status_arms = statuses.iter().map(|(name, code)| { + let name = ident(name.to_pascal_case()); + quote! { + Self::#name => StatusOrAnimation::Status(#code), + } + }); + + let animation_arms = animations.iter().map(|(name, code)| { + let name = ident(name.to_pascal_case()); + quote! { + Self::#name => StatusOrAnimation::Animation(#code), + } + }); + + Ok(quote! { + #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] + pub enum Event { + #(#event_variants,)* + } + + impl Event { + pub(crate) fn status_or_animation(self) -> StatusOrAnimation { + match self { + #(#status_arms)* + #(#animation_arms)* + } + } + } + + #[derive(Clone, Copy, PartialEq, Eq, Debug)] + pub(crate) enum StatusOrAnimation { + Status(u8), + Animation(u8), + } + }) +} diff --git a/build/main.rs b/build/main.rs index 0c06b53..a2f5fd8 100644 --- a/build/main.rs +++ b/build/main.rs @@ -7,35 +7,38 @@ use proc_macro2::{Ident, Span}; mod block; mod entity; +mod entity_event; pub fn main() -> anyhow::Result<()> { - for file in ["blocks.json", "entities.json"] { - println!("cargo:rerun-if-changed=data/{file}"); - } + let generators = [ + (entity::build as fn() -> _, "entity.rs"), + (entity_event::build, "entity_event.rs"), + (block::build, "block.rs"), + ]; - block::build()?; - entity::build()?; + let out_dir = env::var_os("OUT_DIR").context("can't get OUT_DIR env var")?; + + for (g, file_name) in generators { + println!("cargo:rerun-if-changed=extracted/{file_name}"); + + let path = Path::new(&out_dir).join(file_name); + let code = g()?.to_string(); + fs::write(&path, &code)?; + + // Format the output for debugging purposes. + // Doesn't matter if rustfmt is unavailable. + let _ = Command::new("rustfmt").arg(path).output(); + } Ok(()) } fn ident(s: impl AsRef) -> Ident { let s = s.as_ref().trim(); - if s.starts_with(char::is_numeric) { - Ident::new(&format!("_{s}"), Span::call_site()) - } else { - Ident::new(s, Span::call_site()) + + match s.as_bytes() { + // TODO: check for the other rust keywords. + [b'0'..=b'9', ..] | b"type" => Ident::new(&format!("_{s}"), Span::call_site()), + _ => Ident::new(s, Span::call_site()), } } - -fn write_to_out_path(file_name: impl AsRef, content: impl AsRef) -> anyhow::Result<()> { - let out_dir = env::var_os("OUT_DIR").context("can't get OUT_DIR env var")?; - let path = Path::new(&out_dir).join(file_name.as_ref()); - - fs::write(&path, &content.as_ref())?; - - // Format the output for debugging purposes. - // Doesn't matter if rustfmt is unavailable. - let _ = Command::new("rustfmt").arg(path).output(); - Ok(()) -} diff --git a/examples/combat.rs b/examples/combat.rs index e2f6f88..b1a6b70 100644 --- a/examples/combat.rs +++ b/examples/combat.rs @@ -3,12 +3,11 @@ use std::sync::atomic::{AtomicUsize, Ordering}; use log::LevelFilter; use valence::block::{BlockPos, BlockState}; -use valence::client::Event::{self}; -use valence::client::{ClientId, GameMode, Hand, InteractWithEntityKind}; +use valence::client::{ClientId, Event, GameMode, Hand, InteractWithEntityKind}; use valence::config::{Config, ServerListPing}; use valence::dimension::DimensionId; -use valence::entity::state::Pose; -use valence::entity::{EntityId, EntityKind, EntityState}; +use valence::entity::data::Pose; +use valence::entity::{EntityEnum, EntityId, EntityKind, Event as EntityEvent}; use valence::server::{Server, SharedServer, ShutdownResult}; use valence::text::{Color, TextFormat}; use valence::{async_trait, Ticks}; @@ -37,7 +36,7 @@ struct ClientData { /// The client's player entity. player: EntityId, /// The extra knockback on the first hit while sprinting. - has_extra_knockback: bool, + extra_knockback: bool, } #[derive(Default)] @@ -67,7 +66,6 @@ impl Config for Game { } fn online_mode(&self) -> bool { - // You'll want this to be true on real servers. false } @@ -160,7 +158,7 @@ impl Config for Game { .unwrap(); client.data.player = player_id; - client.data.has_extra_knockback = true; + client.data.extra_knockback = true; player.data.client = client_id; player.data.last_attack_time = 0; @@ -181,7 +179,7 @@ impl Config for Game { while let Some(event) = client.pop_event() { match event { Event::StartSprinting => { - client.data.has_extra_knockback = true; + client.data.extra_knockback = true; } Event::InteractWithEntity { id, @@ -195,21 +193,18 @@ impl Config for Game { { target.data.attacked = true; target.data.attacker_pos = client.position(); - target.data.extra_knockback = client.data.has_extra_knockback; + target.data.extra_knockback = client.data.extra_knockback; target.data.last_attack_time = current_tick; - client.data.has_extra_knockback = false; + client.data.extra_knockback = false; } } } Event::ArmSwing(hand) => { let player = server.entities.get_mut(client.data.player).unwrap(); - - if let EntityState::Player(e) = &mut player.state { - match hand { - Hand::Main => e.trigger_swing_main_arm(), - Hand::Off => e.trigger_swing_offhand(), - } + match hand { + Hand::Main => player.trigger_event(EntityEvent::SwingMainHand), + Hand::Off => player.trigger_event(EntityEvent::SwingOffHand), } } _ => (), @@ -237,7 +232,7 @@ impl Config for Game { player.set_pitch(client.pitch()); player.set_on_ground(client.on_ground()); - if let EntityState::Player(player) = &mut player.state { + if let EntityEnum::Player(player) = player.view_mut() { if client.is_sneaking() { player.set_pose(Pose::Sneaking); } else { @@ -266,12 +261,10 @@ impl Config for Game { victim.set_velocity(victim.velocity() / 2.0 + vel.as_()); - if let EntityState::Player(e) = &mut e.state { - e.trigger_take_damage(); - e.trigger_hurt(); - } - victim.player_mut().trigger_take_damage(); - victim.player_mut().trigger_hurt(); + e.trigger_event(EntityEvent::DamageFromGenericSource); + e.trigger_event(EntityEvent::Damage); + victim.trigger_entity_event(EntityEvent::DamageFromGenericSource); + victim.trigger_entity_event(EntityEvent::Damage); } } } diff --git a/examples/conway.rs b/examples/conway.rs index 0efa105..3c54f4c 100644 --- a/examples/conway.rs +++ b/examples/conway.rs @@ -10,8 +10,8 @@ use valence::block::BlockState; use valence::client::{Event, Hand}; use valence::config::{Config, ServerListPing}; use valence::dimension::{Dimension, DimensionId}; -use valence::entity::state::Pose; -use valence::entity::{EntityId, EntityKind, EntityState}; +use valence::entity::data::Pose; +use valence::entity::{EntityEnum, EntityId, EntityKind, Event as EntityEvent}; use valence::server::{Server, SharedServer, ShutdownResult}; use valence::text::{Color, TextFormat}; use valence::{async_trait, ident}; @@ -179,14 +179,10 @@ impl Config for Game { true; } } - Event::ArmSwing(hand) => { - if let EntityState::Player(e) = &mut player.state { - match hand { - Hand::Main => e.trigger_swing_main_arm(), - Hand::Off => e.trigger_swing_offhand(), - } - } - } + Event::ArmSwing(hand) => match hand { + Hand::Main => player.trigger_event(EntityEvent::SwingMainHand), + Hand::Off => player.trigger_event(EntityEvent::SwingOffHand), + }, _ => {} } } @@ -198,7 +194,7 @@ impl Config for Game { player.set_pitch(client.pitch()); player.set_on_ground(client.on_ground()); - if let EntityState::Player(player) = &mut player.state { + if let EntityEnum::Player(player) = player.view_mut() { if client.is_sneaking() { player.set_pose(Pose::Sneaking); } else { diff --git a/examples/raycast.rs b/examples/raycast.rs index 8e18dbb..5e9acb1 100644 --- a/examples/raycast.rs +++ b/examples/raycast.rs @@ -7,7 +7,7 @@ use valence::block::{BlockPos, BlockState}; use valence::client::GameMode; use valence::config::{Config, ServerListPing}; use valence::dimension::DimensionId; -use valence::entity::{EntityKind, EntityState}; +use valence::entity::{EntityEnum, EntityKind}; use valence::server::{Server, SharedServer, ShutdownResult}; use valence::spatial_index::RaycastHit; use valence::text::{Color, TextFormat}; @@ -166,11 +166,12 @@ impl Config for Game { }); for (_, e) in server.entities.iter_mut() { - if let EntityState::Sheep(sheep) = &mut e.state { - if e.data { - sheep.set_sheep_state(5); + let intersected = e.data; + if let EntityEnum::Sheep(sheep) = &mut e.view_mut() { + if intersected { + sheep.set_color(5); } else { - sheep.set_sheep_state(0); + sheep.set_color(0); } } e.data = false; diff --git a/examples/terrain.rs b/examples/terrain.rs index 94d01e6..c9f2287 100644 --- a/examples/terrain.rs +++ b/examples/terrain.rs @@ -117,10 +117,8 @@ impl Config for Game { ); client.send_message("Welcome to the terrain example!".italic()); - client.send_message( - "This demonstrates how to create infinite procedurally generated terrain." - .italic(), - ); + client + .send_message("Explore this infinite procedurally generated terrain.".italic()); } if client.is_disconnected() { diff --git a/extracted/entities.json b/extracted/entities.json new file mode 100644 index 0000000..f1dba60 --- /dev/null +++ b/extracted/entities.json @@ -0,0 +1,2436 @@ +{ + "TadpoleEntity": { + "parent": "FishEntity", + "type": "tadpole", + "translation_key": "entity.minecraft.tadpole", + "fields": [] + }, + "FishEntity": { + "parent": "WaterCreatureEntity", + "fields": [ + { + "name": "from_bucket", + "index": 16, + "type": "boolean", + "default_value": false, + "bits": [] + } + ] + }, + "WaterCreatureEntity": { + "parent": "PathAwareEntity", + "fields": [] + }, + "PathAwareEntity": { + "parent": "MobEntity", + "fields": [] + }, + "MobEntity": { + "parent": "LivingEntity", + "fields": [ + { + "name": "mob_flags", + "index": 15, + "type": "byte", + "default_value": 0, + "bits": [ + { + "name": "ai_disabled", + "index": 0 + }, + { + "name": "left_handed", + "index": 1 + }, + { + "name": "attacking", + "index": 2 + } + ] + } + ] + }, + "LivingEntity": { + "parent": "Entity", + "fields": [ + { + "name": "living_flags", + "index": 8, + "type": "byte", + "default_value": 0, + "bits": [ + { + "name": "using_item", + "index": 0 + }, + { + "name": "off_hand_active", + "index": 1 + }, + { + "name": "using_riptide", + "index": 2 + } + ] + }, + { + "name": "health", + "index": 9, + "type": "float", + "default_value": 6.0, + "bits": [] + }, + { + "name": "potion_swirls_color", + "index": 10, + "type": "integer", + "default_value": 0, + "bits": [] + }, + { + "name": "potion_swirls_ambient", + "index": 11, + "type": "boolean", + "default_value": false, + "bits": [] + }, + { + "name": "stuck_arrow_count", + "index": 12, + "type": "integer", + "default_value": 0, + "bits": [] + }, + { + "name": "stinger_count", + "index": 13, + "type": "integer", + "default_value": 0, + "bits": [] + }, + { + "name": "sleeping_position", + "index": 14, + "type": "optional_block_pos", + "default_value": null, + "bits": [] + } + ] + }, + "Entity": { + "fields": [ + { + "name": "flags", + "index": 0, + "type": "byte", + "default_value": 0, + "bits": [ + { + "name": "on_fire", + "index": 0 + }, + { + "name": "sneaking", + "index": 1 + }, + { + "name": "sprinting", + "index": 3 + }, + { + "name": "swimming", + "index": 4 + }, + { + "name": "invisible", + "index": 5 + }, + { + "name": "glowing", + "index": 6 + }, + { + "name": "fall_flying", + "index": 7 + } + ] + }, + { + "name": "air", + "index": 1, + "type": "integer", + "default_value": 300, + "bits": [] + }, + { + "name": "custom_name", + "index": 2, + "type": "optional_text_component", + "default_value": null, + "bits": [] + }, + { + "name": "name_visible", + "index": 3, + "type": "boolean", + "default_value": false, + "bits": [] + }, + { + "name": "silent", + "index": 4, + "type": "boolean", + "default_value": false, + "bits": [] + }, + { + "name": "no_gravity", + "index": 5, + "type": "boolean", + "default_value": false, + "bits": [] + }, + { + "name": "pose", + "index": 6, + "type": "entity_pose", + "default_value": "standing", + "bits": [] + }, + { + "name": "frozen_ticks", + "index": 7, + "type": "integer", + "default_value": 0, + "bits": [] + } + ] + }, + "FrogEntity": { + "parent": "AnimalEntity", + "type": "frog", + "translation_key": "entity.minecraft.frog", + "fields": [ + { + "name": "variant", + "index": 17, + "type": "frog_variant", + "default_value": "temperate", + "bits": [] + }, + { + "name": "target", + "index": 18, + "type": "optional_int", + "default_value": null, + "bits": [] + } + ] + }, + "AnimalEntity": { + "parent": "PassiveEntity", + "fields": [] + }, + "PassiveEntity": { + "parent": "PathAwareEntity", + "fields": [ + { + "name": "child", + "index": 16, + "type": "boolean", + "default_value": false, + "bits": [] + } + ] + }, + "FireballEntity": { + "parent": "AbstractFireballEntity", + "type": "fireball", + "translation_key": "entity.minecraft.fireball", + "fields": [] + }, + "AbstractFireballEntity": { + "parent": "ExplosiveProjectileEntity", + "fields": [ + { + "name": "item", + "index": 8, + "type": "item_stack", + "default_value": "1 air", + "bits": [] + } + ] + }, + "ExplosiveProjectileEntity": { + "parent": "ProjectileEntity", + "fields": [] + }, + "ProjectileEntity": { + "parent": "Entity", + "fields": [] + }, + "VillagerEntity": { + "parent": "MerchantEntity", + "type": "villager", + "translation_key": "entity.minecraft.villager", + "fields": [ + { + "name": "villager_data", + "index": 18, + "type": "villager_data", + "default_value": { + "type": "plains", + "profession": "none", + "level": 1 + }, + "bits": [] + } + ] + }, + "MerchantEntity": { + "parent": "PassiveEntity", + "fields": [ + { + "name": "head_rolling_time_left", + "index": 17, + "type": "integer", + "default_value": 0, + "bits": [] + } + ] + }, + "ZombieEntity": { + "parent": "HostileEntity", + "type": "zombie", + "translation_key": "entity.minecraft.zombie", + "fields": [ + { + "name": "baby", + "index": 16, + "type": "boolean", + "default_value": false, + "bits": [] + }, + { + "name": "zombie_type", + "index": 17, + "type": "integer", + "default_value": 0, + "bits": [] + }, + { + "name": "converting_in_water", + "index": 18, + "type": "boolean", + "default_value": false, + "bits": [] + } + ] + }, + "HostileEntity": { + "parent": "PathAwareEntity", + "fields": [] + }, + "GlowSquidEntity": { + "parent": "SquidEntity", + "type": "glow_squid", + "translation_key": "entity.minecraft.glow_squid", + "fields": [ + { + "name": "dark_ticks_remaining", + "index": 16, + "type": "integer", + "default_value": 0, + "bits": [] + } + ] + }, + "SquidEntity": { + "parent": "WaterCreatureEntity", + "type": "squid", + "translation_key": "entity.minecraft.squid", + "fields": [] + }, + "PaintingEntity": { + "parent": "AbstractDecorationEntity", + "type": "painting", + "translation_key": "entity.minecraft.painting", + "fields": [ + { + "name": "variant", + "index": 8, + "type": "painting_variant", + "default_value": "kebab", + "bits": [] + } + ] + }, + "AbstractDecorationEntity": { + "parent": "Entity", + "fields": [] + }, + "FishingBobberEntity": { + "parent": "ProjectileEntity", + "type": "fishing_bobber", + "translation_key": "entity.minecraft.fishing_bobber", + "fields": [ + { + "name": "hook_entity_id", + "index": 8, + "type": "integer", + "default_value": 0, + "bits": [] + }, + { + "name": "caught_fish", + "index": 9, + "type": "boolean", + "default_value": false, + "bits": [] + } + ] + }, + "WitherSkullEntity": { + "parent": "ExplosiveProjectileEntity", + "type": "wither_skull", + "translation_key": "entity.minecraft.wither_skull", + "fields": [ + { + "name": "charged", + "index": 8, + "type": "boolean", + "default_value": false, + "bits": [] + } + ] + }, + "PandaEntity": { + "parent": "AnimalEntity", + "type": "panda", + "translation_key": "entity.minecraft.panda", + "fields": [ + { + "name": "ask_for_bamboo_ticks", + "index": 17, + "type": "integer", + "default_value": 0, + "bits": [] + }, + { + "name": "sneeze_progress", + "index": 18, + "type": "integer", + "default_value": 0, + "bits": [] + }, + { + "name": "eating_ticks", + "index": 19, + "type": "integer", + "default_value": 0, + "bits": [] + }, + { + "name": "main_gene", + "index": 20, + "type": "byte", + "default_value": 0, + "bits": [] + }, + { + "name": "hidden_gene", + "index": 21, + "type": "byte", + "default_value": 0, + "bits": [] + }, + { + "name": "panda_flags", + "index": 22, + "type": "byte", + "default_value": 0, + "bits": [ + { + "name": "sneezing", + "index": 1 + }, + { + "name": "playing", + "index": 2 + }, + { + "name": "sitting", + "index": 3 + }, + { + "name": "lying_on_back", + "index": 4 + } + ] + } + ] + }, + "MuleEntity": { + "parent": "AbstractDonkeyEntity", + "type": "mule", + "translation_key": "entity.minecraft.mule", + "fields": [] + }, + "AbstractDonkeyEntity": { + "parent": "AbstractHorseEntity", + "fields": [ + { + "name": "chest", + "index": 19, + "type": "boolean", + "default_value": false, + "bits": [] + } + ] + }, + "AbstractHorseEntity": { + "parent": "AnimalEntity", + "fields": [ + { + "name": "horse_flags", + "index": 17, + "type": "byte", + "default_value": 0, + "bits": [ + { + "name": "tamed", + "index": 1 + }, + { + "name": "saddled", + "index": 2 + }, + { + "name": "bred", + "index": 3 + }, + { + "name": "eating_grass", + "index": 4 + }, + { + "name": "angry", + "index": 5 + }, + { + "name": "eating", + "index": 6 + } + ] + }, + { + "name": "owner_uuid", + "index": 18, + "type": "optional_uuid", + "default_value": null, + "bits": [] + } + ] + }, + "FireworkRocketEntity": { + "parent": "ProjectileEntity", + "type": "firework_rocket", + "translation_key": "entity.minecraft.firework_rocket", + "fields": [ + { + "name": "item", + "index": 8, + "type": "item_stack", + "default_value": "1 air", + "bits": [] + }, + { + "name": "shooter_entity_id", + "index": 9, + "type": "optional_int", + "default_value": null, + "bits": [] + }, + { + "name": "shot_at_angle", + "index": 10, + "type": "boolean", + "default_value": false, + "bits": [] + } + ] + }, + "RavagerEntity": { + "parent": "RaiderEntity", + "type": "ravager", + "translation_key": "entity.minecraft.ravager", + "fields": [] + }, + "RaiderEntity": { + "parent": "PatrolEntity", + "fields": [ + { + "name": "celebrating", + "index": 16, + "type": "boolean", + "default_value": false, + "bits": [] + } + ] + }, + "PatrolEntity": { + "parent": "HostileEntity", + "fields": [] + }, + "EndermanEntity": { + "parent": "HostileEntity", + "type": "enderman", + "translation_key": "entity.minecraft.enderman", + "fields": [ + { + "name": "carried_block", + "index": 16, + "type": "optional_block_state", + "default_value": null, + "bits": [] + }, + { + "name": "angry", + "index": 17, + "type": "boolean", + "default_value": false, + "bits": [] + }, + { + "name": "provoked", + "index": 18, + "type": "boolean", + "default_value": false, + "bits": [] + } + ] + }, + "PillagerEntity": { + "parent": "IllagerEntity", + "type": "pillager", + "translation_key": "entity.minecraft.pillager", + "fields": [ + { + "name": "charging", + "index": 17, + "type": "boolean", + "default_value": false, + "bits": [] + } + ] + }, + "IllagerEntity": { + "parent": "RaiderEntity", + "fields": [] + }, + "GhastEntity": { + "parent": "FlyingEntity", + "type": "ghast", + "translation_key": "entity.minecraft.ghast", + "fields": [ + { + "name": "shooting", + "index": 16, + "type": "boolean", + "default_value": false, + "bits": [] + } + ] + }, + "FlyingEntity": { + "parent": "MobEntity", + "fields": [] + }, + "GiantEntity": { + "parent": "HostileEntity", + "type": "giant", + "translation_key": "entity.minecraft.giant", + "fields": [] + }, + "SpawnerMinecartEntity": { + "parent": "AbstractMinecartEntity", + "type": "spawner_minecart", + "translation_key": "entity.minecraft.spawner_minecart", + "fields": [] + }, + "AbstractMinecartEntity": { + "parent": "Entity", + "fields": [ + { + "name": "damage_wobble_ticks", + "index": 8, + "type": "integer", + "default_value": 0, + "bits": [] + }, + { + "name": "damage_wobble_side", + "index": 9, + "type": "integer", + "default_value": 1, + "bits": [] + }, + { + "name": "damage_wobble_strength", + "index": 10, + "type": "float", + "default_value": 0.0, + "bits": [] + }, + { + "name": "custom_block_id", + "index": 11, + "type": "integer", + "default_value": 0, + "bits": [] + }, + { + "name": "custom_block_offset", + "index": 12, + "type": "integer", + "default_value": 6, + "bits": [] + }, + { + "name": "custom_block_present", + "index": 13, + "type": "boolean", + "default_value": false, + "bits": [] + } + ] + }, + "StrayEntity": { + "parent": "AbstractSkeletonEntity", + "type": "stray", + "translation_key": "entity.minecraft.stray", + "fields": [] + }, + "AbstractSkeletonEntity": { + "parent": "HostileEntity", + "fields": [] + }, + "LightningEntity": { + "parent": "Entity", + "type": "lightning_bolt", + "translation_key": "entity.minecraft.lightning_bolt", + "fields": [] + }, + "EyeOfEnderEntity": { + "parent": "Entity", + "type": "eye_of_ender", + "translation_key": "entity.minecraft.eye_of_ender", + "fields": [ + { + "name": "item", + "index": 8, + "type": "item_stack", + "default_value": "1 air", + "bits": [] + } + ] + }, + "HoglinEntity": { + "parent": "AnimalEntity", + "type": "hoglin", + "translation_key": "entity.minecraft.hoglin", + "fields": [ + { + "name": "baby", + "index": 17, + "type": "boolean", + "default_value": false, + "bits": [] + } + ] + }, + "CatEntity": { + "parent": "TameableEntity", + "type": "cat", + "translation_key": "entity.minecraft.cat", + "fields": [ + { + "name": "cat_variant", + "index": 19, + "type": "cat_variant", + "default_value": "black", + "bits": [] + }, + { + "name": "in_sleeping_pose", + "index": 20, + "type": "boolean", + "default_value": false, + "bits": [] + }, + { + "name": "head_down", + "index": 21, + "type": "boolean", + "default_value": false, + "bits": [] + }, + { + "name": "collar_color", + "index": 22, + "type": "integer", + "default_value": 14, + "bits": [] + } + ] + }, + "TameableEntity": { + "parent": "AnimalEntity", + "fields": [ + { + "name": "tameable_flags", + "index": 17, + "type": "byte", + "default_value": 0, + "bits": [ + { + "name": "sitting_pose", + "index": 0 + }, + { + "name": "tamed", + "index": 2 + } + ] + }, + { + "name": "owner_uuid", + "index": 18, + "type": "optional_uuid", + "default_value": null, + "bits": [] + } + ] + }, + "CaveSpiderEntity": { + "parent": "SpiderEntity", + "type": "cave_spider", + "translation_key": "entity.minecraft.cave_spider", + "fields": [] + }, + "SpiderEntity": { + "parent": "HostileEntity", + "type": "spider", + "translation_key": "entity.minecraft.spider", + "fields": [ + { + "name": "spider_flags", + "index": 16, + "type": "byte", + "default_value": 0, + "bits": [ + { + "name": "climbing_wall", + "index": 0 + } + ] + } + ] + }, + "PolarBearEntity": { + "parent": "AnimalEntity", + "type": "polar_bear", + "translation_key": "entity.minecraft.polar_bear", + "fields": [ + { + "name": "warning", + "index": 17, + "type": "boolean", + "default_value": false, + "bits": [] + } + ] + }, + "MooshroomEntity": { + "parent": "CowEntity", + "type": "mooshroom", + "translation_key": "entity.minecraft.mooshroom", + "fields": [ + { + "name": "type", + "index": 17, + "type": "string", + "default_value": "red", + "bits": [] + } + ] + }, + "CowEntity": { + "parent": "AnimalEntity", + "type": "cow", + "translation_key": "entity.minecraft.cow", + "fields": [] + }, + "ExperienceBottleEntity": { + "parent": "ThrownItemEntity", + "type": "experience_bottle", + "translation_key": "entity.minecraft.experience_bottle", + "fields": [] + }, + "ThrownItemEntity": { + "parent": "ThrownEntity", + "fields": [ + { + "name": "item", + "index": 8, + "type": "item_stack", + "default_value": "1 air", + "bits": [] + } + ] + }, + "ThrownEntity": { + "parent": "ProjectileEntity", + "fields": [] + }, + "PufferfishEntity": { + "parent": "FishEntity", + "type": "pufferfish", + "translation_key": "entity.minecraft.pufferfish", + "fields": [ + { + "name": "puff_state", + "index": 17, + "type": "integer", + "default_value": 0, + "bits": [] + } + ] + }, + "PotionEntity": { + "parent": "ThrownItemEntity", + "type": "potion", + "translation_key": "entity.minecraft.potion", + "fields": [] + }, + "EvokerEntity": { + "parent": "SpellcastingIllagerEntity", + "type": "evoker", + "translation_key": "entity.minecraft.evoker", + "fields": [] + }, + "SpellcastingIllagerEntity": { + "parent": "IllagerEntity", + "fields": [ + { + "name": "spell", + "index": 17, + "type": "byte", + "default_value": 0, + "bits": [] + } + ] + }, + "MinecartEntity": { + "parent": "AbstractMinecartEntity", + "type": "minecart", + "translation_key": "entity.minecraft.minecart", + "fields": [] + }, + "TurtleEntity": { + "parent": "AnimalEntity", + "type": "turtle", + "translation_key": "entity.minecraft.turtle", + "fields": [ + { + "name": "home_pos", + "index": 17, + "type": "block_pos", + "default_value": { + "x": 0, + "y": 0, + "z": 0 + }, + "bits": [] + }, + { + "name": "has_egg", + "index": 18, + "type": "boolean", + "default_value": false, + "bits": [] + }, + { + "name": "digging_sand", + "index": 19, + "type": "boolean", + "default_value": false, + "bits": [] + }, + { + "name": "travel_pos", + "index": 20, + "type": "block_pos", + "default_value": { + "x": 0, + "y": 0, + "z": 0 + }, + "bits": [] + }, + { + "name": "land_bound", + "index": 21, + "type": "boolean", + "default_value": false, + "bits": [] + }, + { + "name": "actively_traveling", + "index": 22, + "type": "boolean", + "default_value": false, + "bits": [] + } + ] + }, + "BoatEntity": { + "parent": "Entity", + "type": "boat", + "translation_key": "entity.minecraft.boat", + "fields": [ + { + "name": "damage_wobble_ticks", + "index": 8, + "type": "integer", + "default_value": 0, + "bits": [] + }, + { + "name": "damage_wobble_side", + "index": 9, + "type": "integer", + "default_value": 1, + "bits": [] + }, + { + "name": "damage_wobble_strength", + "index": 10, + "type": "float", + "default_value": 0.0, + "bits": [] + }, + { + "name": "boat_type", + "index": 11, + "type": "integer", + "default_value": 0, + "bits": [] + }, + { + "name": "left_paddle_moving", + "index": 12, + "type": "boolean", + "default_value": false, + "bits": [] + }, + { + "name": "right_paddle_moving", + "index": 13, + "type": "boolean", + "default_value": false, + "bits": [] + }, + { + "name": "bubble_wobble_ticks", + "index": 14, + "type": "integer", + "default_value": 0, + "bits": [] + } + ] + }, + "FurnaceMinecartEntity": { + "parent": "AbstractMinecartEntity", + "type": "furnace_minecart", + "translation_key": "entity.minecraft.furnace_minecart", + "fields": [ + { + "name": "lit", + "index": 14, + "type": "boolean", + "default_value": false, + "bits": [] + } + ] + }, + "ZombifiedPiglinEntity": { + "parent": "ZombieEntity", + "type": "zombified_piglin", + "translation_key": "entity.minecraft.zombified_piglin", + "fields": [] + }, + "OcelotEntity": { + "parent": "AnimalEntity", + "type": "ocelot", + "translation_key": "entity.minecraft.ocelot", + "fields": [ + { + "name": "trusting", + "index": 17, + "type": "boolean", + "default_value": false, + "bits": [] + } + ] + }, + "EndermiteEntity": { + "parent": "HostileEntity", + "type": "endermite", + "translation_key": "entity.minecraft.endermite", + "fields": [] + }, + "ShulkerEntity": { + "parent": "GolemEntity", + "type": "shulker", + "translation_key": "entity.minecraft.shulker", + "fields": [ + { + "name": "attached_face", + "index": 16, + "type": "facing", + "default_value": "down", + "bits": [] + }, + { + "name": "peek_amount", + "index": 17, + "type": "byte", + "default_value": 0, + "bits": [] + }, + { + "name": "color", + "index": 18, + "type": "byte", + "default_value": 16, + "bits": [] + } + ] + }, + "GolemEntity": { + "parent": "PathAwareEntity", + "fields": [] + }, + "LeashKnotEntity": { + "parent": "AbstractDecorationEntity", + "type": "leash_knot", + "translation_key": "entity.minecraft.leash_knot", + "fields": [] + }, + "SilverfishEntity": { + "parent": "HostileEntity", + "type": "silverfish", + "translation_key": "entity.minecraft.silverfish", + "fields": [] + }, + "HuskEntity": { + "parent": "ZombieEntity", + "type": "husk", + "translation_key": "entity.minecraft.husk", + "fields": [] + }, + "TntMinecartEntity": { + "parent": "AbstractMinecartEntity", + "type": "tnt_minecart", + "translation_key": "entity.minecraft.tnt_minecart", + "fields": [] + }, + "TropicalFishEntity": { + "parent": "SchoolingFishEntity", + "type": "tropical_fish", + "translation_key": "entity.minecraft.tropical_fish", + "fields": [ + { + "name": "variant", + "index": 17, + "type": "integer", + "default_value": 0, + "bits": [] + } + ] + }, + "SchoolingFishEntity": { + "parent": "FishEntity", + "fields": [] + }, + "IllusionerEntity": { + "parent": "SpellcastingIllagerEntity", + "type": "illusioner", + "translation_key": "entity.minecraft.illusioner", + "fields": [] + }, + "LlamaSpitEntity": { + "parent": "ProjectileEntity", + "type": "llama_spit", + "translation_key": "entity.minecraft.llama_spit", + "fields": [] + }, + "AllayEntity": { + "parent": "PathAwareEntity", + "type": "allay", + "translation_key": "entity.minecraft.allay", + "fields": [] + }, + "ArmorStandEntity": { + "parent": "LivingEntity", + "type": "armor_stand", + "translation_key": "entity.minecraft.armor_stand", + "fields": [ + { + "name": "armor_stand_flags", + "index": 15, + "type": "byte", + "default_value": 0, + "bits": [ + { + "name": "small", + "index": 0 + }, + { + "name": "show_arms", + "index": 1 + }, + { + "name": "hide_base_plate", + "index": 2 + }, + { + "name": "marker", + "index": 3 + } + ] + }, + { + "name": "tracker_head_rotation", + "index": 16, + "type": "rotation", + "default_value": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + }, + "bits": [] + }, + { + "name": "tracker_body_rotation", + "index": 17, + "type": "rotation", + "default_value": { + "pitch": 0.0, + "yaw": 0.0, + "roll": 0.0 + }, + "bits": [] + }, + { + "name": "tracker_left_arm_rotation", + "index": 18, + "type": "rotation", + "default_value": { + "pitch": -10.0, + "yaw": 0.0, + "roll": -10.0 + }, + "bits": [] + }, + { + "name": "tracker_right_arm_rotation", + "index": 19, + "type": "rotation", + "default_value": { + "pitch": -15.0, + "yaw": 0.0, + "roll": 10.0 + }, + "bits": [] + }, + { + "name": "tracker_left_leg_rotation", + "index": 20, + "type": "rotation", + "default_value": { + "pitch": -1.0, + "yaw": 0.0, + "roll": -1.0 + }, + "bits": [] + }, + { + "name": "tracker_right_leg_rotation", + "index": 21, + "type": "rotation", + "default_value": { + "pitch": 1.0, + "yaw": 0.0, + "roll": 1.0 + }, + "bits": [] + } + ] + }, + "ChestMinecartEntity": { + "parent": "StorageMinecartEntity", + "type": "chest_minecart", + "translation_key": "entity.minecraft.chest_minecart", + "fields": [] + }, + "StorageMinecartEntity": { + "parent": "AbstractMinecartEntity", + "fields": [] + }, + "CreeperEntity": { + "parent": "HostileEntity", + "type": "creeper", + "translation_key": "entity.minecraft.creeper", + "fields": [ + { + "name": "fuse_speed", + "index": 16, + "type": "integer", + "default_value": -1, + "bits": [] + }, + { + "name": "charged", + "index": 17, + "type": "boolean", + "default_value": false, + "bits": [] + }, + { + "name": "ignited", + "index": 18, + "type": "boolean", + "default_value": false, + "bits": [] + } + ] + }, + "MagmaCubeEntity": { + "parent": "SlimeEntity", + "type": "magma_cube", + "translation_key": "entity.minecraft.magma_cube", + "fields": [] + }, + "SlimeEntity": { + "parent": "MobEntity", + "type": "slime", + "translation_key": "entity.minecraft.slime", + "fields": [ + { + "name": "slime_size", + "index": 16, + "type": "integer", + "default_value": 1, + "bits": [] + } + ] + }, + "WitchEntity": { + "parent": "RaiderEntity", + "type": "witch", + "translation_key": "entity.minecraft.witch", + "fields": [ + { + "name": "drinking", + "index": 17, + "type": "boolean", + "default_value": false, + "bits": [] + } + ] + }, + "SkeletonEntity": { + "parent": "AbstractSkeletonEntity", + "type": "skeleton", + "translation_key": "entity.minecraft.skeleton", + "fields": [ + { + "name": "converting", + "index": 16, + "type": "boolean", + "default_value": false, + "bits": [] + } + ] + }, + "EvokerFangsEntity": { + "parent": "Entity", + "type": "evoker_fangs", + "translation_key": "entity.minecraft.evoker_fangs", + "fields": [] + }, + "DolphinEntity": { + "parent": "WaterCreatureEntity", + "type": "dolphin", + "translation_key": "entity.minecraft.dolphin", + "fields": [ + { + "name": "treasure_pos", + "index": 16, + "type": "block_pos", + "default_value": { + "x": 0, + "y": 0, + "z": 0 + }, + "bits": [] + }, + { + "name": "has_fish", + "index": 17, + "type": "boolean", + "default_value": false, + "bits": [] + }, + { + "name": "moistness", + "index": 18, + "type": "integer", + "default_value": 2400, + "bits": [] + } + ] + }, + "EndCrystalEntity": { + "parent": "Entity", + "type": "end_crystal", + "translation_key": "entity.minecraft.end_crystal", + "fields": [ + { + "name": "beam_target", + "index": 8, + "type": "optional_block_pos", + "default_value": null, + "bits": [] + }, + { + "name": "show_bottom", + "index": 9, + "type": "boolean", + "default_value": true, + "bits": [] + } + ] + }, + "DragonFireballEntity": { + "parent": "ExplosiveProjectileEntity", + "type": "dragon_fireball", + "translation_key": "entity.minecraft.dragon_fireball", + "fields": [] + }, + "ElderGuardianEntity": { + "parent": "GuardianEntity", + "type": "elder_guardian", + "translation_key": "entity.minecraft.elder_guardian", + "fields": [] + }, + "GuardianEntity": { + "parent": "HostileEntity", + "type": "guardian", + "translation_key": "entity.minecraft.guardian", + "fields": [ + { + "name": "spikes_retracted", + "index": 16, + "type": "boolean", + "default_value": false, + "bits": [] + }, + { + "name": "beam_target_id", + "index": 17, + "type": "integer", + "default_value": 0, + "bits": [] + } + ] + }, + "SnowballEntity": { + "parent": "ThrownItemEntity", + "type": "snowball", + "translation_key": "entity.minecraft.snowball", + "fields": [] + }, + "DrownedEntity": { + "parent": "ZombieEntity", + "type": "drowned", + "translation_key": "entity.minecraft.drowned", + "fields": [] + }, + "ShulkerBulletEntity": { + "parent": "ProjectileEntity", + "type": "shulker_bullet", + "translation_key": "entity.minecraft.shulker_bullet", + "fields": [] + }, + "ChickenEntity": { + "parent": "AnimalEntity", + "type": "chicken", + "translation_key": "entity.minecraft.chicken", + "fields": [] + }, + "GlowItemFrameEntity": { + "parent": "ItemFrameEntity", + "type": "glow_item_frame", + "translation_key": "entity.minecraft.glow_item_frame", + "fields": [] + }, + "ItemFrameEntity": { + "parent": "AbstractDecorationEntity", + "type": "item_frame", + "translation_key": "entity.minecraft.item_frame", + "fields": [ + { + "name": "item_stack", + "index": 8, + "type": "item_stack", + "default_value": "1 air", + "bits": [] + }, + { + "name": "rotation", + "index": 9, + "type": "integer", + "default_value": 0, + "bits": [] + } + ] + }, + "LlamaEntity": { + "parent": "AbstractDonkeyEntity", + "type": "llama", + "translation_key": "entity.minecraft.llama", + "fields": [ + { + "name": "strength", + "index": 20, + "type": "integer", + "default_value": 0, + "bits": [] + }, + { + "name": "carpet_color", + "index": 21, + "type": "integer", + "default_value": -1, + "bits": [] + }, + { + "name": "variant", + "index": 22, + "type": "integer", + "default_value": 0, + "bits": [] + } + ] + }, + "ZoglinEntity": { + "parent": "HostileEntity", + "type": "zoglin", + "translation_key": "entity.minecraft.zoglin", + "fields": [ + { + "name": "baby", + "index": 16, + "type": "boolean", + "default_value": false, + "bits": [] + } + ] + }, + "SnowGolemEntity": { + "parent": "GolemEntity", + "type": "snow_golem", + "translation_key": "entity.minecraft.snow_golem", + "fields": [ + { + "name": "snow_golem_flags", + "index": 16, + "type": "byte", + "default_value": 16, + "bits": [ + { + "name": "has_pumpkin", + "index": 4 + } + ] + } + ] + }, + "TraderLlamaEntity": { + "parent": "LlamaEntity", + "type": "trader_llama", + "translation_key": "entity.minecraft.trader_llama", + "fields": [] + }, + "FallingBlockEntity": { + "parent": "Entity", + "type": "falling_block", + "translation_key": "entity.minecraft.falling_block", + "fields": [ + { + "name": "block_pos", + "index": 8, + "type": "block_pos", + "default_value": { + "x": 0, + "y": 0, + "z": 0 + }, + "bits": [] + } + ] + }, + "SmallFireballEntity": { + "parent": "AbstractFireballEntity", + "type": "small_fireball", + "translation_key": "entity.minecraft.small_fireball", + "fields": [] + }, + "AxolotlEntity": { + "parent": "AnimalEntity", + "type": "axolotl", + "translation_key": "entity.minecraft.axolotl", + "fields": [ + { + "name": "variant", + "index": 17, + "type": "integer", + "default_value": 0, + "bits": [] + }, + { + "name": "playing_dead", + "index": 18, + "type": "boolean", + "default_value": false, + "bits": [] + }, + { + "name": "from_bucket", + "index": 19, + "type": "boolean", + "default_value": false, + "bits": [] + } + ] + }, + "TntEntity": { + "parent": "Entity", + "type": "tnt", + "translation_key": "entity.minecraft.tnt", + "fields": [ + { + "name": "fuse", + "index": 8, + "type": "integer", + "default_value": 80, + "bits": [] + } + ] + }, + "DonkeyEntity": { + "parent": "AbstractDonkeyEntity", + "type": "donkey", + "translation_key": "entity.minecraft.donkey", + "fields": [] + }, + "WardenEntity": { + "parent": "HostileEntity", + "type": "warden", + "translation_key": "entity.minecraft.warden", + "fields": [ + { + "name": "anger", + "index": 16, + "type": "integer", + "default_value": 0, + "bits": [] + } + ] + }, + "PiglinEntity": { + "parent": "AbstractPiglinEntity", + "type": "piglin", + "translation_key": "entity.minecraft.piglin", + "fields": [ + { + "name": "baby", + "index": 17, + "type": "boolean", + "default_value": false, + "bits": [] + }, + { + "name": "charging", + "index": 18, + "type": "boolean", + "default_value": false, + "bits": [] + }, + { + "name": "dancing", + "index": 19, + "type": "boolean", + "default_value": false, + "bits": [] + } + ] + }, + "AbstractPiglinEntity": { + "parent": "HostileEntity", + "fields": [ + { + "name": "immune_to_zombification", + "index": 16, + "type": "boolean", + "default_value": false, + "bits": [] + } + ] + }, + "SpectralArrowEntity": { + "parent": "PersistentProjectileEntity", + "type": "spectral_arrow", + "translation_key": "entity.minecraft.spectral_arrow", + "fields": [] + }, + "PersistentProjectileEntity": { + "parent": "ProjectileEntity", + "fields": [ + { + "name": "projectile_flags", + "index": 8, + "type": "byte", + "default_value": 0, + "bits": [ + { + "name": "critical", + "index": 0 + }, + { + "name": "no_clip", + "index": 1 + } + ] + }, + { + "name": "pierce_level", + "index": 9, + "type": "byte", + "default_value": 0, + "bits": [] + } + ] + }, + "WanderingTraderEntity": { + "parent": "MerchantEntity", + "type": "wandering_trader", + "translation_key": "entity.minecraft.wandering_trader", + "fields": [] + }, + "RabbitEntity": { + "parent": "AnimalEntity", + "type": "rabbit", + "translation_key": "entity.minecraft.rabbit", + "fields": [ + { + "name": "rabbit_type", + "index": 17, + "type": "integer", + "default_value": 0, + "bits": [] + } + ] + }, + "BatEntity": { + "parent": "AmbientEntity", + "type": "bat", + "translation_key": "entity.minecraft.bat", + "fields": [ + { + "name": "bat_flags", + "index": 16, + "type": "byte", + "default_value": 1, + "bits": [ + { + "name": "hanging", + "index": 0 + } + ] + } + ] + }, + "AmbientEntity": { + "parent": "MobEntity", + "fields": [] + }, + "ItemEntity": { + "parent": "Entity", + "type": "item", + "translation_key": "entity.minecraft.item", + "fields": [ + { + "name": "stack", + "index": 8, + "type": "item_stack", + "default_value": "1 air", + "bits": [] + } + ] + }, + "GoatEntity": { + "parent": "AnimalEntity", + "type": "goat", + "translation_key": "entity.minecraft.goat", + "fields": [ + { + "name": "screaming", + "index": 17, + "type": "boolean", + "default_value": false, + "bits": [] + }, + { + "name": "left_horn", + "index": 18, + "type": "boolean", + "default_value": true, + "bits": [] + }, + { + "name": "right_horn", + "index": 19, + "type": "boolean", + "default_value": true, + "bits": [] + } + ] + }, + "SalmonEntity": { + "parent": "SchoolingFishEntity", + "type": "salmon", + "translation_key": "entity.minecraft.salmon", + "fields": [] + }, + "ExperienceOrbEntity": { + "parent": "Entity", + "type": "experience_orb", + "translation_key": "entity.minecraft.experience_orb", + "fields": [] + }, + "SheepEntity": { + "parent": "AnimalEntity", + "type": "sheep", + "translation_key": "entity.minecraft.sheep", + "fields": [ + { + "name": "color", + "index": 17, + "type": "byte", + "default_value": 0, + "bits": [] + } + ] + }, + "VexEntity": { + "parent": "HostileEntity", + "type": "vex", + "translation_key": "entity.minecraft.vex", + "fields": [ + { + "name": "vex_flags", + "index": 16, + "type": "byte", + "default_value": 0, + "bits": [ + { + "name": "charging", + "index": 0 + } + ] + } + ] + }, + "BeeEntity": { + "parent": "AnimalEntity", + "type": "bee", + "translation_key": "entity.minecraft.bee", + "fields": [ + { + "name": "bee_flags", + "index": 17, + "type": "byte", + "default_value": 0, + "bits": [ + { + "name": "near_target", + "index": 1 + }, + { + "name": "has_stung", + "index": 2 + }, + { + "name": "has_nectar", + "index": 3 + } + ] + }, + { + "name": "anger", + "index": 18, + "type": "integer", + "default_value": 0, + "bits": [] + } + ] + }, + "HorseEntity": { + "parent": "AbstractHorseEntity", + "type": "horse", + "translation_key": "entity.minecraft.horse", + "fields": [ + { + "name": "variant", + "index": 19, + "type": "integer", + "default_value": 0, + "bits": [] + } + ] + }, + "CommandBlockMinecartEntity": { + "parent": "AbstractMinecartEntity", + "type": "command_block_minecart", + "translation_key": "entity.minecraft.command_block_minecart", + "fields": [ + { + "name": "command", + "index": 14, + "type": "string", + "default_value": "", + "bits": [] + }, + { + "name": "last_output", + "index": 15, + "type": "text_component", + "default_value": "", + "bits": [] + } + ] + }, + "EggEntity": { + "parent": "ThrownItemEntity", + "type": "egg", + "translation_key": "entity.minecraft.egg", + "fields": [] + }, + "EnderDragonEntity": { + "parent": "MobEntity", + "type": "ender_dragon", + "translation_key": "entity.minecraft.ender_dragon", + "fields": [ + { + "name": "phase_type", + "index": 16, + "type": "integer", + "default_value": 10, + "bits": [] + } + ] + }, + "ParrotEntity": { + "parent": "TameableShoulderEntity", + "type": "parrot", + "translation_key": "entity.minecraft.parrot", + "fields": [ + { + "name": "variant", + "index": 19, + "type": "integer", + "default_value": 0, + "bits": [] + } + ] + }, + "TameableShoulderEntity": { + "parent": "TameableEntity", + "fields": [] + }, + "PiglinBruteEntity": { + "parent": "AbstractPiglinEntity", + "type": "piglin_brute", + "translation_key": "entity.minecraft.piglin_brute", + "fields": [] + }, + "EnderPearlEntity": { + "parent": "ThrownItemEntity", + "type": "ender_pearl", + "translation_key": "entity.minecraft.ender_pearl", + "fields": [] + }, + "TridentEntity": { + "parent": "PersistentProjectileEntity", + "type": "trident", + "translation_key": "entity.minecraft.trident", + "fields": [ + { + "name": "loyalty", + "index": 10, + "type": "byte", + "default_value": 0, + "bits": [] + }, + { + "name": "enchanted", + "index": 11, + "type": "boolean", + "default_value": false, + "bits": [] + } + ] + }, + "StriderEntity": { + "parent": "AnimalEntity", + "type": "strider", + "translation_key": "entity.minecraft.strider", + "fields": [ + { + "name": "boost_time", + "index": 17, + "type": "integer", + "default_value": 0, + "bits": [] + }, + { + "name": "cold", + "index": 18, + "type": "boolean", + "default_value": false, + "bits": [] + }, + { + "name": "saddled", + "index": 19, + "type": "boolean", + "default_value": false, + "bits": [] + } + ] + }, + "WitherEntity": { + "parent": "HostileEntity", + "type": "wither", + "translation_key": "entity.minecraft.wither", + "fields": [ + { + "name": "tracked_entity_id_1", + "index": 16, + "type": "integer", + "default_value": 0, + "bits": [] + }, + { + "name": "tracked_entity_id_2", + "index": 17, + "type": "integer", + "default_value": 0, + "bits": [] + }, + { + "name": "tracked_entity_id_3", + "index": 18, + "type": "integer", + "default_value": 0, + "bits": [] + }, + { + "name": "invul_timer", + "index": 19, + "type": "integer", + "default_value": 0, + "bits": [] + } + ] + }, + "ArrowEntity": { + "parent": "PersistentProjectileEntity", + "type": "arrow", + "translation_key": "entity.minecraft.arrow", + "fields": [ + { + "name": "color", + "index": 10, + "type": "integer", + "default_value": -1, + "bits": [] + } + ] + }, + "MarkerEntity": { + "parent": "Entity", + "type": "marker", + "translation_key": "entity.minecraft.marker", + "fields": [] + }, + "WolfEntity": { + "parent": "TameableEntity", + "type": "wolf", + "translation_key": "entity.minecraft.wolf", + "fields": [ + { + "name": "begging", + "index": 19, + "type": "boolean", + "default_value": false, + "bits": [] + }, + { + "name": "collar_color", + "index": 20, + "type": "integer", + "default_value": 14, + "bits": [] + }, + { + "name": "anger_time", + "index": 21, + "type": "integer", + "default_value": 0, + "bits": [] + } + ] + }, + "FoxEntity": { + "parent": "AnimalEntity", + "type": "fox", + "translation_key": "entity.minecraft.fox", + "fields": [ + { + "name": "type", + "index": 17, + "type": "integer", + "default_value": 0, + "bits": [] + }, + { + "name": "fox_flags", + "index": 18, + "type": "byte", + "default_value": 0, + "bits": [ + { + "name": "sitting", + "index": 0 + }, + { + "name": "crouching", + "index": 2 + }, + { + "name": "rolling_head", + "index": 3 + }, + { + "name": "chasing", + "index": 4 + }, + { + "name": "sleeping", + "index": 5 + }, + { + "name": "walking", + "index": 6 + }, + { + "name": "aggressive", + "index": 7 + } + ] + }, + { + "name": "owner", + "index": 19, + "type": "optional_uuid", + "default_value": null, + "bits": [] + }, + { + "name": "other_trusted", + "index": 20, + "type": "optional_uuid", + "default_value": null, + "bits": [] + } + ] + }, + "PlayerEntity": { + "parent": "LivingEntity", + "type": "player", + "translation_key": "entity.minecraft.player", + "fields": [ + { + "name": "absorption_amount", + "index": 15, + "type": "float", + "default_value": 0.0, + "bits": [] + }, + { + "name": "score", + "index": 16, + "type": "integer", + "default_value": 0, + "bits": [] + }, + { + "name": "player_model_parts", + "index": 17, + "type": "byte", + "default_value": 0, + "bits": [ + { + "name": "cape", + "index": 0 + }, + { + "name": "jacket", + "index": 1 + }, + { + "name": "left_sleeve", + "index": 2 + }, + { + "name": "right_sleeve", + "index": 3 + }, + { + "name": "left_pants_leg", + "index": 4 + }, + { + "name": "right_pants_leg", + "index": 5 + }, + { + "name": "hat", + "index": 6 + } + ] + }, + { + "name": "main_arm", + "index": 18, + "type": "byte", + "default_value": 1, + "bits": [] + }, + { + "name": "left_shoulder_entity", + "index": 19, + "type": "nbt_compound", + "default_value": "{}", + "bits": [] + }, + { + "name": "right_shoulder_entity", + "index": 20, + "type": "nbt_compound", + "default_value": "{}", + "bits": [] + } + ] + }, + "PigEntity": { + "parent": "AnimalEntity", + "type": "pig", + "translation_key": "entity.minecraft.pig", + "fields": [ + { + "name": "saddled", + "index": 17, + "type": "boolean", + "default_value": false, + "bits": [] + }, + { + "name": "boost_time", + "index": 18, + "type": "integer", + "default_value": 0, + "bits": [] + } + ] + }, + "WitherSkeletonEntity": { + "parent": "AbstractSkeletonEntity", + "type": "wither_skeleton", + "translation_key": "entity.minecraft.wither_skeleton", + "fields": [] + }, + "ChestBoatEntity": { + "parent": "BoatEntity", + "type": "chest_boat", + "translation_key": "entity.minecraft.chest_boat", + "fields": [] + }, + "BlazeEntity": { + "parent": "HostileEntity", + "type": "blaze", + "translation_key": "entity.minecraft.blaze", + "fields": [ + { + "name": "blaze_flags", + "index": 16, + "type": "byte", + "default_value": 0, + "bits": [ + { + "name": "fire_active", + "index": 0 + } + ] + } + ] + }, + "PhantomEntity": { + "parent": "FlyingEntity", + "type": "phantom", + "translation_key": "entity.minecraft.phantom", + "fields": [ + { + "name": "size", + "index": 16, + "type": "integer", + "default_value": 0, + "bits": [] + } + ] + }, + "SkeletonHorseEntity": { + "parent": "AbstractHorseEntity", + "type": "skeleton_horse", + "translation_key": "entity.minecraft.skeleton_horse", + "fields": [] + }, + "HopperMinecartEntity": { + "parent": "StorageMinecartEntity", + "type": "hopper_minecart", + "translation_key": "entity.minecraft.hopper_minecart", + "fields": [] + }, + "CodEntity": { + "parent": "SchoolingFishEntity", + "type": "cod", + "translation_key": "entity.minecraft.cod", + "fields": [] + }, + "ZombieHorseEntity": { + "parent": "AbstractHorseEntity", + "type": "zombie_horse", + "translation_key": "entity.minecraft.zombie_horse", + "fields": [] + }, + "IronGolemEntity": { + "parent": "GolemEntity", + "type": "iron_golem", + "translation_key": "entity.minecraft.iron_golem", + "fields": [ + { + "name": "iron_golem_flags", + "index": 16, + "type": "byte", + "default_value": 0, + "bits": [ + { + "name": "player_created", + "index": 0 + } + ] + } + ] + }, + "AreaEffectCloudEntity": { + "parent": "Entity", + "type": "area_effect_cloud", + "translation_key": "entity.minecraft.area_effect_cloud", + "fields": [ + { + "name": "radius", + "index": 8, + "type": "float", + "default_value": 3.0, + "bits": [] + }, + { + "name": "color", + "index": 9, + "type": "integer", + "default_value": 0, + "bits": [] + }, + { + "name": "waiting", + "index": 10, + "type": "boolean", + "default_value": false, + "bits": [] + }, + { + "name": "particle_id", + "index": 11, + "type": "particle", + "default_value": "entity_effect", + "bits": [] + } + ] + }, + "VindicatorEntity": { + "parent": "IllagerEntity", + "type": "vindicator", + "translation_key": "entity.minecraft.vindicator", + "fields": [] + }, + "ZombieVillagerEntity": { + "parent": "ZombieEntity", + "type": "zombie_villager", + "translation_key": "entity.minecraft.zombie_villager", + "fields": [ + { + "name": "converting", + "index": 19, + "type": "boolean", + "default_value": false, + "bits": [] + }, + { + "name": "villager_data", + "index": 20, + "type": "villager_data", + "default_value": { + "type": "plains", + "profession": "mason", + "level": 1 + }, + "bits": [] + } + ] + } +} \ No newline at end of file diff --git a/extracted/entity_data.json b/extracted/entity_data.json new file mode 100644 index 0000000..94c140f --- /dev/null +++ b/extracted/entity_data.json @@ -0,0 +1,509 @@ +{ + "types": { + "allay": 0, + "area_effect_cloud": 1, + "armor_stand": 2, + "arrow": 3, + "axolotl": 4, + "bat": 5, + "bee": 6, + "blaze": 7, + "boat": 8, + "chest_boat": 9, + "cat": 10, + "cave_spider": 11, + "chicken": 12, + "cod": 13, + "cow": 14, + "creeper": 15, + "dolphin": 16, + "donkey": 17, + "dragon_fireball": 18, + "drowned": 19, + "elder_guardian": 20, + "end_crystal": 21, + "ender_dragon": 22, + "enderman": 23, + "endermite": 24, + "evoker": 25, + "evoker_fangs": 26, + "experience_orb": 27, + "eye_of_ender": 28, + "falling_block": 29, + "firework_rocket": 30, + "fox": 31, + "frog": 32, + "ghast": 33, + "giant": 34, + "glow_item_frame": 35, + "glow_squid": 36, + "goat": 37, + "guardian": 38, + "hoglin": 39, + "horse": 40, + "husk": 41, + "illusioner": 42, + "iron_golem": 43, + "item": 44, + "item_frame": 45, + "fireball": 46, + "leash_knot": 47, + "lightning_bolt": 48, + "llama": 49, + "llama_spit": 50, + "magma_cube": 51, + "marker": 52, + "minecart": 53, + "chest_minecart": 54, + "command_block_minecart": 55, + "furnace_minecart": 56, + "hopper_minecart": 57, + "spawner_minecart": 58, + "tnt_minecart": 59, + "mule": 60, + "mooshroom": 61, + "ocelot": 62, + "painting": 63, + "panda": 64, + "parrot": 65, + "phantom": 66, + "pig": 67, + "piglin": 68, + "piglin_brute": 69, + "pillager": 70, + "polar_bear": 71, + "tnt": 72, + "pufferfish": 73, + "rabbit": 74, + "ravager": 75, + "salmon": 76, + "sheep": 77, + "shulker": 78, + "shulker_bullet": 79, + "silverfish": 80, + "skeleton": 81, + "skeleton_horse": 82, + "slime": 83, + "small_fireball": 84, + "snow_golem": 85, + "snowball": 86, + "spectral_arrow": 87, + "spider": 88, + "squid": 89, + "stray": 90, + "strider": 91, + "tadpole": 92, + "egg": 93, + "ender_pearl": 94, + "experience_bottle": 95, + "potion": 96, + "trident": 97, + "trader_llama": 98, + "tropical_fish": 99, + "turtle": 100, + "vex": 101, + "villager": 102, + "vindicator": 103, + "wandering_trader": 104, + "warden": 105, + "witch": 106, + "wither": 107, + "wither_skeleton": 108, + "wither_skull": 109, + "wolf": 110, + "zoglin": 111, + "zombie": 112, + "zombie_horse": 113, + "zombie_villager": 114, + "zombified_piglin": 115, + "player": 116, + "fishing_bobber": 117 + }, + "statuses": { + "add_sprinting_particles_or_reset_spawner_minecart_spawn_delay": 1, + "damage_from_generic_source": 2, + "play_death_sound_or_add_projectile_hit_particles": 3, + "play_attack_sound": 4, + "stop_attack": 5, + "add_negative_player_reaction_particles": 6, + "add_positive_player_reaction_particles": 7, + "shake_off_water": 8, + "consume_item": 9, + "set_sheep_eat_grass_timer_or_prime_tnt_minecart": 10, + "look_at_villager": 11, + "add_villager_heart_particles": 12, + "add_villager_angry_particles": 13, + "add_villager_happy_particles": 14, + "add_witch_particles": 15, + "play_cure_zombie_villager_sound": 16, + "explode_firework_client": 17, + "add_breeding_particles": 18, + "reset_squid_thrust_timer": 19, + "play_spawn_effects": 20, + "play_guardian_attack_sound": 21, + "use_reduced_debug_info": 22, + "use_full_debug_info": 23, + "set_op_level_0": 24, + "set_op_level_1": 25, + "set_op_level_2": 26, + "set_op_level_3": 27, + "set_op_level_4": 28, + "block_with_shield": 29, + "break_shield": 30, + "pull_hooked_entity": 31, + "hit_armor_stand": 32, + "damage_from_thorns": 33, + "stop_looking_at_villager": 34, + "use_totem_of_undying": 35, + "damage_from_drowning": 36, + "damage_from_fire": 37, + "add_dolphin_happy_villager_particles": 38, + "stun_ravager": 39, + "tame_ocelot_failed": 40, + "tame_ocelot_success": 41, + "add_splash_particles": 42, + "add_cloud_particles": 43, + "damage_from_berry_bush": 44, + "create_eating_particles": 45, + "add_portal_particles": 46, + "break_mainhand": 47, + "break_offhand": 48, + "break_head": 49, + "break_chest": 50, + "break_legs": 51, + "break_feet": 52, + "drip_honey": 53, + "drip_rich_honey": 54, + "swap_hands": 55, + "reset_wolf_shake": 56, + "damage_from_freezing": 57, + "prepare_ram": 58, + "finish_ram": 59, + "add_death_particles": 60, + "ears_twitch": 61, + "sonic_boom": 62 + }, + "animations": { + "swing_main_hand": 0, + "damage": 1, + "wake_up": 2, + "swing_off_hand": 3, + "crit": 4, + "enchanted_hit": 5 + }, + "villager_types": { + "desert": 0, + "jungle": 1, + "plains": 2, + "savanna": 3, + "snow": 4, + "swamp": 5, + "taiga": 6 + }, + "villager_professions": { + "none": 0, + "armorer": 1, + "butcher": 2, + "cartographer": 3, + "cleric": 4, + "farmer": 5, + "fisherman": 6, + "fletcher": 7, + "leatherworker": 8, + "librarian": 9, + "mason": 10, + "nitwit": 11, + "shepherd": 12, + "toolsmith": 13, + "weaponsmith": 14 + }, + "cat_variants": { + "tabby": 0, + "black": 1, + "red": 2, + "siamese": 3, + "british_shorthair": 4, + "calico": 5, + "persian": 6, + "ragdoll": 7, + "white": 8, + "jellie": 9, + "all_black": 10 + }, + "frog_variants": { + "temperate": 0, + "warm": 1, + "cold": 2 + }, + "painting_variants": { + "kebab": { + "id": 0, + "width": 16, + "height": 16 + }, + "aztec": { + "id": 1, + "width": 16, + "height": 16 + }, + "alban": { + "id": 2, + "width": 16, + "height": 16 + }, + "aztec2": { + "id": 3, + "width": 16, + "height": 16 + }, + "bomb": { + "id": 4, + "width": 16, + "height": 16 + }, + "plant": { + "id": 5, + "width": 16, + "height": 16 + }, + "wasteland": { + "id": 6, + "width": 16, + "height": 16 + }, + "pool": { + "id": 7, + "width": 32, + "height": 16 + }, + "courbet": { + "id": 8, + "width": 32, + "height": 16 + }, + "sea": { + "id": 9, + "width": 32, + "height": 16 + }, + "sunset": { + "id": 10, + "width": 32, + "height": 16 + }, + "creebet": { + "id": 11, + "width": 32, + "height": 16 + }, + "wanderer": { + "id": 12, + "width": 16, + "height": 32 + }, + "graham": { + "id": 13, + "width": 16, + "height": 32 + }, + "match": { + "id": 14, + "width": 32, + "height": 32 + }, + "bust": { + "id": 15, + "width": 32, + "height": 32 + }, + "stage": { + "id": 16, + "width": 32, + "height": 32 + }, + "void": { + "id": 17, + "width": 32, + "height": 32 + }, + "skull_and_roses": { + "id": 18, + "width": 32, + "height": 32 + }, + "wither": { + "id": 19, + "width": 32, + "height": 32 + }, + "fighters": { + "id": 20, + "width": 64, + "height": 32 + }, + "pointer": { + "id": 21, + "width": 64, + "height": 64 + }, + "pigscene": { + "id": 22, + "width": 64, + "height": 64 + }, + "burning_skull": { + "id": 23, + "width": 64, + "height": 64 + }, + "skeleton": { + "id": 24, + "width": 64, + "height": 48 + }, + "earth": { + "id": 25, + "width": 32, + "height": 32 + }, + "wind": { + "id": 26, + "width": 32, + "height": 32 + }, + "water": { + "id": 27, + "width": 32, + "height": 32 + }, + "fire": { + "id": 28, + "width": 32, + "height": 32 + }, + "donkey_kong": { + "id": 29, + "width": 64, + "height": 48 + } + }, + "facing": { + "down": 0, + "up": 1, + "north": 2, + "south": 3, + "west": 4, + "east": 5 + }, + "poses": { + "standing": 0, + "fall_flying": 1, + "sleeping": 2, + "swimming": 3, + "spin_attack": 4, + "crouching": 5, + "long_jumping": 6, + "dying": 7, + "croaking": 8, + "using_tongue": 9, + "roaring": 10, + "sniffing": 11, + "emerging": 12, + "digging": 13 + }, + "particle_types": { + "ambient_entity_effect": 0, + "angry_villager": 1, + "block": 2, + "block_marker": 3, + "bubble": 4, + "cloud": 5, + "crit": 6, + "damage_indicator": 7, + "dragon_breath": 8, + "dripping_lava": 9, + "falling_lava": 10, + "landing_lava": 11, + "dripping_water": 12, + "falling_water": 13, + "dust": 14, + "dust_color_transition": 15, + "effect": 16, + "elder_guardian": 17, + "enchanted_hit": 18, + "enchant": 19, + "end_rod": 20, + "entity_effect": 21, + "explosion_emitter": 22, + "explosion": 23, + "sonic_boom": 24, + "falling_dust": 25, + "firework": 26, + "fishing": 27, + "flame": 28, + "sculk_soul": 29, + "sculk_charge": 30, + "sculk_charge_pop": 31, + "soul_fire_flame": 32, + "soul": 33, + "flash": 34, + "happy_villager": 35, + "composter": 36, + "heart": 37, + "instant_effect": 38, + "item": 39, + "vibration": 40, + "item_slime": 41, + "item_snowball": 42, + "large_smoke": 43, + "lava": 44, + "mycelium": 45, + "note": 46, + "poof": 47, + "portal": 48, + "rain": 49, + "smoke": 50, + "sneeze": 51, + "spit": 52, + "squid_ink": 53, + "sweep_attack": 54, + "totem_of_undying": 55, + "underwater": 56, + "splash": 57, + "witch": 58, + "bubble_pop": 59, + "current_down": 60, + "bubble_column_up": 61, + "nautilus": 62, + "dolphin": 63, + "campfire_cosy_smoke": 64, + "campfire_signal_smoke": 65, + "dripping_honey": 66, + "falling_honey": 67, + "landing_honey": 68, + "falling_nectar": 69, + "falling_spore_blossom": 70, + "ash": 71, + "crimson_spore": 72, + "warped_spore": 73, + "spore_blossom_air": 74, + "dripping_obsidian_tear": 75, + "falling_obsidian_tear": 76, + "landing_obsidian_tear": 77, + "reverse_portal": 78, + "white_ash": 79, + "small_flame": 80, + "snowflake": 81, + "dripping_dripstone_lava": 82, + "falling_dripstone_lava": 83, + "dripping_dripstone_water": 84, + "falling_dripstone_water": 85, + "glow_squid_ink": 86, + "glow": 87, + "wax_on": 88, + "wax_off": 89, + "electric_spark": 90, + "scrape": 91, + "shriek": 92 + } +} \ No newline at end of file diff --git a/extractor/src/main/java/dev/_00a/valence_extractor/extractors/Entities.java b/extractor/src/main/java/dev/_00a/valence_extractor/extractors/Entities.java index 09de8b1..4793b19 100644 --- a/extractor/src/main/java/dev/_00a/valence_extractor/extractors/Entities.java +++ b/extractor/src/main/java/dev/_00a/valence_extractor/extractors/Entities.java @@ -290,9 +290,16 @@ public class Entities implements Main.Extractor { var parent = entityClass.getSuperclass(); var hasParent = parent != null && Entity.class.isAssignableFrom(parent); - entityJson.addProperty("parent", hasParent ? parent.getSimpleName() : null); - entityJson.add("translation_key", entityType != null ? new JsonPrimitive(entityType.getTranslationKey()) : null); + if (hasParent) { + entityJson.addProperty("parent", parent.getSimpleName()); + } + + if (entityType != null) { + entityJson.addProperty("type", Registry.ENTITY_TYPE.getId(entityType).getPath()); + + entityJson.add("translation_key", new JsonPrimitive(entityType.getTranslationKey())); + } var fieldsJson = new JsonArray(); for (var entityField : entityClass.getDeclaredFields()) { diff --git a/extractor/src/main/java/dev/_00a/valence_extractor/extractors/EntityData.java b/extractor/src/main/java/dev/_00a/valence_extractor/extractors/EntityData.java index 051d3c4..8716564 100644 --- a/extractor/src/main/java/dev/_00a/valence_extractor/extractors/EntityData.java +++ b/extractor/src/main/java/dev/_00a/valence_extractor/extractors/EntityData.java @@ -22,6 +22,12 @@ public class EntityData implements Main.Extractor { public JsonElement extract() throws Exception { var dataJson = new JsonObject(); + var typesJson = new JsonObject(); + for (var type : Registry.ENTITY_TYPE) { + typesJson.addProperty(Registry.ENTITY_TYPE.getId(type).getPath(), Registry.ENTITY_TYPE.getRawId(type)); + } + dataJson.add("types", typesJson); + var statusesJson = new JsonObject(); for (var field : EntityStatuses.class.getDeclaredFields()) { if (field.canAccess(null) && field.get(null) instanceof Byte code) { diff --git a/src/client.rs b/src/client.rs index e3789b2..7385daa 100644 --- a/src/client.rs +++ b/src/client.rs @@ -1,7 +1,5 @@ //! Connections to the server after logging in. -/// Contains the [`Event`] enum and related data types. -mod event; use std::collections::{HashSet, VecDeque}; use std::iter::FusedIterator; use std::time::Duration; @@ -18,8 +16,11 @@ use crate::block_pos::BlockPos; use crate::chunk_pos::ChunkPos; use crate::config::Config; use crate::dimension::DimensionId; -use crate::entity::types::Player; -use crate::entity::{velocity_to_packet_units, Entities, Entity, EntityId, EntityKind}; +use crate::entity::kinds::Player; +use crate::entity::{ + velocity_to_packet_units, Entities, EntityId, EntityKind, Event as EntityEvent, + StatusOrAnimation, +}; use crate::player_textures::SignedPlayerTextures; use crate::protocol_inner::packets::play::c2s::{ C2sPlayPacket, DiggingStatus, InteractKind, PlayerCommandId, @@ -28,12 +29,12 @@ pub use crate::protocol_inner::packets::play::s2c::SetTitleAnimationTimes as Tit use crate::protocol_inner::packets::play::s2c::{ Animate, BiomeRegistry, BlockChangeAck, ChatType, ChatTypeChat, ChatTypeNarration, ChatTypeRegistry, ChatTypeRegistryEntry, ClearTitles, DimensionTypeRegistry, - DimensionTypeRegistryEntry, Disconnect, EntityEvent, ForgetLevelChunk, GameEvent, + DimensionTypeRegistryEntry, Disconnect, EntityStatus, ForgetLevelChunk, GameEvent, GameEventReason, KeepAlive, Login, MoveEntityPosition, MoveEntityPositionAndRotation, MoveEntityRotation, PlayerPosition, PlayerPositionFlags, RegistryCodec, RemoveEntities, Respawn, RotateHead, S2cPlayPacket, SetChunkCacheCenter, SetChunkCacheRadius, SetEntityMetadata, SetEntityMotion, SetSubtitleText, SetTitleText, SpawnPosition, SystemChat, - TeleportEntity, UpdateAttributes, UpdateAttributesProperty, ENTITY_EVENT_MAX_BOUND, + TeleportEntity, UpdateAttributes, UpdateAttributesProperty, }; use crate::protocol_inner::{BoundedInt, ByteAngle, Nbt, RawBytes, VarInt}; use crate::server::{C2sPacketChannels, NewClientData, SharedServer}; @@ -43,6 +44,9 @@ use crate::util::{chunks_in_view_distance, is_chunk_in_view_distance}; use crate::world::{WorldId, Worlds}; use crate::{ident, Ticks, LIBRARY_NAMESPACE, STANDARD_TPS}; +/// Contains the [`Event`] enum and related data types. +mod event; + /// A container for all [`Client`]s on a [`Server`](crate::server::Server). /// /// New clients are automatically inserted into this container but @@ -211,6 +215,7 @@ pub struct Client { flags: ClientFlags, /// The data for the client's own player entity. player_data: Player, + entity_events: Vec, } #[bitfield(u16)] @@ -280,6 +285,7 @@ impl Client { .with_modified_spawn_position(true) .with_got_keepalive(true), player_data: Player::new(), + entity_events: Vec::new(), } } @@ -522,6 +528,10 @@ impl Client { self.events.pop_front() } + pub fn trigger_entity_event(&mut self, event: EntityEvent) { + self.entity_events.push(event); + } + /// The current view distance of this client measured in chunks. pub fn view_distance(&self) -> u8 { self.settings @@ -1193,7 +1203,7 @@ impl Client { if let Some(entity) = entities.get(id) { debug_assert!(entity.kind() != EntityKind::Marker); if self.new_position.distance(entity.position()) <= view_dist as f64 * 16.0 { - if let Some(meta) = entity.updated_metadata_packet(id) { + if let Some(meta) = entity.updated_tracked_data_packet(id) { send_packet(&mut self.send, meta); } @@ -1273,7 +1283,7 @@ impl Client { ) } - send_entity_events(&mut self.send, id, entity); + send_entity_events(&mut self.send, id.to_network_id(), entity.events()); return true; } @@ -1291,7 +1301,7 @@ impl Client { // Update the client's own player metadata. let mut data = Vec::new(); - self.player_data.updated_metadata(&mut data); + self.player_data.updated_tracked_data(&mut data); if !data.is_empty() { data.push(0xff); @@ -1320,29 +1330,18 @@ impl Client { .expect("should not be a marker entity"), ); - if let Some(meta) = entity.initial_metadata_packet(id) { + if let Some(meta) = entity.initial_tracked_data_packet(id) { self.send_packet(meta); } - send_entity_events(&mut self.send, id, entity); + send_entity_events(&mut self.send, id.to_network_id(), entity.events()); } None }, ); - for &code in self.player_data.event_codes() { - if code <= ENTITY_EVENT_MAX_BOUND as u8 { - send_packet( - &mut self.send, - EntityEvent { - entity_id: 0, - entity_status: BoundedInt(code), - }, - ); - } - // Don't bother sending animations for self since it shouldn't have - // any effect. - } + send_entity_events(&mut self.send, 0, &self.entity_events); + self.entity_events.clear(); self.player_data.clear_modifications(); self.old_position = self.new_position; @@ -1366,24 +1365,23 @@ fn send_packet(send_opt: &mut SendOpt, pkt: impl Into) { } } -fn send_entity_events(send_opt: &mut SendOpt, id: EntityId, entity: &Entity) { - for &code in entity.state.event_codes() { - if code <= ENTITY_EVENT_MAX_BOUND as u8 { - send_packet( +fn send_entity_events(send_opt: &mut SendOpt, entity_id: i32, events: &[EntityEvent]) { + for &event in events { + match event.status_or_animation() { + StatusOrAnimation::Status(code) => send_packet( send_opt, - EntityEvent { - entity_id: id.to_network_id(), - entity_status: BoundedInt(code), + EntityStatus { + entity_id, + entity_status: code, }, - ); - } else { - send_packet( + ), + StatusOrAnimation::Animation(code) => send_packet( send_opt, Animate { - entity_id: VarInt(id.to_network_id()), - animation: BoundedInt(code - ENTITY_EVENT_MAX_BOUND as u8 - 1), + entity_id: VarInt(entity_id), + animation: code, }, - ) + ), } } } diff --git a/src/entity.rs b/src/entity.rs index 318403c..e069842 100644 --- a/src/entity.rs +++ b/src/entity.rs @@ -1,16 +1,13 @@ //! Dynamic actors in a world. -pub mod state; -pub mod types; - use std::collections::hash_map::Entry; use std::collections::HashMap; use std::iter::FusedIterator; use std::num::NonZeroU32; use bitfield_struct::bitfield; +pub use kinds::{EntityEnum, EntityKind}; use rayon::iter::ParallelIterator; -pub use types::{EntityKind, EntityState}; use uuid::Uuid; use vek::{Aabb, Vec3}; @@ -24,6 +21,11 @@ use crate::util::aabb_from_bottom_and_size; use crate::world::WorldId; use crate::STANDARD_TPS; +pub mod data; +pub mod kinds; + +include!(concat!(env!("OUT_DIR"), "/entity_event.rs")); + /// A container for all [`Entity`]s on a [`Server`](crate::server::Server). /// /// # Spawning Player Entities @@ -72,7 +74,8 @@ impl Entities { Entry::Vacant(ve) => { let (k, e) = self.sm.insert(Entity { data, - state: EntityState::new(kind), + variants: EntityEnum::new(kind), + events: Vec::new(), flags: EntityFlags(0), world: WorldId::NULL, new_position: Vec3::default(), @@ -197,7 +200,8 @@ impl Entities { pub(crate) fn update(&mut self) { for (_, e) in self.iter_mut() { e.old_position = e.new_position; - e.state.clear_modifications(); + e.variants.clear_modifications(); + e.events.clear(); e.flags.set_yaw_or_pitch_modified(false); e.flags.set_head_yaw_modified(false); @@ -239,9 +243,9 @@ impl EntityId { pub struct Entity { /// Custom data. pub data: C::EntityData, - /// Kind-specific state for this entity. - pub state: EntityState, + variants: EntityEnum, flags: EntityFlags, + events: Vec, world: WorldId, new_position: Vec3, old_position: Vec3, @@ -267,9 +271,25 @@ impl Entity { self.flags } + pub fn view(&self) -> &EntityEnum { + &self.variants + } + + pub fn view_mut(&mut self) -> &mut EntityEnum { + &mut self.variants + } + /// Gets the [`EntityKind`] of this entity. pub fn kind(&self) -> EntityKind { - self.state.kind() + self.variants.kind() + } + + pub fn trigger_event(&mut self, event: Event) { + self.events.push(event); + } + + pub(crate) fn events(&self) -> &[Event] { + &self.events } /// Gets the [`WorldId`](crate::world::WorldId) of the world this entity is @@ -387,18 +407,18 @@ impl Entity { /// /// [interact event]: crate::client::Event::InteractWithEntity pub fn hitbox(&self) -> Aabb { - let dims = match &self.state { - EntityState::Allay(_) => [0.6, 0.35, 0.6], - EntityState::ChestBoat(_) => [1.375, 0.5625, 1.375], - EntityState::Frog(_) => [0.5, 0.5, 0.5], - EntityState::Tadpole(_) => [0.4, 0.3, 0.4], - EntityState::Warden(_) => [0.9, 2.9, 0.9], - EntityState::AreaEffectCloud(e) => [ + let dims = match &self.variants { + EntityEnum::Allay(_) => [0.6, 0.35, 0.6], + EntityEnum::ChestBoat(_) => [1.375, 0.5625, 1.375], + EntityEnum::Frog(_) => [0.5, 0.5, 0.5], + EntityEnum::Tadpole(_) => [0.4, 0.3, 0.4], + EntityEnum::Warden(_) => [0.9, 2.9, 0.9], + EntityEnum::AreaEffectCloud(e) => [ e.get_radius() as f64 * 2.0, 0.5, e.get_radius() as f64 * 2.0, ], - EntityState::ArmorStand(e) => { + EntityEnum::ArmorStand(e) => { if e.get_marker() { [0.0, 0.0, 0.0] } else if e.get_small() { @@ -407,160 +427,171 @@ impl Entity { [0.5, 1.975, 0.5] } } - EntityState::Arrow(_) => [0.5, 0.5, 0.5], - EntityState::Axolotl(_) => [1.3, 0.6, 1.3], - EntityState::Bat(_) => [0.5, 0.9, 0.5], - EntityState::Bee(_) => [0.7, 0.6, 0.7], // TODO: baby size? - EntityState::Blaze(_) => [0.6, 1.8, 0.6], - EntityState::Boat(_) => [1.375, 0.5625, 1.375], - EntityState::Cat(_) => [0.6, 0.7, 0.6], - EntityState::CaveSpider(_) => [0.7, 0.5, 0.7], - EntityState::Chicken(_) => [0.4, 0.7, 0.4], // TODO: baby size? - EntityState::Cod(_) => [0.5, 0.3, 0.5], - EntityState::Cow(_) => [0.9, 1.4, 0.9], // TODO: baby size? - EntityState::Creeper(_) => [0.6, 1.7, 0.6], - EntityState::Dolphin(_) => [0.9, 0.6, 0.9], - EntityState::Donkey(_) => [1.5, 1.39648, 1.5], // TODO: baby size? - EntityState::DragonFireball(_) => [1.0, 1.0, 1.0], - EntityState::Drowned(_) => [0.6, 1.95, 0.6], // TODO: baby size? - EntityState::ElderGuardian(_) => [1.9975, 1.9975, 1.9975], - EntityState::EndCrystal(_) => [2.0, 2.0, 2.0], - EntityState::EnderDragon(_) => [16.0, 8.0, 16.0], - EntityState::Enderman(_) => [0.6, 2.9, 0.6], - EntityState::Endermite(_) => [0.4, 0.3, 0.4], - EntityState::Evoker(_) => [0.6, 1.95, 0.6], - EntityState::EvokerFangs(_) => [0.5, 0.8, 0.5], - EntityState::ExperienceOrb(_) => [0.5, 0.5, 0.5], - EntityState::EyeOfEnder(_) => [0.25, 0.25, 0.25], - EntityState::FallingBlock(_) => [0.98, 0.98, 0.98], - EntityState::FireworkRocket(_) => [0.25, 0.25, 0.25], - EntityState::Fox(_) => [0.6, 0.7, 0.6], // TODO: baby size? - EntityState::Ghast(_) => [4.0, 4.0, 4.0], - EntityState::Giant(_) => [3.6, 12.0, 3.6], - EntityState::GlowItemFrame(_) => todo!("account for rotation"), - EntityState::GlowSquid(_) => [0.8, 0.8, 0.8], - EntityState::Goat(_) => [1.3, 0.9, 1.3], // TODO: baby size? - EntityState::Guardian(_) => [0.85, 0.85, 0.85], - EntityState::Hoglin(_) => [1.39648, 1.4, 1.39648], // TODO: baby size? - EntityState::Horse(_) => [1.39648, 1.6, 1.39648], // TODO: baby size? - EntityState::Husk(_) => [0.6, 1.95, 0.6], // TODO: baby size? - EntityState::Illusioner(_) => [0.6, 1.95, 0.6], - EntityState::IronGolem(_) => [1.4, 2.7, 1.4], - EntityState::Item(_) => [0.25, 0.25, 0.25], - EntityState::ItemFrame(_) => todo!("account for rotation"), - EntityState::Fireball(_) => [1.0, 1.0, 1.0], - EntityState::LeashKnot(_) => [0.375, 0.5, 0.375], - EntityState::LightningBolt(_) => [0.0, 0.0, 0.0], - EntityState::Llama(_) => [0.9, 1.87, 0.9], // TODO: baby size? - EntityState::LlamaSpit(_) => [0.25, 0.25, 0.25], - EntityState::MagmaCube(e) => { - let s = e.get_size() as f64 * 0.51000005; + EntityEnum::Arrow(_) => [0.5, 0.5, 0.5], + EntityEnum::Axolotl(_) => [1.3, 0.6, 1.3], + EntityEnum::Bat(_) => [0.5, 0.9, 0.5], + EntityEnum::Bee(_) => [0.7, 0.6, 0.7], // TODO: baby size? + EntityEnum::Blaze(_) => [0.6, 1.8, 0.6], + EntityEnum::Boat(_) => [1.375, 0.5625, 1.375], + EntityEnum::Cat(_) => [0.6, 0.7, 0.6], + EntityEnum::CaveSpider(_) => [0.7, 0.5, 0.7], + EntityEnum::Chicken(_) => [0.4, 0.7, 0.4], // TODO: baby size? + EntityEnum::Cod(_) => [0.5, 0.3, 0.5], + EntityEnum::Cow(_) => [0.9, 1.4, 0.9], // TODO: baby size? + EntityEnum::Creeper(_) => [0.6, 1.7, 0.6], + EntityEnum::Dolphin(_) => [0.9, 0.6, 0.9], + EntityEnum::Donkey(_) => [1.5, 1.39648, 1.5], // TODO: baby size? + EntityEnum::DragonFireball(_) => [1.0, 1.0, 1.0], + EntityEnum::Drowned(_) => [0.6, 1.95, 0.6], // TODO: baby size? + EntityEnum::ElderGuardian(_) => [1.9975, 1.9975, 1.9975], + EntityEnum::EndCrystal(_) => [2.0, 2.0, 2.0], + EntityEnum::EnderDragon(_) => [16.0, 8.0, 16.0], + EntityEnum::Enderman(_) => [0.6, 2.9, 0.6], + EntityEnum::Endermite(_) => [0.4, 0.3, 0.4], + EntityEnum::Evoker(_) => [0.6, 1.95, 0.6], + EntityEnum::EvokerFangs(_) => [0.5, 0.8, 0.5], + EntityEnum::ExperienceOrb(_) => [0.5, 0.5, 0.5], + EntityEnum::EyeOfEnder(_) => [0.25, 0.25, 0.25], + EntityEnum::FallingBlock(_) => [0.98, 0.98, 0.98], + EntityEnum::FireworkRocket(_) => [0.25, 0.25, 0.25], + EntityEnum::Fox(_) => [0.6, 0.7, 0.6], // TODO: baby size? + EntityEnum::Ghast(_) => [4.0, 4.0, 4.0], + EntityEnum::Giant(_) => [3.6, 12.0, 3.6], + EntityEnum::GlowItemFrame(_) => todo!("account for rotation"), + EntityEnum::GlowSquid(_) => [0.8, 0.8, 0.8], + EntityEnum::Goat(_) => [1.3, 0.9, 1.3], // TODO: baby size? + EntityEnum::Guardian(_) => [0.85, 0.85, 0.85], + EntityEnum::Hoglin(_) => [1.39648, 1.4, 1.39648], // TODO: baby size? + EntityEnum::Horse(_) => [1.39648, 1.6, 1.39648], // TODO: baby size? + EntityEnum::Husk(_) => [0.6, 1.95, 0.6], // TODO: baby size? + EntityEnum::Illusioner(_) => [0.6, 1.95, 0.6], + EntityEnum::IronGolem(_) => [1.4, 2.7, 1.4], + EntityEnum::Item(_) => [0.25, 0.25, 0.25], + EntityEnum::ItemFrame(_) => todo!("account for rotation"), + EntityEnum::Fireball(_) => [1.0, 1.0, 1.0], + EntityEnum::LeashKnot(_) => [0.375, 0.5, 0.375], + EntityEnum::Lightning(_) => [0.0, 0.0, 0.0], + EntityEnum::Llama(_) => [0.9, 1.87, 0.9], // TODO: baby size? + EntityEnum::LlamaSpit(_) => [0.25, 0.25, 0.25], + EntityEnum::MagmaCube(e) => { + let s = e.get_slime_size() as f64 * 0.51000005; [s, s, s] } - EntityState::Marker(_) => [0.0, 0.0, 0.0], - EntityState::Minecart(_) => [0.98, 0.7, 0.98], - EntityState::ChestMinecart(_) => [0.98, 0.7, 0.98], - EntityState::CommandBlockMinecart(_) => [0.98, 0.7, 0.98], - EntityState::FurnaceMinecart(_) => [0.98, 0.7, 0.98], - EntityState::HopperMinecart(_) => [0.98, 0.7, 0.98], - EntityState::SpawnerMinecart(_) => [0.98, 0.7, 0.98], - EntityState::TntMinecart(_) => [0.98, 0.7, 0.98], - EntityState::Mule(_) => [1.39648, 1.6, 1.39648], // TODO: baby size? - EntityState::Mooshroom(_) => [0.9, 1.4, 0.9], // TODO: baby size? - EntityState::Ocelot(_) => [0.6, 0.7, 0.6], // TODO: baby size? - EntityState::Painting(_) => todo!("account for rotation and type"), - EntityState::Panda(_) => [0.6, 0.7, 0.6], // TODO: baby size? - EntityState::Parrot(_) => [0.5, 0.9, 0.5], - EntityState::Phantom(_) => [0.9, 0.5, 0.9], - EntityState::Pig(_) => [0.9, 0.9, 0.9], // TODO: baby size? - EntityState::Piglin(_) => [0.6, 1.95, 0.6], // TODO: baby size? - EntityState::PiglinBrute(_) => [0.6, 1.95, 0.6], - EntityState::Pillager(_) => [0.6, 1.95, 0.6], - EntityState::PolarBear(_) => [1.4, 1.4, 1.4], // TODO: baby size? - EntityState::Tnt(_) => [0.98, 0.98, 0.98], - EntityState::Pufferfish(_) => [0.7, 0.7, 0.7], - EntityState::Rabbit(_) => [0.4, 0.5, 0.4], // TODO: baby size? - EntityState::Ravager(_) => [1.95, 2.2, 1.95], - EntityState::Salmon(_) => [0.7, 0.4, 0.7], - EntityState::Sheep(_) => [0.9, 1.3, 0.9], // TODO: baby size? - EntityState::Shulker(_) => [1.0, 1.0, 1.0], // TODO: how is height calculated? - EntityState::ShulkerBullet(_) => [0.3125, 0.3125, 0.3125], - EntityState::Silverfish(_) => [0.4, 0.3, 0.4], - EntityState::Skeleton(_) => [0.6, 1.99, 0.6], - EntityState::SkeletonHorse(_) => [1.39648, 1.6, 1.39648], // TODO: baby size? - EntityState::Slime(e) => { - let s = 0.51000005 * e.get_size() as f64; + EntityEnum::Marker(_) => [0.0, 0.0, 0.0], + EntityEnum::Minecart(_) => [0.98, 0.7, 0.98], + EntityEnum::ChestMinecart(_) => [0.98, 0.7, 0.98], + EntityEnum::CommandBlockMinecart(_) => [0.98, 0.7, 0.98], + EntityEnum::FurnaceMinecart(_) => [0.98, 0.7, 0.98], + EntityEnum::HopperMinecart(_) => [0.98, 0.7, 0.98], + EntityEnum::SpawnerMinecart(_) => [0.98, 0.7, 0.98], + EntityEnum::TntMinecart(_) => [0.98, 0.7, 0.98], + EntityEnum::Mule(_) => [1.39648, 1.6, 1.39648], // TODO: baby size? + EntityEnum::Mooshroom(_) => [0.9, 1.4, 0.9], // TODO: baby size? + EntityEnum::Ocelot(_) => [0.6, 0.7, 0.6], // TODO: baby size? + EntityEnum::Painting(_) => todo!("account for rotation and type"), + EntityEnum::Panda(_) => [0.6, 0.7, 0.6], // TODO: baby size? + EntityEnum::Parrot(_) => [0.5, 0.9, 0.5], + EntityEnum::Phantom(_) => [0.9, 0.5, 0.9], + EntityEnum::Pig(_) => [0.9, 0.9, 0.9], // TODO: baby size? + EntityEnum::Piglin(_) => [0.6, 1.95, 0.6], // TODO: baby size? + EntityEnum::PiglinBrute(_) => [0.6, 1.95, 0.6], + EntityEnum::Pillager(_) => [0.6, 1.95, 0.6], + EntityEnum::PolarBear(_) => [1.4, 1.4, 1.4], // TODO: baby size? + EntityEnum::Tnt(_) => [0.98, 0.98, 0.98], + EntityEnum::Pufferfish(_) => [0.7, 0.7, 0.7], + EntityEnum::Rabbit(_) => [0.4, 0.5, 0.4], // TODO: baby size? + EntityEnum::Ravager(_) => [1.95, 2.2, 1.95], + EntityEnum::Salmon(_) => [0.7, 0.4, 0.7], + EntityEnum::Sheep(_) => [0.9, 1.3, 0.9], // TODO: baby size? + EntityEnum::Shulker(_) => [1.0, 1.0, 1.0], // TODO: how is height calculated? + EntityEnum::ShulkerBullet(_) => [0.3125, 0.3125, 0.3125], + EntityEnum::Silverfish(_) => [0.4, 0.3, 0.4], + EntityEnum::Skeleton(_) => [0.6, 1.99, 0.6], + EntityEnum::SkeletonHorse(_) => [1.39648, 1.6, 1.39648], // TODO: baby size? + EntityEnum::Slime(e) => { + let s = 0.51000005 * e.get_slime_size() as f64; [s, s, s] } - EntityState::SmallFireball(_) => [0.3125, 0.3125, 0.3125], - EntityState::SnowGolem(_) => [0.7, 1.9, 0.7], - EntityState::Snowball(_) => [0.25, 0.25, 0.25], - EntityState::SpectralArrow(_) => [0.5, 0.5, 0.5], - EntityState::Spider(_) => [1.4, 0.9, 1.4], - EntityState::Squid(_) => [0.8, 0.8, 0.8], - EntityState::Stray(_) => [0.6, 1.99, 0.6], - EntityState::Strider(_) => [0.9, 1.7, 0.9], // TODO: baby size? - EntityState::Egg(_) => [0.25, 0.25, 0.25], - EntityState::EnderPearl(_) => [0.25, 0.25, 0.25], - EntityState::ExperienceBottle(_) => [0.25, 0.25, 0.25], - EntityState::Potion(_) => [0.25, 0.25, 0.25], - EntityState::Trident(_) => [0.5, 0.5, 0.5], - EntityState::TraderLlama(_) => [0.9, 1.87, 0.9], - EntityState::TropicalFish(_) => [0.5, 0.4, 0.5], - EntityState::Turtle(_) => [1.2, 0.4, 1.2], // TODO: baby size? - EntityState::Vex(_) => [0.4, 0.8, 0.4], - EntityState::Villager(_) => [0.6, 1.95, 0.6], // TODO: baby size? - EntityState::Vindicator(_) => [0.6, 1.95, 0.6], - EntityState::WanderingTrader(_) => [0.6, 1.95, 0.6], - EntityState::Witch(_) => [0.6, 1.95, 0.6], - EntityState::Wither(_) => [0.9, 3.5, 0.9], - EntityState::WitherSkeleton(_) => [0.7, 2.4, 0.7], - EntityState::WitherSkull(_) => [0.3125, 0.3125, 0.3125], - EntityState::Wolf(_) => [0.6, 0.85, 0.6], // TODO: baby size? - EntityState::Zoglin(_) => [1.39648, 1.4, 1.39648], // TODO: baby size? - EntityState::Zombie(_) => [0.6, 1.95, 0.6], // TODO: baby size? - EntityState::ZombieHorse(_) => [1.39648, 1.6, 1.39648], // TODO: baby size? - EntityState::ZombieVillager(_) => [0.6, 1.95, 0.6], // TODO: baby size? - EntityState::ZombifiedPiglin(_) => [0.6, 1.95, 0.6], // TODO: baby size? - EntityState::Player(_) => [0.6, 1.8, 0.6], // TODO: changes depending on the pose. - EntityState::FishingBobber(_) => [0.25, 0.25, 0.25], + EntityEnum::SmallFireball(_) => [0.3125, 0.3125, 0.3125], + EntityEnum::SnowGolem(_) => [0.7, 1.9, 0.7], + EntityEnum::Snowball(_) => [0.25, 0.25, 0.25], + EntityEnum::SpectralArrow(_) => [0.5, 0.5, 0.5], + EntityEnum::Spider(_) => [1.4, 0.9, 1.4], + EntityEnum::Squid(_) => [0.8, 0.8, 0.8], + EntityEnum::Stray(_) => [0.6, 1.99, 0.6], + EntityEnum::Strider(_) => [0.9, 1.7, 0.9], // TODO: baby size? + EntityEnum::Egg(_) => [0.25, 0.25, 0.25], + EntityEnum::EnderPearl(_) => [0.25, 0.25, 0.25], + EntityEnum::ExperienceBottle(_) => [0.25, 0.25, 0.25], + EntityEnum::Potion(_) => [0.25, 0.25, 0.25], + EntityEnum::Trident(_) => [0.5, 0.5, 0.5], + EntityEnum::TraderLlama(_) => [0.9, 1.87, 0.9], + EntityEnum::TropicalFish(_) => [0.5, 0.4, 0.5], + EntityEnum::Turtle(_) => [1.2, 0.4, 1.2], // TODO: baby size? + EntityEnum::Vex(_) => [0.4, 0.8, 0.4], + EntityEnum::Villager(_) => [0.6, 1.95, 0.6], // TODO: baby size? + EntityEnum::Vindicator(_) => [0.6, 1.95, 0.6], + EntityEnum::WanderingTrader(_) => [0.6, 1.95, 0.6], + EntityEnum::Witch(_) => [0.6, 1.95, 0.6], + EntityEnum::Wither(_) => [0.9, 3.5, 0.9], + EntityEnum::WitherSkeleton(_) => [0.7, 2.4, 0.7], + EntityEnum::WitherSkull(_) => [0.3125, 0.3125, 0.3125], + EntityEnum::Wolf(_) => [0.6, 0.85, 0.6], // TODO: baby size? + EntityEnum::Zoglin(_) => [1.39648, 1.4, 1.39648], // TODO: baby size? + EntityEnum::Zombie(_) => [0.6, 1.95, 0.6], // TODO: baby size? + EntityEnum::ZombieHorse(_) => [1.39648, 1.6, 1.39648], // TODO: baby size? + EntityEnum::ZombieVillager(_) => [0.6, 1.95, 0.6], // TODO: baby size? + EntityEnum::ZombifiedPiglin(_) => [0.6, 1.95, 0.6], // TODO: baby size? + EntityEnum::Player(_) => [0.6, 1.8, 0.6], // TODO: changes depending on the pose. + EntityEnum::FishingBobber(_) => [0.25, 0.25, 0.25], }; aabb_from_bottom_and_size(self.new_position, dims.into()) } - /// Gets the metadata packet to send to clients after this entity has been - /// spawned. + /// Gets the tracked data packet to send to clients after this entity has + /// been spawned. /// - /// Is `None` if there is no initial metadata. - pub(crate) fn initial_metadata_packet(&self, this_id: EntityId) -> Option { - self.state.initial_metadata().map(|meta| SetEntityMetadata { - entity_id: VarInt(this_id.to_network_id()), - metadata: RawBytes(meta), - }) + /// Returns `None` if all the tracked data is at its default values. + pub(crate) fn initial_tracked_data_packet( + &self, + this_id: EntityId, + ) -> Option { + self.variants + .initial_tracked_data() + .map(|meta| SetEntityMetadata { + entity_id: VarInt(this_id.to_network_id()), + metadata: RawBytes(meta), + }) } - /// Gets the metadata packet to send to clients when the entity is modified. + /// Gets the tracked data packet to send to clients when the entity is + /// modified. /// - /// Is `None` if this entity's metadata has not been modified. - pub(crate) fn updated_metadata_packet(&self, this_id: EntityId) -> Option { - self.state.updated_metadata().map(|meta| SetEntityMetadata { - entity_id: VarInt(this_id.to_network_id()), - metadata: RawBytes(meta), - }) + /// Returns `None` if this entity's tracked data has not been modified. + pub(crate) fn updated_tracked_data_packet( + &self, + this_id: EntityId, + ) -> Option { + self.variants + .updated_tracked_data() + .map(|meta| SetEntityMetadata { + entity_id: VarInt(this_id.to_network_id()), + metadata: RawBytes(meta), + }) } pub(crate) fn spawn_packet(&self, this_id: EntityId) -> Option { - match &self.state { - EntityState::Marker(_) => None, - EntityState::ExperienceOrb(_) => { + match &self.variants { + EntityEnum::Marker(_) => None, + EntityEnum::ExperienceOrb(_) => { Some(EntitySpawnPacket::ExperienceOrb(AddExperienceOrb { entity_id: VarInt(this_id.to_network_id()), position: self.new_position, count: 0, // TODO })) } - EntityState::Player(_) => Some(EntitySpawnPacket::Player(AddPlayer { + EntityEnum::Player(_) => Some(EntitySpawnPacket::Player(AddPlayer { entity_id: VarInt(this_id.to_network_id()), player_uuid: self.uuid, position: self.new_position, diff --git a/src/entity/state.rs b/src/entity/data.rs similarity index 69% rename from src/entity/state.rs rename to src/entity/data.rs index 01dfdf2..4e58051 100644 --- a/src/entity/state.rs +++ b/src/entity/data.rs @@ -1,35 +1,63 @@ -//! Types used in [`EntityData`](crate::entity::EntityData). +//! Primitive types used in getters and setters on entities. -use std::io::Write; +use std::io::{Read, Write}; -use crate::protocol_inner::{Encode, VarInt}; +use crate::protocol_inner::{Decode, Encode, VarInt}; -#[derive(Clone, Copy, Default, PartialEq, PartialOrd, Debug)] -pub struct ArmorStandRotations { - /// Rotation on the X axis in degrees. - pub x: f32, - /// Rotation on the Y axis in degrees. - pub y: f32, - /// Rotation on the Z axis in degrees. - pub z: f32, -} +/// Represents an optional `u32` value excluding [`u32::MAX`]. +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Debug)] +pub struct OptionalInt(u32); -impl ArmorStandRotations { - pub fn new(x: f32, y: f32, z: f32) -> Self { - Self { x, y, z } +impl OptionalInt { + /// Returns `None` iff `n` is Some(u32::MAX). + pub fn new(n: impl Into>) -> Option { + match n.into() { + None => Some(Self(0)), + Some(u32::MAX) => None, + Some(n) => Some(Self(n + 1)), + } + } + + pub fn get(self) -> Option { + self.0.checked_sub(1) } } -impl Encode for ArmorStandRotations { +impl Encode for OptionalInt { fn encode(&self, w: &mut impl Write) -> anyhow::Result<()> { - self.x.encode(w)?; - self.y.encode(w)?; - self.z.encode(w) + VarInt(self.0 as i32).encode(w) + } +} + +impl Decode for OptionalInt { + fn decode(r: &mut impl Read) -> anyhow::Result { + Ok(Self(VarInt::decode(r)?.0 as u32)) + } +} + +#[derive(Clone, Copy, PartialEq, PartialOrd, Debug)] +pub struct EulerAngle { + pub pitch: f32, + pub yaw: f32, + pub roll: f32, +} + +impl EulerAngle { + pub fn new(pitch: f32, yaw: f32, roll: f32) -> Self { + Self { pitch, yaw, roll } + } +} + +impl Encode for EulerAngle { + fn encode(&self, w: &mut impl Write) -> anyhow::Result<()> { + self.pitch.encode(w)?; + self.yaw.encode(w)?; + self.roll.encode(w) } } #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] -pub enum Direction { +pub enum Facing { Down, Up, North, @@ -38,7 +66,7 @@ pub enum Direction { East, } -impl Encode for Direction { +impl Encode for Facing { fn encode(&self, w: &mut impl Write) -> anyhow::Result<()> { VarInt(*self as i32).encode(w) } @@ -206,7 +234,7 @@ impl Encode for FrogKind { #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Debug)] pub enum PaintingKind { #[default] - Default, // TODO + Kebab, // TODO } impl Encode for PaintingKind { @@ -214,3 +242,15 @@ impl Encode for PaintingKind { VarInt(*self as i32).encode(w) } } + +// TODO +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] +pub enum Particle { + EntityEffect = 21, +} + +impl Encode for Particle { + fn encode(&self, w: &mut impl Write) -> anyhow::Result<()> { + VarInt(*self as i32).encode(w) + } +} diff --git a/src/entity/types.rs b/src/entity/kinds.rs similarity index 54% rename from src/entity/types.rs rename to src/entity/kinds.rs index 0c9eaa7..03f3597 100644 --- a/src/entity/types.rs +++ b/src/entity/kinds.rs @@ -1,10 +1,9 @@ -//! Contains a struct for each variant in [`EntityKind`]. +//! Contains the [`EntityEnum`] and the types for each variant. -#![allow(clippy::all, missing_docs)] +#![allow(clippy::all, missing_docs, trivial_numeric_casts)] use crate::block::{BlockPos, BlockState}; -use crate::entity::state::*; -use crate::entity::EntityId; +use crate::entity::data::*; use crate::protocol_inner::{Encode, VarInt}; use crate::text::Text; use crate::uuid::Uuid; diff --git a/src/protocol_inner/packets.rs b/src/protocol_inner/packets.rs index 9e29d2a..1dd8962 100644 --- a/src/protocol_inner/packets.rs +++ b/src/protocol_inner/packets.rs @@ -618,7 +618,7 @@ pub mod play { def_struct! { Animate 0x03 { entity_id: VarInt, - animation: BoundedInt, + animation: u8, } } @@ -735,12 +735,10 @@ pub mod play { } } - pub const ENTITY_EVENT_MAX_BOUND: i64 = 62; - def_struct! { - EntityEvent 0x18 { + EntityStatus 0x18 { entity_id: i32, - entity_status: BoundedInt, + entity_status: u8, } } @@ -1270,7 +1268,7 @@ pub mod play { BossEvent, ClearTitles, Disconnect, - EntityEvent, + EntityStatus, ForgetLevelChunk, GameEvent, KeepAlive,