valence/build/entity.rs
Ryan adc8a4faae Give World ownership over entities, clients, and chunks.
This change was made to make it easier for invariants to be upheld. When
the spatial partition is added, we can ensure that changes to entities
are immediately reflected in the partition. Additionally, chunks being
shared between worlds was a leaky abstraction to begin with and is now
removed. A method in `Config` is now necessary to determine what world a
client should join.

Along with this, most mutable references have been wrapped in a newtype
to ensure that `mem::swap` cannot be used on them, which would break
invariants. This is analogous to `Pin<&mut T>`. The reason we can't use
Pin directly is because it would require unnecessary unsafe code
within the library.
2022-05-16 02:36:14 -07:00

2241 lines
60 KiB
Rust

//! See: <https://wiki.vg/Entity_metadata>
use std::collections::{BTreeMap, HashMap};
use anyhow::Context;
use heck::{ToPascalCase, ToSnakeCase};
use proc_macro2::TokenStream;
use quote::quote;
use serde::Deserialize;
use crate::{ident, write_to_out_path};
struct Class {
name: &'static str,
inherit: Option<&'static Class>,
fields: &'static [Field],
}
struct Field {
name: &'static str,
typ: Type,
}
/// Each variant contains the default value for the field.
enum Type {
BitFields(&'static [BitField]),
Byte(u8),
VarInt(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,
Pose,
// ==== Specialized ==== //
OptEntityId,
BoatVariant,
MainHand,
}
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::Pose => quote! { Pose::default() },
Type::OptEntityId => quote! { None },
Type::BoatVariant => quote! { BoatVariant::default() },
Type::MainHand => quote! { MainHand::default() },
}
}
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::Pose => 18,
Type::OptEntityId => 17,
Type::BoatVariant => 1,
Type::MainHand => 0,
}
}
}
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),
},
],
};
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),
},
],
};
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),
},
],
};
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,
},
]),
}],
};
const AMBIENT_CREATURE: Class = Class {
name: "ambient_creature",
inherit: Some(&MOB),
fields: &[],
};
const PATHFINDER_MOB: Class = Class {
name: "pathfinder_mob",
inherit: Some(&MOB),
fields: &[],
};
const WATER_ANIMAL: Class = Class {
name: "water_animal",
inherit: Some(&PATHFINDER_MOB),
fields: &[],
};
const ABSTRACT_FISH: Class = Class {
name: "abstract_fish",
inherit: Some(&WATER_ANIMAL),
fields: &[Field {
name: "from_bucket",
typ: Type::Bool(false),
}],
};
const AGEABLE_MOB: Class = Class {
name: "ageable_mob",
inherit: Some(&PATHFINDER_MOB),
fields: &[Field {
name: "is_baby",
typ: Type::Bool(false),
}],
};
const ANIMAL: Class = Class {
name: "animal",
inherit: Some(&PATHFINDER_MOB),
fields: &[],
};
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,
},
],
};
const CHESTED_HORSE: Class = Class {
name: "chested_horse",
inherit: Some(&ABSTRACT_HORSE),
fields: &[Field {
name: "has_chest",
typ: Type::Bool(false),
}],
};
const COW: Class = Class {
name: "cow",
inherit: Some(&ANIMAL),
fields: &[],
};
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,
},
]),
}],
};
const ABSTRACT_VILLAGER: Class = Class {
name: "abstract_villager",
inherit: Some(&AGEABLE_MOB),
fields: &[Field {
name: "head_shake_timer",
typ: Type::VarInt(0),
}],
};
const ABSTRACT_GOLEM: Class = Class {
name: "abstract_golem",
inherit: Some(&PATHFINDER_MOB),
fields: &[],
};
const MONSTER: Class = Class {
name: "monster",
inherit: Some(&PATHFINDER_MOB),
fields: &[],
};
const BASE_PIGLIN: Class = Class {
name: "base_piglin",
inherit: Some(&MONSTER),
fields: &[Field {
name: "zombification_immune",
typ: Type::Bool(false),
}],
};
const GUARDIAN: Class = Class {
name: "guardian",
inherit: Some(&MONSTER),
fields: &[
Field {
name: "retracting_spikes",
typ: Type::Bool(false),
},
Field {
name: "target",
typ: Type::OptEntityId,
},
],
};
const RAIDER: Class = Class {
name: "raider",
inherit: Some(&MONSTER),
fields: &[Field {
name: "celebrating",
typ: Type::Bool(false),
}],
};
const ABSTRACT_ILLAGER: Class = Class {
name: "abstract_illager",
inherit: Some(&RAIDER),
fields: &[],
};
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) */
}],
};
const ABSTRACT_SKELETON: Class = Class {
name: "abstract_skeleton",
inherit: Some(&MONSTER),
fields: &[],
};
const SPIDER: Class = Class {
name: "spider",
inherit: Some(&MONSTER),
fields: &[Field {
name: "spider_flags",
typ: Type::BitFields(&[BitField {
name: "climbing",
offset: 0,
default: false,
}]),
}],
};
const ZOMBIE: Class = Class {
name: "zombie",
inherit: Some(&MONSTER),
fields: &[Field {
name: "baby",
typ: Type::Bool(false),
}],
};
const FLYING: Class = Class {
name: "flying",
inherit: Some(&MOB),
fields: &[],
};
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),
},
],
};
const ABSTRACT_MINECART_CONTAINER: Class = Class {
name: "abstract_minecart_container",
inherit: Some(&ABSTRACT_MINECART),
fields: &[],
};
const ENTITIES: &[Class] = &[
Class {
// TODO: how is this defined?
name: "leash_knot",
inherit: None,
fields: &[],
},
Class {
// TODO: how is this defined?
name: "lightning_bolt",
inherit: None,
fields: &[],
},
Class {
name: "experience_orb",
inherit: None,
fields: &[],
},
Class {
name: "painting",
inherit: None,
fields: &[],
},
Class {
name: "marker",
inherit: None,
fields: &[],
},
Class {
name: "item",
inherit: Some(&BASE_ENTITY),
fields: &[], // TODO: what are the fields?
},
Class {
name: "egg",
inherit: Some(&BASE_ENTITY),
fields: &[Field {
name: "item",
typ: Type::Slot,
}],
},
Class {
name: "ender_pearl",
inherit: Some(&BASE_ENTITY),
fields: &[Field {
name: "item",
typ: Type::Slot,
}],
},
Class {
name: "experience_bottle",
inherit: Some(&BASE_ENTITY),
fields: &[Field {
name: "item",
typ: Type::Slot,
}],
},
Class {
name: "potion",
inherit: Some(&BASE_ENTITY),
fields: &[Field {
name: "potion",
typ: Type::Slot,
}],
},
Class {
name: "snowball",
inherit: Some(&BASE_ENTITY),
fields: &[Field {
name: "item",
typ: Type::Slot,
}],
},
Class {
name: "eye_of_ender",
inherit: Some(&BASE_ENTITY),
fields: &[Field {
name: "item",
typ: Type::Slot,
}],
},
Class {
name: "falling_block",
inherit: Some(&BASE_ENTITY),
fields: &[Field {
name: "spawn_position",
typ: Type::BlockPos(0, 0, 0),
}],
},
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,
},
],
},
Class {
name: "fishing_bobber",
inherit: Some(&BASE_ENTITY),
fields: &[
Field {
name: "hooked_entity",
typ: Type::OptEntityId,
},
Field {
name: "catchable",
typ: Type::Bool(false),
},
],
},
Class {
name: "arrow",
inherit: Some(&ABSTRACT_ARROW),
fields: &[Field {
name: "color",
typ: Type::VarInt(-1), // TODO: custom type
}],
},
Class {
name: "spectral_arrow",
inherit: Some(&ABSTRACT_ARROW),
fields: &[],
},
Class {
name: "trident",
inherit: Some(&ABSTRACT_ARROW),
fields: &[
Field {
name: "loyalty_level",
typ: Type::VarInt(0),
},
Field {
name: "enchantment_glint",
typ: Type::Bool(false),
},
],
},
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: "typ",
typ: Type::BoatVariant,
},
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),
},
],
},
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),
},
],
},
Class {
name: "dragon_fireball",
inherit: Some(&BASE_ENTITY),
fields: &[],
},
Class {
name: "small_fireball",
inherit: Some(&BASE_ENTITY),
fields: &[Field {
name: "item",
typ: Type::Slot,
}],
},
Class {
name: "fireball",
inherit: Some(&BASE_ENTITY),
fields: &[Field {
name: "item",
typ: Type::Slot,
}],
},
Class {
name: "wither_skull",
inherit: Some(&BASE_ENTITY),
fields: &[Field {
name: "invulnerable",
typ: Type::Bool(false),
}],
},
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),
},
],
},
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?
},
],
},
Class {
// TODO: How is glow item frame defined? This is a guess.
name: "glow_item_frame",
inherit: Some(&BASE_ENTITY),
fields: &[
Field {
name: "item",
typ: Type::Slot,
},
Field {
name: "rotation",
typ: Type::VarInt(0), // TODO: Direction enum?
},
],
},
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,
},
],
},
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),
},
],
},
Class {
name: "bat",
inherit: Some(&AMBIENT_CREATURE),
fields: &[Field {
name: "bat_flags",
typ: Type::BitFields(&[BitField {
name: "hanging",
offset: 0,
default: false,
}]),
}],
},
Class {
name: "squid",
inherit: Some(&WATER_ANIMAL),
fields: &[],
},
Class {
// TODO: How is glow squid defined? This is a guess.
name: "glow_squid",
inherit: Some(&WATER_ANIMAL),
fields: &[],
},
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),
},
],
},
Class {
name: "cod",
inherit: Some(&ABSTRACT_FISH),
fields: &[],
},
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?)
}],
},
Class {
name: "salmon",
inherit: Some(&ABSTRACT_FISH),
fields: &[],
},
Class {
name: "tropical_fish",
inherit: Some(&ABSTRACT_FISH),
fields: &[Field {
name: "variant",
typ: Type::VarInt(0), // TODO: TropicalFishVariant enum
}],
},
Class {
name: "horse",
inherit: Some(&ABSTRACT_HORSE),
fields: &[Field {
name: "variant",
typ: Type::VarInt(0), // TODO: HorseVariant enum
}],
},
Class {
name: "zombie_horse",
inherit: Some(&ABSTRACT_HORSE),
fields: &[],
},
Class {
name: "skeleton_horse",
inherit: Some(&ABSTRACT_HORSE),
fields: &[],
},
Class {
name: "donkey",
inherit: Some(&CHESTED_HORSE),
fields: &[],
},
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.
},
],
},
Class {
name: "trader_llama",
inherit: None, // TODO: really?
fields: &[],
},
Class {
name: "mule",
inherit: Some(&CHESTED_HORSE),
fields: &[],
},
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),
},
],
},
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),
},
],
},
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,
},
],
},
Class {
name: "ocelot",
inherit: Some(&ANIMAL),
fields: &[Field {
name: "trusting",
typ: Type::Bool(false),
}],
},
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,
},
]),
},
],
},
Class {
name: "pig",
inherit: Some(&ANIMAL),
fields: &[
Field {
name: "has_saddle",
typ: Type::Bool(false),
},
Field {
name: "boost_timer",
typ: Type::VarInt(0),
},
],
},
Class {
name: "rabbit",
inherit: Some(&ANIMAL),
fields: &[Field {
name: "variant",
typ: Type::VarInt(0), // TODO: rabbit variant enum.
}],
},
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),
},
],
},
Class {
name: "polar_bear",
inherit: Some(&ANIMAL),
fields: &[Field {
name: "standing_up",
typ: Type::Bool(true),
}],
},
Class {
name: "chicken",
inherit: Some(&ANIMAL),
fields: &[],
},
COW,
Class {
name: "hoglin",
inherit: Some(&ANIMAL),
fields: &[Field {
name: "zombification_immune",
typ: Type::Bool(false),
}],
},
Class {
name: "mooshroom",
inherit: Some(&COW),
fields: &[Field {
name: "variant",
typ: Type::String("red"), // TODO: "red" or "brown" enum.
}],
},
Class {
name: "sheep",
inherit: Some(&ANIMAL),
fields: &[Field {
name: "sheep_state",
typ: Type::Byte(0), // TODO: sheep state type.
}],
},
Class {
name: "goat",
inherit: Some(&ANIMAL),
fields: &[], // TODO: What are the goat fields?
},
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),
},
],
},
Class {
name: "cat",
inherit: Some(&TAMEABLE_ANIMAL),
fields: &[
Field {
name: "variant",
typ: Type::VarInt(1), // TODO: cat variant enum.
},
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.
},
],
},
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),
},
],
},
Class {
name: "parrot",
inherit: Some(&TAMEABLE_ANIMAL),
fields: &[Field {
name: "variant",
typ: Type::VarInt(0), // TODO: parrot variant enum.
}],
},
Class {
name: "villager",
inherit: Some(&ABSTRACT_VILLAGER),
fields: &[Field {
name: "villager_data",
typ: Type::VillagerData,
}],
},
Class {
name: "wandering_trader",
inherit: Some(&ABSTRACT_VILLAGER),
fields: &[],
},
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,
}]),
}],
},
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,
}]),
}],
},
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
},
],
},
Class {
// TODO: how is this defined?
name: "shulker_bullet",
inherit: Some(&BASE_ENTITY),
fields: &[],
},
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),
},
],
},
Class {
name: "piglin_brute",
inherit: Some(&BASE_PIGLIN),
fields: &[],
},
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,
}]),
}],
},
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),
},
],
},
Class {
name: "endermite",
inherit: Some(&MONSTER),
fields: &[],
},
Class {
name: "giant",
inherit: Some(&MONSTER),
fields: &[],
},
GUARDIAN,
Class {
name: "elder_guardian",
inherit: Some(&GUARDIAN),
fields: &[],
},
Class {
name: "silverfish",
inherit: Some(&MONSTER),
fields: &[],
},
Class {
name: "vindicator",
inherit: Some(&ABSTRACT_ILLAGER),
fields: &[],
},
Class {
name: "pillager",
inherit: Some(&ABSTRACT_ILLAGER),
fields: &[Field {
name: "charging",
typ: Type::Bool(false),
}],
},
Class {
name: "evoker",
inherit: Some(&SPELLCASTER_ILLAGER),
fields: &[],
},
Class {
name: "illusioner",
inherit: Some(&SPELLCASTER_ILLAGER),
fields: &[],
},
Class {
name: "ravager",
inherit: Some(&RAIDER),
fields: &[],
},
Class {
name: "evoker_fangs",
inherit: Some(&BASE_ENTITY),
fields: &[],
},
Class {
name: "witch",
inherit: Some(&RAIDER),
fields: &[Field {
name: "drinking_potion",
typ: Type::Bool(false),
}],
},
Class {
name: "vex",
inherit: Some(&MONSTER),
fields: &[Field {
name: "vex_flags",
typ: Type::BitFields(&[BitField {
name: "attacking",
offset: 0,
default: false,
}]),
}],
},
Class {
name: "skeleton",
inherit: Some(&ABSTRACT_SKELETON),
fields: &[],
},
Class {
name: "wither_skeleton",
inherit: Some(&ABSTRACT_SKELETON),
fields: &[],
},
Class {
name: "stray",
inherit: Some(&ABSTRACT_SKELETON),
fields: &[],
},
SPIDER,
Class {
name: "cave_spider",
inherit: Some(&SPIDER), // TODO: does cave_spider inherit from spider?
fields: &[],
},
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),
},
],
},
Class {
name: "zoglin",
inherit: Some(&MONSTER),
fields: &[Field {
name: "baby",
typ: Type::Bool(false),
}],
},
ZOMBIE,
Class {
name: "zombie_villager",
inherit: Some(&ZOMBIE),
fields: &[
Field {
name: "converting",
typ: Type::Bool(false),
},
Field {
name: "villager_data",
typ: Type::VillagerData,
},
],
},
Class {
name: "husk",
inherit: Some(&ZOMBIE),
fields: &[],
},
Class {
name: "drowned",
inherit: Some(&ZOMBIE),
fields: &[],
},
Class {
name: "zombified_piglin",
inherit: Some(&ZOMBIE),
fields: &[],
},
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),
},
],
},
Class {
name: "ender_dragon",
inherit: Some(&MOB),
fields: &[Field {
name: "phase",
typ: Type::VarInt(10), // TODO: dragon phase enum
}],
},
Class {
name: "ghast",
inherit: Some(&FLYING),
fields: &[Field {
name: "attacking",
typ: Type::Bool(false),
}],
},
Class {
name: "phantom",
inherit: Some(&FLYING),
fields: &[Field {
name: "size",
typ: Type::VarInt(0),
}],
},
Class {
name: "slime",
inherit: Some(&MOB),
fields: &[Field {
name: "size",
typ: Type::VarInt(1), // TODO: bounds?
}],
},
Class {
name: "magma_cube",
inherit: Some(&MOB),
fields: &[Field {
name: "size",
typ: Type::VarInt(1),
}],
},
Class {
name: "llama_spit",
inherit: Some(&BASE_ENTITY),
fields: &[],
},
Class {
name: "minecart",
inherit: Some(&ABSTRACT_MINECART),
fields: &[],
},
Class {
name: "hopper_minecart",
inherit: Some(&ABSTRACT_MINECART_CONTAINER),
fields: &[],
},
Class {
name: "chest_minecart",
inherit: Some(&ABSTRACT_MINECART_CONTAINER),
fields: &[],
},
Class {
name: "furnace_minecart",
inherit: Some(&ABSTRACT_MINECART),
fields: &[Field {
name: "has_fuel",
typ: Type::Bool(false),
}],
},
Class {
name: "tnt_minecart",
inherit: Some(&ABSTRACT_MINECART),
fields: &[],
},
Class {
name: "spawner_minecart",
inherit: Some(&ABSTRACT_MINECART),
fields: &[],
},
Class {
name: "command_block_minecart",
inherit: Some(&ABSTRACT_MINECART),
fields: &[
Field {
name: "command",
typ: Type::String(""),
},
Field {
name: "last_output",
typ: Type::Text,
},
],
},
Class {
name: "tnt",
inherit: Some(&BASE_ENTITY),
fields: &[Field {
name: "fuse_timer",
typ: Type::VarInt(80),
}],
},
];
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,
}
let json_entities: Vec<JsonEntity> =
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"))?,
);
}
assert_eq!(json_entities.len(), entities.len());
res
};
let mut all_classes = BTreeMap::new();
for mut class in entities.iter().cloned() {
while let None = all_classes.insert(class.name, class) {
match class.inherit {
Some(parent) => class = parent,
None => break,
}
}
}
let entity_type_variants = entities
.iter()
.map(|c| ident(c.name.to_pascal_case()))
.collect::<Vec<_>>();
/*
let set_type_arms = entities.iter().map(|&entity| {
let entity_name = ident(entity.name.to_pascal_case());
let mut old_fields = Vec::new();
collect_class_fields(entity, &mut old_fields);
let new_type_arms = entities.iter().map(|&new_entity| {
let new_entity_name = ident(new_entity.name.to_pascal_case());
let mut new_fields = Vec::new();
collect_class_fields(new_entity, &mut new_fields);
let assign_fields = new_fields
.iter()
.cloned()
.filter(|&new_field| old_fields.iter().any(|&f| f.name == new_field.name))
.map(|new_field| {
let name = ident(new_field.name.to_snake_case());
quote! {
new.#name = old.#name;
}
});
quote! {
EntityType::#new_entity_name => {
let mut new = #new_entity_name::new();
#(#assign_fields)*
*self = Self::#new_entity_name(new);
}
}
});
quote! {
Self::#entity_name(old) => match new_type {
#(#new_type_arms)*
},
}
});
*/
let entity_structs = entities.iter().map(|&class| {
let mut fields = Vec::new();
collect_class_fields(class, &mut fields);
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<str> },
Type::Text => quote! { Box<Text> },
Type::OptText(_) => quote! { Option<Box<Text>> },
Type::Slot => quote! { () }, // TODO
Type::Bool(_) => quote! { bool },
Type::ArmorStandRotations(_, _, _) => quote! { ArmorStandRotations },
Type::BlockPos(_, _, _) => quote! { BlockPos },
Type::OptBlockPos(_) => quote! { Option<BlockPos> },
Type::Direction => quote! { Direction },
Type::OptUuid => quote! { Option<Uuid> },
Type::BlockState => quote! { BlockState },
Type::Nbt => quote! { nbt::Blob },
Type::Particle => quote! { () }, // TODO
Type::VillagerData => quote! { VillagerData },
Type::Pose => quote! { Pose },
Type::OptEntityId => quote! { Option<EntityId> },
Type::BoatVariant => quote! { BoatVariant },
Type::MainHand => quote! { MainHand },
};
quote! {
#name: #typ,
}
});
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.to_string()));
let setter_name = ident(format!("set_{}", name.to_string()));
let field_offset = field_offset as u32;
// TODO: documentation on methods.
let standard_getter_setter = |type_name: TokenStream| quote! {
pub fn #getter_name(&self) -> #type_name {
self.#name
}
pub fn #setter_name(&mut self, #name: #type_name) {
if self.#name != #name {
self.modified_flags |= 1 << #field_offset;
}
self.#name = #name;
}
};
match field.typ {
Type::BitFields(bfs) => bfs
.iter()
.map(|bf| {
let bit_name = ident(bf.name.to_snake_case());
let getter_name = ident(format!("get_{}", bit_name.to_string()));
let setter_name = ident(format!("set_{}", bit_name.to_string()));
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;
}
}
}
})
.collect(),
Type::Byte(_) => standard_getter_setter(quote!(u8)),
Type::VarInt(_) => quote! {
pub fn #getter_name(&self) -> i32 {
self.#name.0
}
pub fn #setter_name(&mut self, #name: i32) {
if self.#name.0 != #name {
self.modified_flags |= 1 << #field_offset;
}
self.#name = VarInt(#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<Box<str>>) {
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<Text>) {
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<impl Into<Text>>) {
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<BlockPos>)),
Type::Direction => standard_getter_setter(quote!(Direction)),
Type::OptUuid => standard_getter_setter(quote!(Option<Uuid>)),
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::Pose => standard_getter_setter(quote!(Pose)),
Type::OptEntityId => standard_getter_setter(quote!(Option<EntityId>)),
Type::BoatVariant => standard_getter_setter(quote!(BoatVariant)),
Type::MainHand => standard_getter_setter(quote!(MainHand)),
}
})
.collect::<TokenStream>();
quote! {
pub struct #name {
/// Contains a set bit for each modified field.
modified_flags: u32,
#(#struct_fields)*
}
impl #name {
pub(super) fn new() -> Self {
Self {
modified_flags: 0,
#(#constructor_fields)*
}
}
#getter_setters
}
}
});
let initial_metadata_arms = entities.iter().map(|&entity| {
let name = ident(entity.name.to_pascal_case());
let mut fields = Vec::new();
collect_class_fields(entity, &mut fields);
let check_fields = fields.into_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 m.#name != #default {
data.push(#index);
VarInt(#type_id).encode(&mut data).unwrap();
m.#name.encode(&mut data).unwrap();
}
}
});
quote! {
Self::#name(m) => {
#(#check_fields)*
}
}
});
let updated_metadata_arms = entities.iter().map(|&entity| {
let name = ident(entity.name.to_pascal_case());
let mut fields = Vec::new();
collect_class_fields(entity, &mut fields);
let update_fields = fields.into_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 (m.modified_flags >> #u32_index) & 1 == 1 {
data.push(#u8_index);
VarInt(#type_id).encode(&mut data).unwrap();
m.#name.encode(&mut data).unwrap();
}
}
});
quote! {
Self::#name(m) => {
if m.modified_flags == 0 {
return None;
}
#(#update_fields)*
}
}
});
let finished = quote! {
/// Identifies a type of entity, such as `chicken`, `zombie` or `item`.
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
pub enum EntityType {
#(#entity_type_variants,)*
}
impl Default for EntityType {
fn default() -> Self {
Self::Marker
}
}
#(#entity_structs)*
/// An enum encoding the type of n entity along with its metadata.
///
/// Metadata encompases most of an entity's state, except for some
/// basic pieces of information such as position and rotation.
pub enum EntityMeta {
#(#entity_type_variants(#entity_type_variants),)*
}
impl EntityMeta {
pub(super) fn new(typ: EntityType) -> Self {
match typ {
#(EntityType::#entity_type_variants => Self::#entity_type_variants(#entity_type_variants::new()),)*
}
}
pub(super) fn typ(&self) -> EntityType {
match self {
#(Self::#entity_type_variants(_) => EntityType::#entity_type_variants,)*
}
}
pub(super) fn initial_metadata(&self) -> Option<Vec<u8>> {
let mut data = Vec::new();
match self {
#(#initial_metadata_arms)*
}
if data.is_empty() {
None
} else {
data.push(0xff);
Some(data)
}
}
pub(super) fn updated_metadata(&self) -> Option<Vec<u8>> {
let mut data = Vec::new();
match self {
#(#updated_metadata_arms)*
}
if data.is_empty() {
None
} else {
data.push(0xff);
Some(data)
}
}
pub(super) fn clear_modifications(&mut self) {
match self {
#(Self::#entity_type_variants(m) => m.modified_flags = 0,)*
}
}
}
};
write_to_out_path("entity.rs", &finished.to_string())
}
fn collect_class_fields(class: &Class, fields: &mut Vec<&'static Field>) {
if let Some(parent) = class.inherit {
collect_class_fields(parent, fields);
}
fields.extend(class.fields);
}