aboutsummaryrefslogtreecommitdiff
path: root/azalea-protocol/src/packets
diff options
context:
space:
mode:
authormat <git@matdoes.dev>2025-08-18 16:42:48 -0545
committermat <git@matdoes.dev>2025-08-18 16:42:48 -0545
commit9c39e05d37926de13588777ecd84f34d2f6e0a1d (patch)
treeae6427df80f5e96a1e9f7370de30d398d917c5fa /azalea-protocol/src/packets
parentc2ada754f9888fe791a5f604534c55cc6da71590 (diff)
downloadazalea-drasl-9c39e05d37926de13588777ecd84f34d2f6e0a1d.tar.xz
fix ClientboundMerchantOffers
Diffstat (limited to 'azalea-protocol/src/packets')
-rw-r--r--azalea-protocol/src/packets/game/c_merchant_offers.rs117
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) },
+ }
+ }
}