diff options
| author | mat <git@matdoes.dev> | 2025-08-18 16:42:48 -0545 |
|---|---|---|
| committer | mat <git@matdoes.dev> | 2025-08-18 16:42:48 -0545 |
| commit | 9c39e05d37926de13588777ecd84f34d2f6e0a1d (patch) | |
| tree | ae6427df80f5e96a1e9f7370de30d398d917c5fa | |
| parent | c2ada754f9888fe791a5f604534c55cc6da71590 (diff) | |
| download | azalea-drasl-9c39e05d37926de13588777ecd84f34d2f6e0a1d.tar.xz | |
fix ClientboundMerchantOffers
| -rw-r--r-- | azalea-client/src/plugins/interact/mod.rs | 37 | ||||
| -rw-r--r-- | azalea-inventory/src/components.rs | 10 | ||||
| -rw-r--r-- | azalea-inventory/src/slot.rs | 11 | ||||
| -rw-r--r-- | azalea-protocol/src/packets/game/c_merchant_offers.rs | 117 | ||||
| -rw-r--r-- | azalea/src/pathfinder/goto_event.rs | 5 |
5 files changed, 155 insertions, 25 deletions
diff --git a/azalea-client/src/plugins/interact/mod.rs b/azalea-client/src/plugins/interact/mod.rs index 0275ca97..547d58ff 100644 --- a/azalea-client/src/plugins/interact/mod.rs +++ b/azalea-client/src/plugins/interact/mod.rs @@ -11,14 +11,14 @@ use azalea_core::{ tick::GameTick, }; use azalea_entity::{ - Attributes, Crouching, LocalEntity, LookDirection, PlayerAbilities, + Attributes, LocalEntity, LookDirection, PlayerAbilities, attributes::{ creative_block_interaction_range_modifier, creative_entity_interaction_range_modifier, }, clamp_look_direction, }; use azalea_inventory::{ItemStack, ItemStackData, components}; -use azalea_physics::PhysicsSet; +use azalea_physics::{PhysicsSet, local_player::PhysicsState}; use azalea_protocol::packets::game::{ ServerboundInteract, ServerboundUseItem, s_interact::{self, InteractionHand}, @@ -250,7 +250,7 @@ pub fn handle_start_use_item_queued( &mut BlockStatePredictionHandler, &HitResultComponent, &LookDirection, - &Crouching, + &PhysicsState, Option<&Mining>, )>, entity_id_query: Query<&MinecraftEntityId>, @@ -261,7 +261,7 @@ pub fn handle_start_use_item_queued( mut prediction_handler, hit_result, look_direction, - crouching, + physics_state, mining, ) in query { @@ -332,17 +332,26 @@ pub fn handle_start_use_item_queued( continue; }; - commands.trigger(SendPacketEvent::new( - entity, - ServerboundInteract { - entity_id, - action: s_interact::ActionType::InteractAt { - location: r.location, - hand: InteractionHand::MainHand, - }, - using_secondary_action: **crouching, + let mut interact = ServerboundInteract { + entity_id, + action: s_interact::ActionType::InteractAt { + location: r.location, + hand: InteractionHand::MainHand, }, - )); + using_secondary_action: physics_state.trying_to_crouch, + }; + commands.trigger(SendPacketEvent::new(entity, interact.clone())); + // TODO: this is true if the interaction failed, which i think can only happen + // in certain cases when interacting with armor stands + let consumes_action = false; + if !consumes_action { + // but yes, most of the time vanilla really does send two interact packets like + // this + interact.action = s_interact::ActionType::Interact { + hand: InteractionHand::MainHand, + }; + commands.trigger(SendPacketEvent::new(entity, interact)); + } } } } diff --git a/azalea-inventory/src/components.rs b/azalea-inventory/src/components.rs index 3da393e5..f63ca1ad 100644 --- a/azalea-inventory/src/components.rs +++ b/azalea-inventory/src/components.rs @@ -130,11 +130,15 @@ macro_rules! define_data_components { pub unsafe fn azalea_write_as( &self, kind: registry::DataComponentKind, - buf: &mut Vec<u8>, + buf: &mut impl std::io::Write, ) -> io::Result<()> { + let mut value = Vec::new(); match kind { - $( DataComponentKind::$x => unsafe { self.$x.encode(buf) }, )* - } + $( DataComponentKind::$x => unsafe { self.$x.encode(&mut value)? }, )* + }; + buf.write_all(&value)?; + + Ok(()) } /// # Safety /// diff --git a/azalea-inventory/src/slot.rs b/azalea-inventory/src/slot.rs index e9da4856..2d4202e8 100644 --- a/azalea-inventory/src/slot.rs +++ b/azalea-inventory/src/slot.rs @@ -349,6 +349,17 @@ impl DataComponentPatch { ) }) } + /// Insert a new component into this patch, or mark a component as removed. + /// + /// # Safety + /// The [`DataComponentUnion`] must be of the correct kind. + pub unsafe fn unchecked_insert_component( + &mut self, + kind: DataComponentKind, + value: Option<DataComponentUnion>, + ) { + self.components.insert(kind, value); + } } impl Drop for DataComponentPatch { diff --git a/azalea-protocol/src/packets/game/c_merchant_offers.rs b/azalea-protocol/src/packets/game/c_merchant_offers.rs index f50f9932..8d4ba9b1 100644 --- a/azalea-protocol/src/packets/game/c_merchant_offers.rs +++ b/azalea-protocol/src/packets/game/c_merchant_offers.rs @@ -1,6 +1,16 @@ -use azalea_buf::AzBuf; -use azalea_inventory::ItemStack; +use std::{ + any::Any, + fmt::{self, Debug}, + io::{self, Cursor, Write}, +}; + +use azalea_buf::{AzBuf, AzaleaRead, AzaleaWrite, BufReadError}; +use azalea_inventory::{ + DataComponentPatch, ItemStack, ItemStackData, + components::{self, DataComponentUnion}, +}; use azalea_protocol_macros::ClientboundGamePacket; +use azalea_registry::{DataComponentKind, Item}; #[derive(Clone, Debug, AzBuf, ClientboundGamePacket)] pub struct ClientboundMerchantOffers { @@ -17,14 +27,105 @@ pub struct ClientboundMerchantOffers { #[derive(Clone, Debug, AzBuf)] pub struct MerchantOffer { - pub base_cost_a: ItemStack, + pub base_cost_a: ItemCost, pub result: ItemStack, - pub cost_b: ItemStack, + pub cost_b: Option<ItemCost>, pub out_of_stock: bool, - pub uses: u32, - pub max_uses: u32, - pub xp: u32, + pub uses: i32, + pub max_uses: i32, + pub xp: i32, pub special_price_diff: i32, pub price_multiplier: f32, - pub demand: u32, + pub demand: i32, +} + +/// An item that a merchant can buy. +/// +/// This can be converted into an [`ItemStackData`] with +/// [`Self::into_item_stack`]. +#[derive(Clone, Debug, AzBuf)] +pub struct ItemCost { + pub item: Item, + #[var] + pub count: i32, + pub components: DataComponentExactPredicate, +} +impl ItemCost { + pub fn into_item_stack(self) -> ItemStackData { + let mut component_patch = DataComponentPatch::default(); + for component in self.components.expected { + unsafe { + component_patch.unchecked_insert_component(component.kind, Some(component.value)); + } + } + // TODO: add a fast way to iterate over default components, and insert the ones + // that aren't present as None + + ItemStackData { + kind: self.item, + count: self.count, + component_patch, + } + } +} + +/// Similar to [`DataComponentPatch`], but it's only additive, meaning that +/// there are no `None` values. +/// +/// If you got this from [`ItemCost`], consider using +/// [`ItemCost::into_item_stack`] for a better API instead. +#[derive(Clone, Debug, AzBuf)] +pub struct DataComponentExactPredicate { + pub expected: Vec<TypedDataComponent>, +} + +pub struct TypedDataComponent { + kind: DataComponentKind, + value: DataComponentUnion, +} +impl TypedDataComponent { + pub fn kind(&self) -> &DataComponentKind { + &self.kind + } + pub fn value(&self) -> &DataComponentUnion { + &self.value + } + pub fn as_dyn(&self) -> &dyn components::EncodableDataComponent { + // SAFETY: the kind is correct because we got it from azalea_read_as, and the + // kind isn't mutable + unsafe { self.value.as_kind(self.kind) } + } + pub fn get<T: components::DataComponentTrait>(&self) -> Option<&T> { + let component = self.as_dyn(); + let component_any = component as &dyn Any; + component_any.downcast_ref::<T>() + } +} +impl AzaleaRead for TypedDataComponent { + fn azalea_read(buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError> { + let kind = DataComponentKind::azalea_read(buf)?; + let value = DataComponentUnion::azalea_read_as(kind, buf)?; + Ok(Self { kind, value }) + } +} +impl AzaleaWrite for TypedDataComponent { + fn azalea_write(&self, buf: &mut impl Write) -> io::Result<()> { + self.kind.azalea_write(buf)?; + unsafe { self.value.azalea_write_as(self.kind, buf) } + } +} +impl Debug for TypedDataComponent { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("TypedDataComponent") + .field("kind", &self.kind) + .finish() + } +} +impl Clone for TypedDataComponent { + fn clone(&self) -> Self { + Self { + kind: self.kind, + value: unsafe { self.value.clone_as(self.kind) }, + } + } } diff --git a/azalea/src/pathfinder/goto_event.rs b/azalea/src/pathfinder/goto_event.rs index 8a4f78d0..bd0e1540 100644 --- a/azalea/src/pathfinder/goto_event.rs +++ b/azalea/src/pathfinder/goto_event.rs @@ -115,3 +115,8 @@ impl PathfinderOpts { self } } +impl Default for PathfinderOpts { + fn default() -> Self { + Self::new() + } +} |
