diff options
| author | mat <27899617+mat-1@users.noreply.github.com> | 2024-04-23 10:34:50 -0500 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-04-23 10:34:50 -0500 |
| commit | 1d80f531b74bc3b31023753acb81b35efcdadd73 (patch) | |
| tree | 675635c7c41fbb456e3e0dd7b9f09c7211d356f0 /azalea-inventory/src | |
| parent | 0ddad8bd9c7c0e8846aec8bc90c95416418c9a63 (diff) | |
| download | azalea-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.rs | 656 | ||||
| -rw-r--r-- | azalea-inventory/src/item/mod.rs | 4 | ||||
| -rw-r--r-- | azalea-inventory/src/lib.rs | 4 | ||||
| -rw-r--r-- | azalea-inventory/src/operations.rs | 10 | ||||
| -rw-r--r-- | azalea-inventory/src/slot.rs | 172 |
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 + } +} |
