aboutsummaryrefslogtreecommitdiff
path: root/azalea-inventory/src
diff options
context:
space:
mode:
authormat <27899617+mat-1@users.noreply.github.com>2024-04-23 10:34:50 -0500
committerGitHub <noreply@github.com>2024-04-23 10:34:50 -0500
commit1d80f531b74bc3b31023753acb81b35efcdadd73 (patch)
tree675635c7c41fbb456e3e0dd7b9f09c7211d356f0 /azalea-inventory/src
parent0ddad8bd9c7c0e8846aec8bc90c95416418c9a63 (diff)
downloadazalea-drasl-1d80f531b74bc3b31023753acb81b35efcdadd73.tar.xz
1.20.5 (#127)
* 23w51b * make recalculate_near_end_of_path public so other plugins can do .after(recalculate_near_end_of_path) * update to 24w03a i think * start implementing 24w13a * registries work (but a lot of packets are still broken) * fix recipes and commands packets * i love codecs :D i am not going insane :D mojang's java is very readable :D * item components are "implemented" meowmeowmeowmeowmeowmeowmeowmeowmeowmeowmeowmeowmeowmeowmeowmeowmeowmeow * update to 1.20.5-pre3 * fix all the broken packets and clippy (mojang please don't do an update like this again or i will murder someone) * 1.20.5-rc1 * fix failing tests * 1.20.5
Diffstat (limited to 'azalea-inventory/src')
-rw-r--r--azalea-inventory/src/components.rs656
-rw-r--r--azalea-inventory/src/item/mod.rs4
-rw-r--r--azalea-inventory/src/lib.rs4
-rw-r--r--azalea-inventory/src/operations.rs10
-rw-r--r--azalea-inventory/src/slot.rs172
5 files changed, 817 insertions, 29 deletions
diff --git a/azalea-inventory/src/components.rs b/azalea-inventory/src/components.rs
new file mode 100644
index 00000000..6f9ea527
--- /dev/null
+++ b/azalea-inventory/src/components.rs
@@ -0,0 +1,656 @@
+use core::f64;
+use std::{any::Any, collections::HashMap, io::Cursor};
+
+use azalea_buf::{BufReadError, McBuf, McBufReadable, McBufWritable};
+use azalea_chat::FormattedText;
+use azalea_core::{position::GlobalPos, resource_location::ResourceLocation};
+use azalea_registry::{
+ Attribute, Block, DataComponentKind, Enchantment, HolderSet, Item, MobEffect, Potion,
+ TrimMaterial, TrimPattern,
+};
+use simdnbt::owned::{Nbt, NbtCompound};
+use uuid::Uuid;
+
+use crate::ItemSlot;
+
+pub trait DataComponent: Send + Sync + Any {}
+
+pub trait EncodableDataComponent: Send + Sync + Any {
+ fn encode(&self, buf: &mut Vec<u8>) -> Result<(), std::io::Error>;
+ // using the Clone trait makes it not be object-safe, so we have our own clone
+ // function instead
+ fn clone(&self) -> Box<dyn EncodableDataComponent>;
+ // same deal here
+ fn eq(&self, other: Box<dyn EncodableDataComponent>) -> bool;
+}
+
+impl<T> EncodableDataComponent for T
+where
+ T: DataComponent + Clone + McBufWritable + McBufReadable + PartialEq,
+{
+ fn encode(&self, buf: &mut Vec<u8>) -> Result<(), std::io::Error> {
+ self.write_into(buf)
+ }
+ fn clone(&self) -> Box<dyn EncodableDataComponent> {
+ let cloned = self.clone();
+ Box::new(cloned)
+ }
+ fn eq(&self, other: Box<dyn EncodableDataComponent>) -> bool {
+ let other_any: Box<dyn Any> = other;
+ if let Some(other) = other_any.downcast_ref::<T>() {
+ self == other
+ } else {
+ false
+ }
+ }
+}
+
+pub fn from_kind(
+ kind: azalea_registry::DataComponentKind,
+ buf: &mut Cursor<&[u8]>,
+) -> Result<Box<dyn EncodableDataComponent>, BufReadError> {
+ Ok(match kind {
+ DataComponentKind::CustomData => Box::new(CustomData::read_from(buf)?),
+ DataComponentKind::MaxStackSize => Box::new(MaxStackSize::read_from(buf)?),
+ DataComponentKind::MaxDamage => Box::new(MaxDamage::read_from(buf)?),
+ DataComponentKind::Damage => Box::new(Damage::read_from(buf)?),
+ DataComponentKind::Unbreakable => Box::new(Unbreakable::read_from(buf)?),
+ DataComponentKind::CustomName => Box::new(CustomName::read_from(buf)?),
+ DataComponentKind::ItemName => Box::new(ItemName::read_from(buf)?),
+ DataComponentKind::Lore => Box::new(Lore::read_from(buf)?),
+ DataComponentKind::Rarity => Box::new(Rarity::read_from(buf)?),
+ DataComponentKind::Enchantments => Box::new(Enchantments::read_from(buf)?),
+ DataComponentKind::CanPlaceOn => Box::new(CanPlaceOn::read_from(buf)?),
+ DataComponentKind::CanBreak => Box::new(CanBreak::read_from(buf)?),
+ DataComponentKind::AttributeModifiers => Box::new(AttributeModifiers::read_from(buf)?),
+ DataComponentKind::CustomModelData => Box::new(CustomModelData::read_from(buf)?),
+ DataComponentKind::HideAdditionalTooltip => {
+ Box::new(HideAdditionalTooltip::read_from(buf)?)
+ }
+ DataComponentKind::HideTooltip => Box::new(HideTooltip::read_from(buf)?),
+ DataComponentKind::RepairCost => Box::new(RepairCost::read_from(buf)?),
+ DataComponentKind::CreativeSlotLock => Box::new(CreativeSlotLock::read_from(buf)?),
+ DataComponentKind::EnchantmentGlintOverride => {
+ Box::new(EnchantmentGlintOverride::read_from(buf)?)
+ }
+ DataComponentKind::IntangibleProjectile => Box::new(IntangibleProjectile::read_from(buf)?),
+ DataComponentKind::Food => Box::new(Food::read_from(buf)?),
+ DataComponentKind::FireResistant => Box::new(FireResistant::read_from(buf)?),
+ DataComponentKind::Tool => Box::new(Tool::read_from(buf)?),
+ DataComponentKind::StoredEnchantments => Box::new(StoredEnchantments::read_from(buf)?),
+ DataComponentKind::DyedColor => Box::new(DyedColor::read_from(buf)?),
+ DataComponentKind::MapColor => Box::new(MapColor::read_from(buf)?),
+ DataComponentKind::MapId => Box::new(MapId::read_from(buf)?),
+ DataComponentKind::MapDecorations => Box::new(MapDecorations::read_from(buf)?),
+ DataComponentKind::MapPostProcessing => Box::new(MapPostProcessing::read_from(buf)?),
+ DataComponentKind::ChargedProjectiles => Box::new(ChargedProjectiles::read_from(buf)?),
+ DataComponentKind::BundleContents => Box::new(BundleContents::read_from(buf)?),
+ DataComponentKind::PotionContents => Box::new(PotionContents::read_from(buf)?),
+ DataComponentKind::SuspiciousStewEffects => {
+ Box::new(SuspiciousStewEffects::read_from(buf)?)
+ }
+ DataComponentKind::WritableBookContent => Box::new(WritableBookContent::read_from(buf)?),
+ DataComponentKind::WrittenBookContent => Box::new(WrittenBookContent::read_from(buf)?),
+ DataComponentKind::Trim => Box::new(Trim::read_from(buf)?),
+ DataComponentKind::DebugStickState => Box::new(DebugStickState::read_from(buf)?),
+ DataComponentKind::EntityData => Box::new(EntityData::read_from(buf)?),
+ DataComponentKind::BucketEntityData => Box::new(BucketEntityData::read_from(buf)?),
+ DataComponentKind::BlockEntityData => Box::new(BlockEntityData::read_from(buf)?),
+ DataComponentKind::Instrument => Box::new(Instrument::read_from(buf)?),
+ DataComponentKind::OminousBottleAmplifier => {
+ Box::new(OminousBottleAmplifier::read_from(buf)?)
+ }
+ DataComponentKind::Recipes => Box::new(Recipes::read_from(buf)?),
+ DataComponentKind::LodestoneTracker => Box::new(LodestoneTracker::read_from(buf)?),
+ DataComponentKind::FireworkExplosion => Box::new(FireworkExplosion::read_from(buf)?),
+ DataComponentKind::Fireworks => Box::new(Fireworks::read_from(buf)?),
+ DataComponentKind::Profile => Box::new(Profile::read_from(buf)?),
+ DataComponentKind::NoteBlockSound => Box::new(NoteBlockSound::read_from(buf)?),
+ DataComponentKind::BannerPatterns => Box::new(BannerPatterns::read_from(buf)?),
+ DataComponentKind::BaseColor => Box::new(BaseColor::read_from(buf)?),
+ DataComponentKind::PotDecorations => Box::new(PotDecorations::read_from(buf)?),
+ DataComponentKind::Container => Box::new(Container::read_from(buf)?),
+ DataComponentKind::BlockState => Box::new(BlockState::read_from(buf)?),
+ DataComponentKind::Bees => Box::new(Bees::read_from(buf)?),
+ DataComponentKind::Lock => Box::new(Lock::read_from(buf)?),
+ DataComponentKind::ContainerLoot => Box::new(ContainerLoot::read_from(buf)?),
+ })
+}
+
+#[derive(Clone, PartialEq, McBuf)]
+pub struct CustomData {
+ pub nbt: Nbt,
+}
+impl DataComponent for CustomData {}
+
+#[derive(Clone, PartialEq, McBuf)]
+pub struct MaxStackSize {
+ #[var]
+ pub count: i32,
+}
+impl DataComponent for MaxStackSize {}
+
+#[derive(Clone, PartialEq, McBuf)]
+pub struct MaxDamage {
+ #[var]
+ pub amount: i32,
+}
+impl DataComponent for MaxDamage {}
+
+#[derive(Clone, PartialEq, McBuf)]
+pub struct Damage {
+ #[var]
+ pub amount: i32,
+}
+
+impl DataComponent for Damage {}
+
+#[derive(Clone, PartialEq, McBuf)]
+pub struct Unbreakable {
+ pub show_in_tooltip: bool,
+}
+impl DataComponent for Unbreakable {}
+impl Default for Unbreakable {
+ fn default() -> Self {
+ Self {
+ show_in_tooltip: true,
+ }
+ }
+}
+
+#[derive(Clone, PartialEq, McBuf)]
+pub struct CustomName {
+ pub name: FormattedText,
+}
+impl DataComponent for CustomName {}
+
+#[derive(Clone, PartialEq, McBuf)]
+pub struct ItemName {
+ pub name: FormattedText,
+}
+impl DataComponent for ItemName {}
+
+#[derive(Clone, PartialEq, McBuf)]
+pub struct Lore {
+ pub lines: Vec<FormattedText>,
+ // vanilla also has styled_lines here but it doesn't appear to be used for the protocol
+}
+impl DataComponent for Lore {}
+
+#[derive(Clone, PartialEq, Copy, McBuf)]
+pub enum Rarity {
+ Common,
+ Uncommon,
+ Rare,
+ Epic,
+}
+impl DataComponent for Rarity {}
+
+#[derive(Clone, PartialEq, McBuf)]
+pub struct Enchantments {
+ #[var]
+ pub levels: HashMap<Enchantment, u32>,
+ pub show_in_tooltip: bool,
+}
+impl DataComponent for Enchantments {}
+
+#[derive(Clone, PartialEq, McBuf)]
+pub enum BlockStateValueMatcher {
+ Exact {
+ value: String,
+ },
+ Range {
+ min: Option<String>,
+ max: Option<String>,
+ },
+}
+
+#[derive(Clone, PartialEq, McBuf)]
+pub struct BlockStatePropertyMatcher {
+ pub name: String,
+ pub value_matcher: BlockStateValueMatcher,
+}
+
+#[derive(Clone, PartialEq, McBuf)]
+pub struct BlockPredicate {
+ pub blocks: Option<HolderSet<Block, ResourceLocation>>,
+ pub properties: Option<Vec<BlockStatePropertyMatcher>>,
+ pub nbt: Option<NbtCompound>,
+}
+
+#[derive(Clone, PartialEq, McBuf)]
+pub struct AdventureModePredicate {
+ pub predicates: Vec<BlockPredicate>,
+ pub show_in_tooltip: bool,
+}
+
+#[derive(Clone, PartialEq, McBuf)]
+pub struct CanPlaceOn {
+ pub predicate: AdventureModePredicate,
+}
+impl DataComponent for CanPlaceOn {}
+
+#[derive(Clone, PartialEq, McBuf)]
+pub struct CanBreak {
+ pub predicate: AdventureModePredicate,
+}
+impl DataComponent for CanBreak {}
+
+#[derive(Clone, Copy, PartialEq, McBuf)]
+pub enum EquipmentSlotGroup {
+ Any,
+ Mainhand,
+ Offhand,
+ Hand,
+ Feet,
+ Legs,
+ Chest,
+ Head,
+ Armor,
+ Body,
+}
+
+#[derive(Clone, Copy, PartialEq, McBuf)]
+pub enum AttributeModifierOperation {
+ Addition,
+ MultiplyBase,
+ MultiplyTotal,
+}
+
+// this is duplicated in azalea-entity, BUT the one there has a different
+// protocol format (and we can't use it anyways because it would cause a
+// circular dependency)
+#[derive(Clone, PartialEq, McBuf)]
+pub struct AttributeModifier {
+ pub uuid: Uuid,
+ pub name: String,
+ pub amount: f64,
+ pub operation: AttributeModifierOperation,
+}
+
+#[derive(Clone, PartialEq, McBuf)]
+pub struct AttributeModifiersEntry {
+ pub attribute: Attribute,
+ pub modifier: AttributeModifier,
+ pub slot: EquipmentSlotGroup,
+}
+
+#[derive(Clone, PartialEq, McBuf)]
+pub struct AttributeModifiers {
+ pub modifiers: Vec<AttributeModifiersEntry>,
+ pub show_in_tooltip: bool,
+}
+impl DataComponent for AttributeModifiers {}
+
+#[derive(Clone, PartialEq, McBuf)]
+pub struct CustomModelData {
+ #[var]
+ pub value: i32,
+}
+impl DataComponent for CustomModelData {}
+
+#[derive(Clone, PartialEq, McBuf)]
+pub struct HideAdditionalTooltip;
+impl DataComponent for HideAdditionalTooltip {}
+
+#[derive(Clone, PartialEq, McBuf)]
+pub struct HideTooltip;
+impl DataComponent for HideTooltip {}
+
+#[derive(Clone, PartialEq, McBuf)]
+pub struct RepairCost {
+ #[var]
+ pub cost: u32,
+}
+impl DataComponent for RepairCost {}
+
+#[derive(Clone, PartialEq, McBuf)]
+pub struct CreativeSlotLock;
+impl DataComponent for CreativeSlotLock {}
+
+#[derive(Clone, PartialEq, McBuf)]
+pub struct EnchantmentGlintOverride {
+ pub show_glint: bool,
+}
+impl DataComponent for EnchantmentGlintOverride {}
+
+#[derive(Clone, PartialEq, McBuf)]
+pub struct IntangibleProjectile;
+impl DataComponent for IntangibleProjectile {}
+
+#[derive(Clone, PartialEq, McBuf)]
+pub struct MobEffectDetails {
+ #[var]
+ pub amplifier: i32,
+ #[var]
+ pub duration: i32,
+ pub ambient: bool,
+ pub show_particles: bool,
+ pub show_icon: bool,
+ pub hidden_effect: Option<Box<MobEffectDetails>>,
+}
+
+#[derive(Clone, PartialEq, McBuf)]
+pub struct MobEffectInstance {
+ pub effect: MobEffect,
+ pub details: MobEffectDetails,
+}
+
+#[derive(Clone, PartialEq, McBuf)]
+pub struct PossibleEffect {
+ pub effect: MobEffectInstance,
+ pub probability: f32,
+}
+
+#[derive(Clone, PartialEq, McBuf)]
+pub struct Food {
+ #[var]
+ pub nutrition: i32,
+ pub saturation: f32,
+ pub can_always_eat: bool,
+ pub eat_seconds: f32,
+ pub effects: Vec<PossibleEffect>,
+}
+impl DataComponent for Food {}
+
+#[derive(Clone, PartialEq, McBuf)]
+pub struct FireResistant;
+impl DataComponent for FireResistant {}
+
+#[derive(Clone, PartialEq, McBuf)]
+pub struct ToolRule {
+ pub blocks: Vec<Block>,
+ pub speed: Option<f32>,
+ pub correct_for_drops: Option<bool>,
+}
+
+#[derive(Clone, PartialEq, McBuf)]
+pub struct Tool {
+ pub rules: Vec<ToolRule>,
+ pub default_mining_speed: f32,
+ #[var]
+ pub damage_per_block: i32,
+}
+impl DataComponent for Tool {}
+
+#[derive(Clone, PartialEq, McBuf)]
+pub struct StoredEnchantments {
+ #[var]
+ pub enchantments: HashMap<Enchantment, i32>,
+ pub show_in_tooltip: bool,
+}
+impl DataComponent for StoredEnchantments {}
+
+#[derive(Clone, PartialEq, McBuf)]
+pub struct DyedColor {
+ pub rgb: i32,
+ pub show_in_tooltip: bool,
+}
+impl DataComponent for DyedColor {}
+
+#[derive(Clone, PartialEq, McBuf)]
+pub struct MapColor {
+ pub color: i32,
+}
+impl DataComponent for MapColor {}
+
+#[derive(Clone, PartialEq, McBuf)]
+pub struct MapId {
+ #[var]
+ pub id: i32,
+}
+impl DataComponent for MapId {}
+
+#[derive(Clone, PartialEq, McBuf)]
+pub struct MapDecorations {
+ pub decorations: NbtCompound,
+}
+impl DataComponent for MapDecorations {}
+
+#[derive(Clone, Copy, PartialEq, McBuf)]
+pub enum MapPostProcessing {
+ Lock,
+ Scale,
+}
+impl DataComponent for MapPostProcessing {}
+
+#[derive(Clone, PartialEq, McBuf)]
+pub struct ChargedProjectiles {
+ pub items: Vec<ItemSlot>,
+}
+impl DataComponent for ChargedProjectiles {}
+
+#[derive(Clone, PartialEq, McBuf)]
+pub struct BundleContents {
+ pub items: Vec<ItemSlot>,
+}
+impl DataComponent for BundleContents {}
+
+#[derive(Clone, PartialEq, McBuf)]
+pub struct PotionContents {
+ pub potion: Option<Potion>,
+ pub custom_color: Option<i32>,
+ pub custom_effects: Vec<MobEffectInstance>,
+}
+impl DataComponent for PotionContents {}
+
+#[derive(Clone, PartialEq, McBuf)]
+pub struct SuspiciousStewEffect {
+ pub effect: MobEffect,
+ #[var]
+ pub duration: i32,
+}
+
+#[derive(Clone, PartialEq, McBuf)]
+pub struct SuspiciousStewEffects {
+ pub effects: Vec<SuspiciousStewEffect>,
+}
+impl DataComponent for SuspiciousStewEffects {}
+
+#[derive(Clone, PartialEq, McBuf)]
+pub struct WritableBookContent {
+ pub pages: Vec<String>,
+}
+impl DataComponent for WritableBookContent {}
+
+#[derive(Clone, PartialEq, McBuf)]
+pub struct WrittenBookContent {
+ pub title: String,
+ pub author: String,
+ #[var]
+ pub generation: i32,
+ pub pages: Vec<FormattedText>,
+ pub resolved: bool,
+}
+impl DataComponent for WrittenBookContent {}
+
+#[derive(Clone, PartialEq, McBuf)]
+pub struct Trim {
+ pub material: TrimMaterial,
+ pub pattern: TrimPattern,
+ pub show_in_tooltip: bool,
+}
+impl DataComponent for Trim {}
+
+#[derive(Clone, PartialEq, McBuf)]
+pub struct DebugStickState {
+ pub properties: NbtCompound,
+}
+impl DataComponent for DebugStickState {}
+
+#[derive(Clone, PartialEq, McBuf)]
+pub struct EntityData {
+ pub entity: NbtCompound,
+}
+impl DataComponent for EntityData {}
+
+#[derive(Clone, PartialEq, McBuf)]
+pub struct BucketEntityData {
+ pub entity: NbtCompound,
+}
+impl DataComponent for BucketEntityData {}
+
+#[derive(Clone, PartialEq, McBuf)]
+pub struct BlockEntityData {
+ pub entity: NbtCompound,
+}
+impl DataComponent for BlockEntityData {}
+
+#[derive(Clone, PartialEq, McBuf)]
+pub struct Instrument {
+ pub instrument: azalea_registry::Instrument,
+}
+impl DataComponent for Instrument {}
+
+#[derive(Clone, PartialEq, McBuf)]
+pub struct OminousBottleAmplifier {
+ #[var]
+ pub amplifier: i32,
+}
+impl DataComponent for OminousBottleAmplifier {}
+
+#[derive(Clone, PartialEq, McBuf)]
+pub struct Recipes {
+ pub recipes: Vec<ResourceLocation>,
+}
+impl DataComponent for Recipes {}
+
+#[derive(Clone, PartialEq, McBuf)]
+pub struct LodestoneTracker {
+ pub target: Option<GlobalPos>,
+ pub tracked: bool,
+}
+impl DataComponent for LodestoneTracker {}
+
+#[derive(Clone, Copy, PartialEq, McBuf)]
+pub enum FireworkExplosionShape {
+ SmallBall,
+ LargeBall,
+ Star,
+ Creeper,
+ Burst,
+}
+
+#[derive(Clone, PartialEq, McBuf)]
+pub struct FireworkExplosion {
+ pub shape: FireworkExplosionShape,
+ pub colors: Vec<i32>,
+ pub fade_colors: Vec<i32>,
+ pub has_trail: bool,
+ pub has_twinkle: bool,
+}
+impl DataComponent for FireworkExplosion {}
+
+#[derive(Clone, PartialEq, McBuf)]
+pub struct Fireworks {
+ #[var]
+ pub flight_duration: i32,
+ pub explosions: Vec<FireworkExplosion>,
+}
+impl DataComponent for Fireworks {}
+
+#[derive(Clone, PartialEq, McBuf)]
+pub struct GameProfileProperty {
+ pub name: String,
+ pub value: String,
+ pub signature: Option<String>,
+}
+
+#[derive(Clone, PartialEq, McBuf)]
+pub struct Profile {
+ pub name: String,
+ pub id: Option<Uuid>,
+ pub properties: Vec<GameProfileProperty>,
+}
+impl DataComponent for Profile {}
+
+#[derive(Clone, PartialEq, McBuf)]
+pub struct NoteBlockSound {
+ pub sound: ResourceLocation,
+}
+impl DataComponent for NoteBlockSound {}
+
+#[derive(Clone, PartialEq, McBuf)]
+pub struct BannerPattern {
+ #[var]
+ pub pattern: i32,
+ #[var]
+ pub color: i32,
+}
+
+#[derive(Clone, PartialEq, McBuf)]
+pub struct BannerPatterns {
+ pub patterns: Vec<BannerPattern>,
+}
+impl DataComponent for BannerPatterns {}
+
+#[derive(Clone, Copy, PartialEq, McBuf)]
+pub enum DyeColor {
+ White,
+ Orange,
+ Magenta,
+ LightBlue,
+ Yellow,
+ Lime,
+ Pink,
+ Gray,
+ LightGray,
+ Cyan,
+ Purple,
+ Blue,
+ Brown,
+ Green,
+ Red,
+ Black,
+}
+
+#[derive(Clone, PartialEq, McBuf)]
+pub struct BaseColor {
+ pub color: DyeColor,
+}
+impl DataComponent for BaseColor {}
+
+#[derive(Clone, PartialEq, McBuf)]
+pub struct PotDecorations {
+ pub items: Vec<Item>,
+}
+impl DataComponent for PotDecorations {}
+
+#[derive(Clone, PartialEq, McBuf)]
+pub struct Container {
+ pub items: Vec<ItemSlot>,
+}
+impl DataComponent for Container {}
+
+#[derive(Clone, PartialEq, McBuf)]
+pub struct BlockState {
+ pub properties: HashMap<String, String>,
+}
+impl DataComponent for BlockState {}
+
+#[derive(Clone, PartialEq, McBuf)]
+pub struct BeehiveOccupant {
+ pub entity_data: NbtCompound,
+ #[var]
+ pub ticks_in_hive: i32,
+ #[var]
+ pub min_ticks_in_hive: i32,
+}
+
+#[derive(Clone, PartialEq, McBuf)]
+pub struct Bees {
+ pub occupants: Vec<BeehiveOccupant>,
+}
+impl DataComponent for Bees {}
+
+#[derive(Clone, PartialEq, McBuf)]
+pub struct Lock {
+ pub key: String,
+}
+impl DataComponent for Lock {}
+
+#[derive(Clone, PartialEq, McBuf)]
+pub struct ContainerLoot {
+ pub loot: NbtCompound,
+}
+impl DataComponent for ContainerLoot {}
diff --git a/azalea-inventory/src/item/mod.rs b/azalea-inventory/src/item/mod.rs
index 0ad7b2c0..862cd20e 100644
--- a/azalea-inventory/src/item/mod.rs
+++ b/azalea-inventory/src/item/mod.rs
@@ -5,7 +5,7 @@ pub trait MaxStackSizeExt {
/// [`ItemSlotData`].
///
/// [`ItemSlotData`]: crate::ItemSlotData
- fn max_stack_size(&self) -> i8;
+ fn max_stack_size(&self) -> i32;
/// Whether this item can be stacked with other items.
///
@@ -16,7 +16,7 @@ pub trait MaxStackSizeExt {
}
impl MaxStackSizeExt for azalea_registry::Item {
- fn max_stack_size(&self) -> i8 {
+ fn max_stack_size(&self) -> i32 {
// TODO: have the properties for every item defined somewhere
64
}
diff --git a/azalea-inventory/src/lib.rs b/azalea-inventory/src/lib.rs
index b2ea215a..ba14eff8 100644
--- a/azalea-inventory/src/lib.rs
+++ b/azalea-inventory/src/lib.rs
@@ -1,5 +1,7 @@
-#![doc = include_str!("../README.md")]
+#![feature(trait_upcasting)]
+/// Representations of various inventory data structures in Minecraft.
+pub mod components;
pub mod item;
pub mod operations;
mod slot;
diff --git a/azalea-inventory/src/operations.rs b/azalea-inventory/src/operations.rs
index ca21a3e7..652d1900 100644
--- a/azalea-inventory/src/operations.rs
+++ b/azalea-inventory/src/operations.rs
@@ -621,7 +621,7 @@ impl Menu {
}
/// Get the maximum number of items that can be placed in this slot.
- pub fn max_stack_size(&self, _target_slot_index: usize) -> u8 {
+ pub fn max_stack_size(&self, _target_slot_index: usize) -> u32 {
64
}
@@ -671,9 +671,11 @@ impl Menu {
let target_slot = self.slot(target_slot_index).unwrap();
if let ItemSlot::Present(target_item) = target_slot {
// the target slot is empty, so we can just move the item there
- if self.may_place(target_slot_index, item) && target_item.is_same_item_and_nbt(item) {
+ if self.may_place(target_slot_index, item)
+ && target_item.is_same_item_and_components(item)
+ {
let slot_item_limit = self.max_stack_size(target_slot_index);
- let new_target_slot_data = item.split(u8::min(slot_item_limit, item.count as u8));
+ let new_target_slot_data = item.split(u32::min(slot_item_limit, item.count as u32));
// get the target slot again but mut this time so we can update it
let target_slot = self.slot_mut(target_slot_index).unwrap();
@@ -691,7 +693,7 @@ impl Menu {
let target_slot = self.slot(target_slot_index).unwrap();
if target_slot.is_empty() && self.may_place(target_slot_index, item) {
let slot_item_limit = self.max_stack_size(target_slot_index);
- let new_target_slot_data = item.split(u8::min(slot_item_limit, item.count as u8));
+ let new_target_slot_data = item.split(u32::min(slot_item_limit, item.count as u32));
let target_slot = self.slot_mut(target_slot_index).unwrap();
*target_slot = ItemSlot::Present(new_target_slot_data);
diff --git a/azalea-inventory/src/slot.rs b/azalea-inventory/src/slot.rs
index c4f8da05..9aa33263 100644
--- a/azalea-inventory/src/slot.rs
+++ b/azalea-inventory/src/slot.rs
@@ -1,6 +1,12 @@
-use azalea_buf::{BufReadError, McBuf, McBufReadable, McBufWritable};
-use simdnbt::owned::Nbt;
-use std::io::{Cursor, Write};
+use azalea_buf::{BufReadError, McBufReadable, McBufVarReadable, McBufVarWritable, McBufWritable};
+use azalea_registry::DataComponentKind;
+use std::{
+ collections::HashMap,
+ fmt,
+ io::{Cursor, Write},
+};
+
+use crate::components::{self};
/// Either an item in an inventory or nothing.
#[derive(Debug, Clone, Default, PartialEq)]
@@ -33,7 +39,7 @@ impl ItemSlot {
///
/// Note that it's possible for the count to be zero or negative when the
/// slot is present.
- pub fn count(&self) -> i8 {
+ pub fn count(&self) -> i32 {
match self {
ItemSlot::Empty => 0,
ItemSlot::Present(i) => i.count,
@@ -41,7 +47,7 @@ impl ItemSlot {
}
/// Remove `count` items from this slot, returning the removed items.
- pub fn split(&mut self, count: u8) -> ItemSlot {
+ pub fn split(&mut self, count: u32) -> ItemSlot {
match self {
ItemSlot::Empty => ItemSlot::Empty,
ItemSlot::Present(i) => {
@@ -83,20 +89,20 @@ impl ItemSlot {
/// An item in an inventory, with a count and NBT. Usually you want [`ItemSlot`]
/// or [`azalea_registry::Item`] instead.
-#[derive(Debug, Clone, McBuf, PartialEq)]
+#[derive(Debug, Clone, PartialEq)]
pub struct ItemSlotData {
- pub kind: azalea_registry::Item,
/// The amount of the item in this slot.
///
/// The count can be zero or negative, but this is rare.
- pub count: i8,
- pub nbt: Nbt,
+ pub count: i32,
+ pub kind: azalea_registry::Item,
+ pub components: DataComponentPatch,
}
impl ItemSlotData {
/// Remove `count` items from this slot, returning the removed items.
- pub fn split(&mut self, count: u8) -> ItemSlotData {
- let returning_count = i8::min(count as i8, self.count);
+ pub fn split(&mut self, count: u32) -> ItemSlotData {
+ let returning_count = i32::min(count as i32, self.count);
let mut returning = self.clone();
returning.count = returning_count;
self.count -= returning_count;
@@ -116,39 +122,161 @@ impl ItemSlotData {
/// let mut a = ItemSlotData {
/// kind: Item::Stone,
/// count: 1,
- /// nbt: Default::default(),
+ /// components: Default::default(),
/// };
/// let mut b = ItemSlotData {
/// kind: Item::Stone,
/// count: 2,
- /// nbt: Default::default(),
+ /// components: Default::default(),
/// };
- /// assert!(a.is_same_item_and_nbt(&b));
+ /// assert!(a.is_same_item_and_components(&b));
///
/// b.kind = Item::Dirt;
- /// assert!(!a.is_same_item_and_nbt(&b));
+ /// assert!(!a.is_same_item_and_components(&b));
/// ```
- pub fn is_same_item_and_nbt(&self, other: &ItemSlotData) -> bool {
- self.kind == other.kind && self.nbt == other.nbt
+ pub fn is_same_item_and_components(&self, other: &ItemSlotData) -> bool {
+ self.kind == other.kind && self.components == other.components
}
}
impl McBufReadable for ItemSlot {
fn read_from(buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError> {
- let slot = Option::<ItemSlotData>::read_from(buf)?;
- Ok(slot.map_or(ItemSlot::Empty, ItemSlot::Present))
+ let count = i32::var_read_from(buf)?;
+ if count <= 0 {
+ Ok(ItemSlot::Empty)
+ } else {
+ let kind = azalea_registry::Item::read_from(buf)?;
+ let components = DataComponentPatch::read_from(buf)?;
+ Ok(ItemSlot::Present(ItemSlotData {
+ count,
+ kind,
+ components,
+ }))
+ }
}
}
impl McBufWritable for ItemSlot {
fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
match self {
- ItemSlot::Empty => false.write_into(buf)?,
+ ItemSlot::Empty => 0.var_write_into(buf)?,
ItemSlot::Present(i) => {
- true.write_into(buf)?;
- i.write_into(buf)?;
+ i.count.var_write_into(buf)?;
+ i.kind.write_into(buf)?;
+ i.components.write_into(buf)?;
}
};
Ok(())
}
}
+
+#[derive(Default)]
+pub struct DataComponentPatch {
+ components: HashMap<DataComponentKind, Option<Box<dyn components::EncodableDataComponent>>>,
+}
+
+impl DataComponentPatch {
+ pub fn get(&self, kind: DataComponentKind) -> Option<&dyn components::EncodableDataComponent> {
+ self.components.get(&kind).and_then(|c| c.as_deref())
+ }
+}
+
+impl McBufReadable for DataComponentPatch {
+ fn read_from(buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError> {
+ let components_with_data_count = u32::var_read_from(buf)?;
+ let components_without_data_count = u32::var_read_from(buf)?;
+
+ if components_without_data_count == 0 && components_with_data_count == 0 {
+ return Ok(DataComponentPatch::default());
+ }
+
+ let mut components = HashMap::new();
+ for _ in 0..components_with_data_count {
+ let component_kind = DataComponentKind::read_from(buf)?;
+ let component_data = components::from_kind(component_kind, buf)?;
+ components.insert(component_kind, Some(component_data));
+ }
+
+ for _ in 0..components_without_data_count {
+ let component_kind = DataComponentKind::read_from(buf)?;
+ components.insert(component_kind, None);
+ }
+
+ Ok(DataComponentPatch { components })
+ }
+}
+
+impl McBufWritable for DataComponentPatch {
+ fn write_into(&self, buf: &mut impl Write) -> Result<(), std::io::Error> {
+ let mut components_with_data_count = 0;
+ let mut components_without_data_count = 0;
+ for component in self.components.values() {
+ if component.is_some() {
+ components_with_data_count += 1;
+ } else {
+ components_without_data_count += 1;
+ }
+ }
+
+ components_with_data_count.write_into(buf)?;
+ components_without_data_count.write_into(buf)?;
+
+ for (kind, component) in &self.components {
+ if let Some(component) = component {
+ kind.write_into(buf)?;
+ let mut component_buf = Vec::new();
+ component.encode(&mut component_buf).unwrap();
+ component_buf.write_into(buf)?;
+ }
+ }
+
+ for (kind, component) in &self.components {
+ if component.is_none() {
+ kind.write_into(buf)?;
+ }
+ }
+
+ Ok(())
+ }
+}
+
+impl Clone for DataComponentPatch {
+ fn clone(&self) -> Self {
+ let mut components = HashMap::with_capacity(self.components.len());
+ for (kind, component) in &self.components {
+ components.insert(*kind, component.as_ref().map(|c| (*c).clone()));
+ }
+ DataComponentPatch { components }
+ }
+}
+impl fmt::Debug for DataComponentPatch {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_set().entries(self.components.keys()).finish()
+ }
+}
+impl PartialEq for DataComponentPatch {
+ fn eq(&self, other: &Self) -> bool {
+ if self.components.len() != other.components.len() {
+ return false;
+ }
+ for (kind, component) in &self.components {
+ if let Some(other_component) = other.components.get(kind) {
+ // we can't use PartialEq, but we can use our own eq method
+ if let Some(component) = component {
+ if let Some(other_component) = other_component {
+ if !component.eq((*other_component).clone()) {
+ return false;
+ }
+ } else {
+ return false;
+ }
+ } else if other_component.is_some() {
+ return false;
+ }
+ } else {
+ return false;
+ }
+ }
+ true
+ }
+}