aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormat <git@matdoes.dev>2025-10-07 06:01:19 +0400
committermat <git@matdoes.dev>2025-10-07 06:01:19 +0400
commit1cf6d92f1a8be954f6885704c31f317b99b05972 (patch)
treeab241a7b6fda815e777b914a8a9a153d23e81f06
parent06807ec3ea7df6e83eed51b38f9d5e3bea9e7045 (diff)
downloadazalea-drasl-1cf6d92f1a8be954f6885704c31f317b99b05972.tar.xz
update ResolvableProfile data component to 1.21.9
-rw-r--r--Cargo.lock1
-rw-r--r--azalea-auth/src/game_profile.rs115
-rw-r--r--azalea-client/src/client.rs16
-rw-r--r--azalea-client/src/plugins/packet/config/events.rs4
-rw-r--r--azalea-client/tests/mine_block_rollback.rs1
-rw-r--r--azalea-client/tests/mine_block_timing.rs1
-rw-r--r--azalea-client/tests/mine_block_without_rollback.rs1
-rw-r--r--azalea-client/tests/packet_order_set_carried_item.rs3
-rw-r--r--azalea-entity/src/data.rs51
-rw-r--r--azalea-entity/src/metadata.rs7
-rw-r--r--azalea-inventory/Cargo.toml1
-rw-r--r--azalea-inventory/src/components/mod.rs (renamed from azalea-inventory/src/components.rs)23
-rw-r--r--azalea-inventory/src/components/profile.rs73
-rw-r--r--codegen/lib/code/data_components.py8
-rw-r--r--codegen/lib/code/entity.py5
15 files changed, 170 insertions, 140 deletions
diff --git a/Cargo.lock b/Cargo.lock
index e19a8279..6a46064c 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -436,6 +436,7 @@ dependencies = [
name = "azalea-inventory"
version = "0.14.0+mc1.21.9"
dependencies = [
+ "azalea-auth",
"azalea-buf",
"azalea-chat",
"azalea-core",
diff --git a/azalea-auth/src/game_profile.rs b/azalea-auth/src/game_profile.rs
index c944bcc4..4f5bccfd 100644
--- a/azalea-auth/src/game_profile.rs
+++ b/azalea-auth/src/game_profile.rs
@@ -4,14 +4,14 @@ use std::{
};
use azalea_buf::{
- AzaleaRead, AzaleaReadLimited, AzaleaReadVar, AzaleaWrite, AzaleaWriteVar, BufReadError,
+ AzBuf, AzaleaRead, AzaleaReadLimited, AzaleaReadVar, AzaleaWrite, AzaleaWriteVar, BufReadError,
};
use indexmap::IndexMap;
-use serde::{Deserialize, Serialize};
+use serde::{Deserialize, Serialize, Serializer};
use uuid::Uuid;
/// Information about the player that's usually stored on Mojang's servers.
-#[derive(Debug, Clone, Default, Eq, PartialEq)]
+#[derive(Debug, Clone, Default, Eq, PartialEq, AzBuf)]
pub struct GameProfile {
/// The UUID of the player.
///
@@ -27,26 +27,6 @@ pub struct GameProfile {
/// This is an `Arc` to make it cheaper to clone.
pub properties: Arc<GameProfileProperties>,
}
-impl AzaleaRead for GameProfile {
- fn azalea_read(buf: &mut io::Cursor<&[u8]>) -> Result<Self, BufReadError> {
- let uuid = Uuid::azalea_read(buf)?;
- let name = String::azalea_read(buf)?;
- let properties = GameProfileProperties::azalea_read(buf)?;
- Ok(GameProfile {
- uuid,
- name,
- properties: Arc::new(properties),
- })
- }
-}
-impl AzaleaWrite for GameProfile {
- fn azalea_write(&self, buf: &mut impl Write) -> io::Result<()> {
- self.uuid.azalea_write(buf)?;
- self.name.azalea_write(buf)?;
- self.properties.azalea_write(buf)?;
- Ok(())
- }
-}
impl GameProfile {
pub fn new(uuid: Uuid, name: String) -> Self {
@@ -60,20 +40,10 @@ impl GameProfile {
impl From<SerializableGameProfile> for GameProfile {
fn from(value: SerializableGameProfile) -> Self {
- let mut properties = IndexMap::new();
- for value in value.properties {
- properties.insert(
- value.name,
- ProfilePropertyValue {
- value: value.value,
- signature: value.signature,
- },
- );
- }
Self {
- uuid: value.id,
- name: value.name,
- properties: Arc::new(GameProfileProperties { map: properties }),
+ uuid: value.id.unwrap_or_default(),
+ name: value.name.unwrap_or_default(),
+ properties: Arc::new(value.properties.into()),
}
}
}
@@ -134,36 +104,81 @@ impl AzaleaWrite for ProfilePropertyValue {
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SerializableGameProfile {
- pub id: Uuid,
- pub name: String,
- pub properties: Vec<SerializableProfilePropertyValue>,
+ #[serde(default)]
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub id: Option<Uuid>,
+ #[serde(default)]
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub name: Option<String>,
+ #[serde(default)]
+ #[serde(skip_serializing_if = "SerializableProfileProperties::is_empty")]
+ pub properties: SerializableProfileProperties,
}
impl From<GameProfile> for SerializableGameProfile {
fn from(value: GameProfile) -> Self {
- let mut properties = Vec::new();
- for (key, value) in &value.properties.map {
- properties.push(SerializableProfilePropertyValue {
- name: key.clone(),
- value: value.value.clone(),
- signature: value.signature.clone(),
- });
- }
Self {
- id: value.uuid,
- name: value.name,
- properties,
+ id: Some(value.uuid),
+ name: Some(value.name),
+ properties: (*value.properties).clone().into(),
}
}
}
+#[derive(Debug, Clone, Serialize, Deserialize, Default)]
+#[serde(transparent)]
+pub struct SerializableProfileProperties {
+ pub list: Vec<SerializableProfilePropertyValue>,
+}
+impl SerializableProfileProperties {
+ pub fn is_empty(&self) -> bool {
+ self.list.is_empty()
+ }
+}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SerializableProfilePropertyValue {
pub name: String,
pub value: String,
+ #[serde(default)]
+ #[serde(skip_serializing_if = "Option::is_none")]
pub signature: Option<String>,
}
+impl From<GameProfileProperties> for SerializableProfileProperties {
+ fn from(value: GameProfileProperties) -> Self {
+ let mut list = Vec::new();
+ for (name, entry) in value.map {
+ list.push(SerializableProfilePropertyValue {
+ name,
+ value: entry.value,
+ signature: entry.signature,
+ });
+ }
+ Self { list }
+ }
+}
+impl From<SerializableProfileProperties> for GameProfileProperties {
+ fn from(value: SerializableProfileProperties) -> Self {
+ let mut map = IndexMap::new();
+ for entry in value.list {
+ map.insert(
+ entry.name,
+ ProfilePropertyValue {
+ value: entry.value,
+ signature: entry.signature,
+ },
+ );
+ }
+ Self { map }
+ }
+}
+impl Serialize for GameProfile {
+ fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
+ let serializable = SerializableGameProfile::from(self.clone());
+ serializable.serialize(serializer)
+ }
+}
+
#[cfg(test)]
mod tests {
use super::*;
diff --git a/azalea-client/src/client.rs b/azalea-client/src/client.rs
index bb827838..cf07f8b2 100644
--- a/azalea-client/src/client.rs
+++ b/azalea-client/src/client.rs
@@ -261,12 +261,13 @@ impl Client {
/// return it.
///
///
- /// If the component can't be cloned, try [`Self::map_component`] instead.
- /// If it isn't guaranteed to be present, use [`Self::get_component`] or
- /// [`Self::map_get_component`].
+ /// If the component can't be cloned, try [`Self::query_self`] instead.
+ /// If it isn't guaranteed to be present, you can use
+ /// [`Self::get_component`] or [`Self::query_self`].
///
- /// You may also use [`Self::ecs`] and [`Self::query`] directly if you need
- /// more control over when the ECS is locked.
+ ///
+ /// You may also use [`Self::ecs`] directly if you need more control over
+ /// when the ECS is locked.
///
/// # Panics
///
@@ -285,9 +286,10 @@ impl Client {
/// Get a component from this client, or `None` if it doesn't exist.
///
- /// If the component can't be cloned, try [`Self::map_component`] instead.
+ /// If the component can't be cloned, consider using [`Self::query_self`]
+ /// with `Option<&T>` instead.
///
- /// You may also have to use [`Self::with_query`] directly.
+ /// You may also have to use [`Self::query_self`] directly.
pub fn get_component<T: Component + Clone>(&self) -> Option<T> {
self.query_self::<Option<&T>, _>(|t| t.cloned())
}
diff --git a/azalea-client/src/plugins/packet/config/events.rs b/azalea-client/src/plugins/packet/config/events.rs
index be5cfdb1..92df8d74 100644
--- a/azalea-client/src/plugins/packet/config/events.rs
+++ b/azalea-client/src/plugins/packet/config/events.rs
@@ -54,10 +54,10 @@ pub fn handle_outgoing_packets_observer(
/// A Bevy trigger that's sent when our client receives a [`ClientboundPing`]
/// packet in the config state.
///
-/// Also see [`PingEvent`].
+/// Also see [`GamePingEvent`].
///
/// [`ClientboundPing`]: azalea_protocol::packets::config::ClientboundPing
-/// [`PingEvent`]: crate::packet::game::PingEvent
+/// [`GamePingEvent`]: crate::packet::game::GamePingEvent
#[derive(Event, Debug, Clone)]
pub struct ConfigPingEvent {
pub entity: Entity,
diff --git a/azalea-client/tests/mine_block_rollback.rs b/azalea-client/tests/mine_block_rollback.rs
index 67a66169..44ad629f 100644
--- a/azalea-client/tests/mine_block_rollback.rs
+++ b/azalea-client/tests/mine_block_rollback.rs
@@ -30,6 +30,7 @@ fn test_mine_block_rollback() {
simulation.write_message(StartMiningBlockEvent {
entity: simulation.entity,
position: pos,
+ force: true,
});
simulation.tick();
assert_eq!(simulation.get_block_state(pos), Some(Block::Air.into()));
diff --git a/azalea-client/tests/mine_block_timing.rs b/azalea-client/tests/mine_block_timing.rs
index 1cbd2f61..6548b28a 100644
--- a/azalea-client/tests/mine_block_timing.rs
+++ b/azalea-client/tests/mine_block_timing.rs
@@ -57,6 +57,7 @@ fn test_mine_block_timing() {
simulation.write_message(StartMiningBlockEvent {
entity: simulation.entity,
position: pos,
+ force: false,
});
sent_packets.clear();
simulation.tick();
diff --git a/azalea-client/tests/mine_block_without_rollback.rs b/azalea-client/tests/mine_block_without_rollback.rs
index 16a371e5..a5a437a0 100644
--- a/azalea-client/tests/mine_block_without_rollback.rs
+++ b/azalea-client/tests/mine_block_without_rollback.rs
@@ -29,6 +29,7 @@ fn test_mine_block_without_rollback() {
simulation.write_message(StartMiningBlockEvent {
entity: simulation.entity,
position: pos,
+ force: true,
});
simulation.tick();
assert_eq!(simulation.get_block_state(pos), Some(Block::Air.into()));
diff --git a/azalea-client/tests/packet_order_set_carried_item.rs b/azalea-client/tests/packet_order_set_carried_item.rs
index 0506ad8e..a357dec0 100644
--- a/azalea-client/tests/packet_order_set_carried_item.rs
+++ b/azalea-client/tests/packet_order_set_carried_item.rs
@@ -55,13 +55,14 @@ fn test_packet_order_set_carried_item() {
simulation.tick();
simulation.tick();
- simulation.write_message(SetSelectedHotbarSlotEvent {
+ simulation.trigger(SetSelectedHotbarSlotEvent {
entity: simulation.entity,
slot: 1,
});
simulation.write_message(StartMiningBlockEvent {
entity: simulation.entity,
position: pos,
+ force: false,
});
sent_packets.clear();
diff --git a/azalea-entity/src/data.rs b/azalea-entity/src/data.rs
index 84a5b153..65267c4a 100644
--- a/azalea-entity/src/data.rs
+++ b/azalea-entity/src/data.rs
@@ -2,15 +2,13 @@
use std::io::{self, Cursor, Write};
-use azalea_auth::game_profile::{GameProfile, GameProfileProperties};
use azalea_buf::{AzBuf, AzaleaRead, AzaleaReadVar, AzaleaWrite, AzaleaWriteVar, BufReadError};
use azalea_chat::FormattedText;
use azalea_core::{
direction::Direction,
position::{BlockPos, GlobalPos, Vec3f32},
- resource_location::ResourceLocation,
};
-use azalea_inventory::ItemStack;
+use azalea_inventory::{ItemStack, components};
use bevy_ecs::component::Component;
use derive_more::Deref;
use enum_as_inner::EnumAsInner;
@@ -97,7 +95,7 @@ pub enum EntityDataValue {
WeatheringCopperState(WeatheringCopperStateKind),
Vector3(Vec3f32),
Quaternion(Quaternion),
- ResolvableProfile(ResolvableProfile),
+ ResolvableProfile(components::Profile),
}
#[derive(Clone, Debug, PartialEq)]
@@ -111,51 +109,6 @@ pub struct Quaternion {
pub w: f32,
}
-#[derive(Clone, Debug, AzBuf, Default, PartialEq)]
-pub struct ResolvableProfile {
- pub unpack: Box<PartialOrFullProfile>,
- pub skin_patch: Box<PlayerSkinPatch>,
-}
-
-#[derive(Clone, Debug, AzBuf, PartialEq)]
-pub enum PartialOrFullProfile {
- Partial(PartialProfile),
- Full(GameProfile),
-}
-impl Default for PartialOrFullProfile {
- fn default() -> Self {
- Self::Partial(PartialProfile::default())
- }
-}
-
-#[derive(Clone, Debug, AzBuf, Default, PartialEq)]
-pub struct PartialProfile {
- #[limit(16)]
- pub name: Option<String>,
- pub id: Option<Uuid>,
- pub properties: GameProfileProperties,
-}
-
-#[derive(Clone, Debug, AzBuf, Default, PartialEq)]
-pub struct PlayerSkinPatch {
- pub body: Option<ResourceTexture>,
- pub cape: Option<ResourceTexture>,
- pub elytra: Option<ResourceTexture>,
- pub model: Option<PlayerModelType>,
-}
-
-#[derive(Clone, Debug, Copy, AzBuf, Default, PartialEq)]
-pub enum PlayerModelType {
- #[default]
- Wide,
- Slim,
-}
-
-#[derive(Clone, Debug, AzBuf, PartialEq)]
-pub struct ResourceTexture {
- pub id: ResourceLocation,
-}
-
// mojang just calls this ArmadilloState but i added "Kind" since otherwise it
// collides with a name in metadata.rs
#[derive(Clone, Debug, Copy, Default, AzBuf, PartialEq)]
diff --git a/azalea-entity/src/metadata.rs b/azalea-entity/src/metadata.rs
index 56455bb7..68b8b78c 100644
--- a/azalea-entity/src/metadata.rs
+++ b/azalea-entity/src/metadata.rs
@@ -8,7 +8,7 @@ use azalea_core::{
direction::Direction,
position::{BlockPos, Vec3f32},
};
-use azalea_inventory::ItemStack;
+use azalea_inventory::{ItemStack, components};
use azalea_registry::DataRegistry;
use bevy_ecs::{bundle::Bundle, component::Component};
use derive_more::{Deref, DerefMut};
@@ -17,8 +17,7 @@ use uuid::Uuid;
use super::{
ArmadilloStateKind, CopperGolemStateKind, EntityDataItem, EntityDataValue, OptionalUnsignedInt,
- Pose, Quaternion, ResolvableProfile, Rotations, SnifferStateKind, VillagerData,
- WeatheringCopperStateKind,
+ Pose, Quaternion, Rotations, SnifferStateKind, VillagerData, WeatheringCopperStateKind,
};
use crate::particle::Particle;
@@ -6054,7 +6053,7 @@ pub struct MannequinPlayerMainHand(pub u8);
#[derive(Component, Deref, DerefMut, Clone, PartialEq)]
pub struct MannequinPlayerModeCustomisation(pub u8);
#[derive(Component, Deref, DerefMut, Clone, PartialEq)]
-pub struct Profile(pub ResolvableProfile);
+pub struct Profile(pub components::Profile);
#[derive(Component, Deref, DerefMut, Clone, PartialEq)]
pub struct Immovable(pub bool);
#[derive(Component, Deref, DerefMut, Clone, PartialEq)]
diff --git a/azalea-inventory/Cargo.toml b/azalea-inventory/Cargo.toml
index d10aa787..8fb1d625 100644
--- a/azalea-inventory/Cargo.toml
+++ b/azalea-inventory/Cargo.toml
@@ -7,6 +7,7 @@ license.workspace = true
repository.workspace = true
[dependencies]
+azalea-auth.workspace = true
azalea-buf.workspace = true
azalea-chat = { workspace = true, features = ["azalea-buf"] }
azalea-core.workspace = true
diff --git a/azalea-inventory/src/components.rs b/azalea-inventory/src/components/mod.rs
index b820afc9..5661547a 100644
--- a/azalea-inventory/src/components.rs
+++ b/azalea-inventory/src/components/mod.rs
@@ -1,3 +1,5 @@
+mod profile;
+
use core::f64;
use std::{
any::Any,
@@ -21,10 +23,10 @@ use azalea_registry::{
self as registry, Attribute, Block, DamageKind, DataComponentKind, Enchantment, EntityKind,
Holder, HolderSet, Item, MobEffect, Potion, SoundEvent, TrimMaterial, TrimPattern,
};
+pub use profile::*;
use serde::{Serialize, ser::SerializeMap};
use simdnbt::owned::{Nbt, NbtCompound};
use tracing::trace;
-use uuid::Uuid;
use crate::{ItemStack, item::consume_effect::ConsumeEffect};
@@ -835,25 +837,6 @@ impl Default for Fireworks {
}
#[derive(Clone, PartialEq, AzBuf, Debug, Serialize)]
-pub struct GameProfileProperty {
- pub name: String,
- pub value: String,
- #[serde(skip_serializing_if = "is_default")]
- pub signature: Option<String>,
-}
-
-#[derive(Clone, PartialEq, AzBuf, Debug, Serialize)]
-pub struct Profile {
- #[serde(skip_serializing_if = "is_default")]
- pub name: Option<String>,
- #[serde(skip_serializing_if = "is_default")]
- #[serde(serialize_with = "azalea_core::codec_utils::uuid")]
- pub id: Option<Uuid>,
- #[serde(skip_serializing_if = "is_default")]
- pub properties: Vec<GameProfileProperty>,
-}
-
-#[derive(Clone, PartialEq, AzBuf, Debug, Serialize)]
#[serde(transparent)]
pub struct NoteBlockSound {
pub sound: ResourceLocation,
diff --git a/azalea-inventory/src/components/profile.rs b/azalea-inventory/src/components/profile.rs
new file mode 100644
index 00000000..9966bc8f
--- /dev/null
+++ b/azalea-inventory/src/components/profile.rs
@@ -0,0 +1,73 @@
+use azalea_auth::game_profile::{
+ GameProfile, GameProfileProperties, SerializableProfileProperties,
+};
+use azalea_buf::AzBuf;
+use azalea_core::{codec_utils::*, resource_location::ResourceLocation};
+use serde::{Serialize, Serializer};
+use uuid::Uuid;
+
+#[derive(Clone, Debug, AzBuf, Default, PartialEq, Serialize)]
+#[doc(alias = "ResolvableProfile")]
+pub struct Profile {
+ #[serde(flatten)]
+ pub unpack: Box<PartialOrFullProfile>,
+ #[serde(flatten)]
+ pub skin_patch: Box<PlayerSkinPatch>,
+}
+
+#[derive(Clone, Debug, AzBuf, PartialEq, Serialize)]
+#[serde(untagged)]
+pub enum PartialOrFullProfile {
+ Partial(PartialProfile),
+ Full(GameProfile),
+}
+impl Default for PartialOrFullProfile {
+ fn default() -> Self {
+ Self::Partial(PartialProfile::default())
+ }
+}
+
+#[derive(Clone, Debug, AzBuf, Default, PartialEq, Serialize)]
+pub struct PartialProfile {
+ #[limit(16)]
+ #[serde(skip_serializing_if = "is_default")]
+ pub name: Option<String>,
+ #[serde(skip_serializing_if = "is_default")]
+ pub id: Option<Uuid>,
+ #[serde(serialize_with = "serialize_properties")]
+ pub properties: GameProfileProperties,
+}
+fn serialize_properties<S: Serializer>(
+ properties: &GameProfileProperties,
+ serializer: S,
+) -> Result<S::Ok, S::Error> {
+ let serializable = SerializableProfileProperties::from(properties.clone());
+ serializable.serialize(serializer)
+}
+
+#[derive(Clone, Debug, AzBuf, Default, PartialEq, Serialize)]
+pub struct PlayerSkinPatch {
+ #[serde(rename = "texture")]
+ #[serde(skip_serializing_if = "is_default")]
+ pub body: Option<ResourceTexture>,
+ #[serde(skip_serializing_if = "is_default")]
+ pub cape: Option<ResourceTexture>,
+ #[serde(skip_serializing_if = "is_default")]
+ pub elytra: Option<ResourceTexture>,
+ #[serde(skip_serializing_if = "is_default")]
+ pub model: Option<PlayerModelType>,
+}
+
+#[derive(Clone, Debug, Copy, AzBuf, Default, PartialEq, Serialize)]
+#[serde(rename_all = "snake_case")]
+pub enum PlayerModelType {
+ #[default]
+ Wide,
+ Slim,
+}
+
+#[derive(Clone, Debug, AzBuf, PartialEq, Serialize)]
+#[serde(transparent)]
+pub struct ResourceTexture {
+ pub id: ResourceLocation,
+}
diff --git a/codegen/lib/code/data_components.py b/codegen/lib/code/data_components.py
index b1444860..89796a91 100644
--- a/codegen/lib/code/data_components.py
+++ b/codegen/lib/code/data_components.py
@@ -4,7 +4,7 @@ import lib.extract
import lib.utils
-DATA_COMPONENTS_DIR = "azalea-inventory/src/components.rs"
+DATA_COMPONENTS_DIR = "azalea-inventory/src/components/mod.rs"
DEFAULT_DATA_COMPONENTS_DIR = "azalea-inventory/src/default_components/generated.rs"
@@ -322,7 +322,7 @@ use crate::{
entity_id = python_value["id"]
elif isinstance(python_value, str):
entity_id = python_value
-
+
if entity_id and entity_id.startswith("minecraft:"):
entity_name = entity_id[10:] # Remove "minecraft:" prefix
entity_name_camel = lib.utils.to_camel_case(entity_name)
@@ -512,7 +512,7 @@ use crate::{
# Special handling for EntityData to use EntityData structure
# Keep rust_value as "value" so it gets processed correctly
field_type = "EntityKind"
-
+
def transform_value_fn(rust_value: str):
return f"{component_struct_name} {{ kind: {rust_value}, data: NbtCompound::new() }}"
@@ -623,7 +623,7 @@ use crate::{
def get_enum_and_struct_fields():
"""
Returns a map like map like `{ "MaxStackSize": { "count": i32 }, "Rarity": [ "common", ... ], ... }`
- with an entry for each struct in components.rs.
+ with an entry for each struct in components/mod.rs.
"""
with open(DATA_COMPONENTS_DIR, "r") as f:
diff --git a/codegen/lib/code/entity.py b/codegen/lib/code/entity.py
index 1c2a0d48..18a7a04b 100644
--- a/codegen/lib/code/entity.py
+++ b/codegen/lib/code/entity.py
@@ -113,7 +113,7 @@ use azalea_core::{
direction::Direction,
position::{BlockPos, Vec3f32},
};
-use azalea_inventory::ItemStack;
+use azalea_inventory::{ItemStack, components};
use azalea_registry::DataRegistry;
use bevy_ecs::{bundle::Bundle, component::Component};
use derive_more::{Deref, DerefMut};
@@ -122,8 +122,7 @@ use uuid::Uuid;
use super::{
ArmadilloStateKind, CopperGolemStateKind, EntityDataItem, EntityDataValue, OptionalUnsignedInt,
- Pose, Quaternion, ResolvableProfile, Rotations, SnifferStateKind, VillagerData,
- WeatheringCopperStateKind,
+ Pose, Quaternion, Rotations, SnifferStateKind, VillagerData, WeatheringCopperStateKind,
};
use crate::particle::Particle;