diff --git a/Cargo.lock b/Cargo.lock index da16af8..f237437 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -252,9 +252,9 @@ dependencies = [ "fnv", "ident_case", "proc-macro2", - "quote", + "quote 1.0.40", "strsim", - "syn", + "syn 2.0.101", ] [[package]] @@ -264,8 +264,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" dependencies = [ "darling_core", - "quote", - "syn", + "quote 1.0.40", + "syn 2.0.101", ] [[package]] @@ -308,6 +308,22 @@ version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" +[[package]] +name = "enum_index" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5532bdea562e7be83060c36185eecccba82fe16729d2eaad2891d65417656dd" + +[[package]] +name = "enum_index_derive" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ab22c8085548bf06190113dca556e149ecdbb05ae5b972a2b9899f26b944ee4" +dependencies = [ + "quote 0.3.15", + "syn 0.11.11", +] + [[package]] name = "env_filter" version = "0.1.3" @@ -492,8 +508,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97265751f8a9a4228476f2fc17874a9e7e70e96b893368e42619880fe143b48a" dependencies = [ "proc-macro2", - "quote", - "syn", + "quote 1.0.40", + "syn 2.0.101", ] [[package]] @@ -600,8 +616,8 @@ checksum = "f5030daf005bface118c096f510ffb781fc28f9ab6a32ab224d8631be6851d30" dependencies = [ "by_address", "proc-macro2", - "quote", - "syn", + "quote 1.0.40", + "syn 2.0.101", ] [[package]] @@ -652,8 +668,8 @@ dependencies = [ "phf_generator", "phf_shared", "proc-macro2", - "quote", - "syn", + "quote 1.0.40", + "syn 2.0.101", ] [[package]] @@ -695,6 +711,12 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "quote" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a" + [[package]] name = "quote" version = "1.0.40" @@ -774,6 +796,8 @@ dependencies = [ "colog", "craftflow-nbt", "dashmap", + "enum_index", + "enum_index_derive", "ignore-result", "itertools", "log", @@ -823,8 +847,8 @@ checksum = "486b028b311aaaea83e0ba65a3e6e3cbef381e74e9d0bd6263faefd1fb503c1d" dependencies = [ "darling", "proc-macro2", - "quote", - "syn", + "quote 1.0.40", + "syn 2.0.101", ] [[package]] @@ -834,8 +858,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" dependencies = [ "proc-macro2", - "quote", - "syn", + "quote 1.0.40", + "syn 2.0.101", ] [[package]] @@ -885,8 +909,8 @@ checksum = "8d00caa5193a3c8362ac2b73be6b9e768aa5a4b2f721d8f4b339600c3cb51f8e" dependencies = [ "darling", "proc-macro2", - "quote", - "syn", + "quote 1.0.40", + "syn 2.0.101", ] [[package]] @@ -913,6 +937,17 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" +[[package]] +name = "syn" +version = "0.11.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3b891b9015c88c576343b9b3e41c2c11a51c219ef067b264bd9c8aa9b441dad" +dependencies = [ + "quote 0.3.15", + "synom", + "unicode-xid", +] + [[package]] name = "syn" version = "2.0.101" @@ -920,10 +955,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf" dependencies = [ "proc-macro2", - "quote", + "quote 1.0.40", "unicode-ident", ] +[[package]] +name = "synom" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a393066ed9010ebaed60b9eafa373d4b1baac186dd7e008555b0f702b51945b6" +dependencies = [ + "unicode-xid", +] + [[package]] name = "thiserror" version = "1.0.69" @@ -940,8 +984,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", - "quote", - "syn", + "quote 1.0.40", + "syn 2.0.101", ] [[package]] @@ -1028,6 +1072,12 @@ version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" +[[package]] +name = "unicode-xid" +version = "0.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c1f860d7d29cf02cb2f3f359fd35991af3d30bac52c57d265a3c461074cb4dc" + [[package]] name = "utf8parse" version = "0.2.2" @@ -1070,8 +1120,8 @@ dependencies = [ "bumpalo", "log", "proc-macro2", - "quote", - "syn", + "quote 1.0.40", + "syn 2.0.101", "wasm-bindgen-shared", ] @@ -1081,7 +1131,7 @@ version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" dependencies = [ - "quote", + "quote 1.0.40", "wasm-bindgen-macro-support", ] @@ -1092,8 +1142,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" dependencies = [ "proc-macro2", - "quote", - "syn", + "quote 1.0.40", + "syn 2.0.101", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -1127,8 +1177,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" dependencies = [ "proc-macro2", - "quote", - "syn", + "quote 1.0.40", + "syn 2.0.101", ] [[package]] @@ -1138,8 +1188,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" dependencies = [ "proc-macro2", - "quote", - "syn", + "quote 1.0.40", + "syn 2.0.101", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 1d9e7fe..c055c57 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,3 +19,5 @@ uuid = { version = "1.16.0", features = ["v3"] } dashmap = "6.1.0" paste = "1.0.15" ignore-result = "0.2.0" +enum_index = "0.2.0" +enum_index_derive = "0.2.0" diff --git a/src/data/mod.rs b/src/data/mod.rs index 76157f0..0666e2f 100644 --- a/src/data/mod.rs +++ b/src/data/mod.rs @@ -7,6 +7,7 @@ use super::ServerError; pub mod component; pub mod slot; +pub mod sound; // Трейт для чтения NBT-совместимых приколов pub trait ReadWriteNBT: DataReader + DataWriter { @@ -50,3 +51,16 @@ impl ReadWritePosition for Packet { Ok(self.write_long(((x & 0x3FFFFFF) << 38) | ((z & 0x3FFFFFF) << 12) | (y & 0xFFF))?) } } + +#[derive(Clone)] +pub enum IDSet { + Tag(String), + Ids(Vec), +} + +#[derive(Clone)] +pub struct Property { + name: String, + value: String, + signature: Option, +} diff --git a/src/data/slot.rs b/src/data/slot.rs index 655454e..6a76b49 100644 --- a/src/data/slot.rs +++ b/src/data/slot.rs @@ -1,23 +1,574 @@ -use rust_mc_proto::{DataReader, DataWriter}; +use craftflow_nbt::DynNBT; +use enum_index::EnumIndex; +use enum_index_derive::EnumIndex; +use rust_mc_proto::{DataReader, DataWriter, Packet}; +use uuid::Uuid; use crate::ServerError; +use super::{IDSet, Property, component::TextComponent, sound::SoundEvent}; + +pub const SLOT_COMPONENT_LENGTH: u16 = 96; + +#[derive(Clone)] +pub struct BlockPredicate; + +#[derive(Clone)] +pub struct ConsumeEffect; + +#[derive(Clone)] +pub struct PotionEffect; + +#[derive(Clone)] +pub struct TrimPattern; + +#[derive(Clone)] +pub struct Instrument; + +#[derive(Clone)] +pub struct TrimMaterial; + +#[derive(Clone)] +pub struct JukeboxSong; + +#[derive(Clone)] +pub struct FireworkExplosion; + +#[derive(Clone)] +pub struct ChickenVariant; + +#[derive(Clone)] +pub struct PaintingVariant; + +#[derive(Clone)] +pub struct HiveBee { + entity_data: DynNBT, + ticks_in_hive: i32, + min_ticks_in_hive: i32, +} + +#[derive(Clone)] +pub struct BannerLayer { + pattern_type: i32, + asset_id: Option, + translation_key: Option, + /// Can be one of the following: + /// - 0 - White + /// - 1 - Orange + /// - 2 - Magenta + /// - 3 - Light Blue + /// - 4 - Yellow + /// - 5 - Lime + /// - 6 - Pink + /// - 7 - Gray + /// - 8 - Light Gray + /// - 9 - Cyan + /// - 10 - Purple + /// - 11 - Blue + /// - 12 - Brown + /// - 13 - Green + /// - 14 - Red + /// - 15 - Black + color: u8, +} + +#[derive(Clone)] +pub struct AttributeModifier { + attribute_id: u64, + modifier_id: String, + value: f64, + /// The operation to be applied upon the value. Can be one of the following: + /// - 0 - Add + /// - 1 - Multiply base + /// - 2 - Multiply total + operation: u8, + /// The item slot placement required for the modifier to have effect. + /// Can be one of the following: + /// - 0 - Any + /// - 1 - Main hand + /// - 2 - Off hand + /// - 3 - Hand + /// - 4 - Feet + /// - 5 - Legs + /// - 6 - Chest + /// - 7 - Head + /// - 8 - Armor + /// - 9 - Body + slot: u8, +} + +#[derive(Clone)] +pub struct ToolRule { + blocks: IDSet, + has_speed: bool, + speed: Option, + has_correct_drop_for_blocks: bool, + correct_drop_for_blocks: Option, +} + +#[derive(Clone)] +pub struct DamageReduction { + horizontal_blocking_angle: f32, + damage_kind: Option, + base: f32, + factor: f32, +} + +/// https://minecraft.wiki/w/Java_Edition_protocol/Slot_data#Structured_components +#[derive(Clone, EnumIndex)] +pub enum SlotComponent { + CustomData(DynNBT), + /// 1 - 99 + MaxStackSize(i32), + MaxDamage(i32), + Damage(i32), + Unbreakable, + CustomName(TextComponent), + ItemName(TextComponent), + ItemModel(String), + Lore(TextComponent), + /// Can be one of the following: + /// - 0 - Common (white) + /// - 1 - Uncommon (yellow) + /// - 2 - Rare (aqua) + /// - 3 - Epic (pink) + Rarity(u8), + /// Key: The ID of the enchantment in the enchantment registry. \ + /// Value: The level of the enchantment. + Enchantments(Vec<(u64, i32)>), + CanPlaceOn(Vec), + CanBreak(Vec), + AttributeModifiers(Vec), + /// Floats, Flags, Strings, Colors + CustomModelData(Vec, Vec, Vec, Vec), + /// Parameters: + /// - Hide Tooltip + /// - The IDs of data components in the minecraft:data_component_type registry to hide. + TooltipDisplay(bool, Vec), + /// Accumulated anvil usage cost. + /// The client displays "Too Expensive" if the value is greater than 40 and + /// the player is not in creative mode (more specifically, + /// if they don't have the insta-build flag enabled). + /// This behavior can be overridden by setting the level + /// with the Set Container Property packet. + RepairCost(i32), + /// Marks the item as non-interactive on the creative inventory (the first 5 rows of items). \ + /// This is used internally by the client on the paper icon in the saved hot-bars tab. + CreativeSlotLock, + EnchantmentGlintOverride(bool), + /// Marks the projectile as intangible (cannot be picked-up). + IntangibleProjectile, + /// Parameters: + /// - Nutrition, Non-negative + /// - How much saturation will be given after consuming the item. + /// - Whether the item can always be eaten, even at full hunger. + Food(u32, f32, bool), + /// Animation: 0: none, 1: eat, 2: drink, 3: block, 4: bow, 5: spear, 6: crossbow, 7: spyglass, 8: toot_horn, 9: brush + Consumable { + consume_seconds: f32, + animation: u8, + sound: SoundEvent, + has_particles: bool, + effects: Vec, + }, + UseRemainder(Slot), + /// Group of items to apply the cooldown to. Only present if Has cooldown group is true; otherwise defaults to the item's identifier. + UseCooldown { + seconds: f32, + group: Option, + }, + /// Parameter - Types, Tag specifying damage types the item is immune to. Not prefixed by '#'!. + DamageResistant(String), + Tool { + rules: Vec, + default_mining_speed: f32, + damage_per_block: i32, + }, + Weapon { + damage_per_attack: i32, + disable_blocking_for_seconds: f32, + }, + // Opaque internal value controlling how expensive enchantments may be offered. + Enchantable(i32), + Equippable { + slot: u8, + equip_sound: SoundEvent, + model: Option, + camera_overlay: Option, + allowed_entities: Option, + dispensable: bool, + swappable: bool, + damage_on_hurt: bool, + }, + /// Items that can be combined with this item in an anvil to repair it. + Repairable(IDSet), + /// Makes the item function like elytra. + Glider, + TooltipStyle(String), + /// Makes the item function like a totem of undying. + DeathProtection(Vec), + BlockAttacks { + block_delay_seconds: f32, + disable_cooldown_scale: f32, + damage_reductions: Vec, + item_damage_threshold: f32, + item_damage_base: f32, + item_damage_factor: f32, + bypassed_by: Option, + block_sound: Option, + disable_sound: Option, + }, + /// The enchantments stored in this enchanted book. + /// Key: The ID of the enchantment in the enchantment registry. \ + /// Value: The level of the enchantment. + StoredEnchantments(Vec<(u64, i32)>), + DyedColor(i32), + MapColor(i32), + MapId(i32), + MapDecorations(DynNBT), + /// Type of post processing. Can be either: + /// - 0 - Lock + /// - 1 - Scale + MapPostProcessing(u8), + /// Projectiles loaded into a charged crossbow. + ChargedProjectiles(Vec), + BundleContents(Vec), + PotionContents { + potion_id: Option, + custom_color: Option, + custom_effects: Vec, + custom_name: String, + }, + /// Parameter - Effect Multiplier + PotionDurationScale(f32), + /// Key - The ID of the effect in the potion effect type registry. + /// Value - The duration of the effect. + SuspiciousStewEffects(Vec<(u64, i32)>), + /// Parameter - Pages + /// Page: + /// - The raw text of the page + /// - The content after passing through chat filters + WritableBookContent(Vec<(String, Option)>), + WrittenBookContent { + raw_title: String, + filtered_title: Option, + author: String, + generation: i32, + /// Page: + /// - The raw text of the page + /// - The content after passing through chat filters + pages: Vec<(String, Option)>, + resolved: bool, + }, + /// Armor's trim pattern and color + Trim, + DebugStrickState(DynNBT), + EntityData(DynNBT), + BucketEntityData(DynNBT), + BlockEntityData(DynNBT), + Instrument(Instrument), + ProvidesTrimMaterial(TrimMaterial), + /// Between 0 and 4. + OminousBottleAmplifier(u8), + JukeboxPlayable(JukeboxSong), + /// A pattern identifier like #minecraft:pattern_item/globe + ProvidesBannerPatterns(String), + Recipes(DynNBT), + LodestoneTracker { + has_global_position: bool, + dimension: String, + position: (f64, f64, f64), + tracked: bool, + }, + FireworkExplosion(FireworkExplosion), + Fireworks { + flight_duration: i32, + explosions: Vec, + }, + Profile { + name: Option, + unique_id: Option, + properties: Vec, + }, + NoteBlockSound(String), + BannerPatterns(Vec), + /// Can be one of the following: + /// - 0 - White + /// - 1 - Orange + /// - 2 - Magenta + /// - 3 - Light Blue + /// - 4 - Yellow + /// - 5 - Lime + /// - 6 - Pink + /// - 7 - Gray + /// - 8 - Light Gray + /// - 9 - Cyan + /// - 10 - Purple + /// - 11 - Blue + /// - 12 - Brown + /// - 13 - Green + /// - 14 - Red + /// - 15 - Black + BaseColor(u8), + /// The ID of the items in the item registry. + PotDecorations([u64; 4]), + /// Items inside a container of any type. + Container(Vec), + BlockState(Vec<(String, String)>), + Bees(Vec), + Lock(String), + ContainerLoot(DynNBT), + BreakSound(SoundEvent), + VillagerVariant(u64), + WolfVariant(u64), + WolfSoundVariant(u64), + WolfCollar(u8), + /// 0: red, 1: snow + FoxVariant(u8), + /// 0: small, 1: medium, 2: large. + SalmonSize(u8), + ParrotVariant(u64), + /// 0: kob, 1: sunstreak, 2: snooper, 3: dasher, 4: brinely, 5: spotty, 6: flopper, 7: stripey, 8: glitter, 9: blockfish, 10: betty, 11: clayfish. + TropicalFishPattern(u8), + /// Can be one of the following: + /// - 0 - White + /// - 1 - Orange + /// - 2 - Magenta + /// - 3 - Light Blue + /// - 4 - Yellow + /// - 5 - Lime + /// - 6 - Pink + /// - 7 - Gray + /// - 8 - Light Gray + /// - 9 - Cyan + /// - 10 - Purple + /// - 11 - Blue + /// - 12 - Brown + /// - 13 - Green + /// - 14 - Red + /// - 15 - Black + TropicalFishBaseColor(u8), + /// Can be one of the following: + /// - 0 - White + /// - 1 - Orange + /// - 2 - Magenta + /// - 3 - Light Blue + /// - 4 - Yellow + /// - 5 - Lime + /// - 6 - Pink + /// - 7 - Gray + /// - 8 - Light Gray + /// - 9 - Cyan + /// - 10 - Purple + /// - 11 - Blue + /// - 12 - Brown + /// - 13 - Green + /// - 14 - Red + /// - 15 - Black + TropicalFishPatternColor(u8), + /// 0: red, 1: brown. + MooshroomVariant(u8), + /// 0: brown, 1: white, 2: black, 3: white splotched, 4: gold, 5: salt, 6: evil. + RabbitVariant(u8), + PigVariant(u64), + CowVariant(u64), + ChickenVariant(ChickenVariant), + FrogVariant(u64), + /// 0: white, 1: creamy, 2: chestnut, 3: brown, 4: black, 5: gray, 6: dark brown. + HorseVariant(u8), + PaintingVariant(PaintingVariant), + /// 0: creamy, 1: white, 2: brown, 3: gray. + LlamaVariant(u8), + /// 0: lucy, 1: wild, 2: gold, 3: cyan, 4: blue. + AxolotlVariant(u8), + CatVariant(u64), + /// Can be one of the following: + /// - 0 - White + /// - 1 - Orange + /// - 2 - Magenta + /// - 3 - Light Blue + /// - 4 - Yellow + /// - 5 - Lime + /// - 6 - Pink + /// - 7 - Gray + /// - 8 - Light Gray + /// - 9 - Cyan + /// - 10 - Purple + /// - 11 - Blue + /// - 12 - Brown + /// - 13 - Green + /// - 14 - Red + /// - 15 - Black + CatCollar(u8), + /// Can be one of the following: + /// - 0 - White + /// - 1 - Orange + /// - 2 - Magenta + /// - 3 - Light Blue + /// - 4 - Yellow + /// - 5 - Lime + /// - 6 - Pink + /// - 7 - Gray + /// - 8 - Light Gray + /// - 9 - Cyan + /// - 10 - Purple + /// - 11 - Blue + /// - 12 - Brown + /// - 13 - Green + /// - 14 - Red + /// - 15 - Black + SheepColor(u8), + /// Can be one of the following: + /// - 0 - White + /// - 1 - Orange + /// - 2 - Magenta + /// - 3 - Light Blue + /// - 4 - Yellow + /// - 5 - Lime + /// - 6 - Pink + /// - 7 - Gray + /// - 8 - Light Gray + /// - 9 - Cyan + /// - 10 - Purple + /// - 11 - Blue + /// - 12 - Brown + /// - 13 - Green + /// - 14 - Red + /// - 15 - Black + ShulkerColor(u8), +} + +pub trait ReadWriteSlotComponent: DataReader + DataWriter { + fn read_slot_component(&mut self) -> Result; + fn write_slot_component(&mut self, val: &SlotComponent) -> Result<(), ServerError>; +} + +impl ReadWriteSlotComponent for Packet { + fn read_slot_component(&mut self) -> Result { + let id = self.read_u16_varint()?; + + todo!() + } + fn write_slot_component(&mut self, val: &SlotComponent) -> Result<(), ServerError> { + self.write_usize_varint(val.enum_index())?; + + todo!() + } +} + +#[derive(Clone)] pub struct Slot { - // TODO: write fields + pub id: i32, + pub amount: i32, + pub components: Vec, } pub trait ReadWriteSlot: DataReader + DataWriter { - fn read_slot(&mut self) -> Result; - fn write_slot(&mut self, val: Slot) -> Result<(), ServerError>; + fn read_slot(&mut self) -> Result, ServerError>; + fn write_slot(&mut self, val: Option) -> Result<(), ServerError>; } +impl ReadWriteSlot for Packet { + fn read_slot(&mut self) -> Result, ServerError> { + let amount = self.read_varint()?; + + if amount > 0 { + let id = self.read_varint()?; + let components_len = self.read_varint()?; + self.read_varint()?; // components_to_remove_len + let mut components = Vec::new(); + for _ in 0..components_len { + components.push(self.read_slot_component()?); + } + + Ok(Some(Slot { + id, + amount, + components, + })) + } else { + Ok(None) + } + } + fn write_slot(&mut self, val: Option) -> Result<(), ServerError> { + if let Some(val) = val { + self.write_varint(val.amount)?; + self.write_varint(val.id)?; + self.write_usize_varint(val.components.len())?; + let mut components_to_remove: Vec = (0..SLOT_COMPONENT_LENGTH).collect(); + self.write_usize_varint(SLOT_COMPONENT_LENGTH as usize - val.components.len())?; + for comp in val.components { + let id = comp.enum_index() as u16; + self.write_slot_component(&comp)?; + if let Some(index) = components_to_remove.iter().position(|v| *v == id) { + components_to_remove.swap_remove(index); + } + } + for id in components_to_remove { + self.write_u16_varint(id)?; + } + } else { + self.write_varint(0)?; + } + Ok(()) + } +} + +#[derive(Clone)] pub struct HashedSlot { - // TODO: write fields + pub id: i32, + pub amount: i32, + /// id -> crc32 hash + pub components: Vec<(u16, i32)>, } pub trait ReadWriteHashedSlot: DataReader + DataWriter { - fn read_hashed_slot(&mut self) -> Result; - fn write_hashed_slot(&mut self, val: HashedSlot) -> Result<(), ServerError>; + fn read_hashed_slot(&mut self) -> Result, ServerError>; + fn write_hashed_slot(&mut self, val: Option) -> Result<(), ServerError>; } -// TODO: implement traits for packet +impl ReadWriteHashedSlot for Packet { + fn read_hashed_slot(&mut self) -> Result, ServerError> { + let amount = self.read_varint()?; + + if amount > 0 { + let id = self.read_varint()?; + let components_len = self.read_varint()?; + self.read_varint()?; // components_to_remove_len + let mut components = Vec::new(); + for _ in 0..components_len { + components.push((self.read_u16_varint()?, self.read_int()?)); + } + + Ok(Some(HashedSlot { + id, + amount, + components, + })) + } else { + Ok(None) + } + } + fn write_hashed_slot(&mut self, val: Option) -> Result<(), ServerError> { + if let Some(val) = val { + self.write_varint(val.amount)?; + self.write_varint(val.id)?; + self.write_usize_varint(val.components.len())?; + let mut components_to_remove: Vec = (0..SLOT_COMPONENT_LENGTH).collect(); + self.write_usize_varint(SLOT_COMPONENT_LENGTH as usize - val.components.len())?; + for (id, hash) in val.components { + self.write_u16_varint(id)?; + self.write_int(hash)?; + if let Some(index) = components_to_remove.iter().position(|v| *v == id) { + components_to_remove.swap_remove(index); + } + } + for id in components_to_remove { + self.write_u16_varint(id)?; + } + } else { + self.write_varint(0)?; + } + Ok(()) + } +} diff --git a/src/data/sound.rs b/src/data/sound.rs new file mode 100644 index 0000000..3467693 --- /dev/null +++ b/src/data/sound.rs @@ -0,0 +1,2 @@ +#[derive(Clone)] +pub struct SoundEvent;