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 /azalea-protocol/src | |
| parent | c2ada754f9888fe791a5f604534c55cc6da71590 (diff) | |
| download | azalea-drasl-9c39e05d37926de13588777ecd84f34d2f6e0a1d.tar.xz | |
fix ClientboundMerchantOffers
Diffstat (limited to 'azalea-protocol/src')
| -rw-r--r-- | azalea-protocol/src/packets/game/c_merchant_offers.rs | 117 |
1 files changed, 109 insertions, 8 deletions
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) }, + } + } } |
