Skip to main content

azalea_protocol/packets/game/
c_merchant_offers.rs

1use std::{
2    any::Any,
3    fmt::{self, Debug},
4    io::{self, Cursor, Write},
5    mem::ManuallyDrop,
6};
7
8use azalea_buf::{AzBuf, BufReadError};
9use azalea_inventory::{
10    DataComponentPatch, ItemStack, ItemStackData,
11    components::{self, DataComponentUnion},
12};
13use azalea_protocol_macros::ClientboundGamePacket;
14use azalea_registry::builtin::{DataComponentKind, ItemKind};
15
16#[derive(AzBuf, ClientboundGamePacket, Clone, Debug, PartialEq)]
17pub struct ClientboundMerchantOffers {
18    #[var]
19    pub container_id: i32,
20    pub offers: Vec<MerchantOffer>,
21    #[var]
22    pub villager_level: u32,
23    #[var]
24    pub villager_xp: u32,
25    pub show_progress: bool,
26    pub can_restock: bool,
27}
28
29#[derive(AzBuf, Clone, Debug, PartialEq)]
30pub struct MerchantOffer {
31    pub base_cost_a: ItemCost,
32    pub result: ItemStack,
33    pub cost_b: Option<ItemCost>,
34    pub out_of_stock: bool,
35    pub uses: i32,
36    pub max_uses: i32,
37    pub xp: i32,
38    pub special_price_diff: i32,
39    pub price_multiplier: f32,
40    pub demand: i32,
41}
42
43/// An item that a merchant can buy.
44///
45/// This can be converted into an [`ItemStackData`] with
46/// [`Self::into_item_stack`].
47#[derive(AzBuf, Clone, Debug, PartialEq)]
48pub struct ItemCost {
49    pub item: ItemKind,
50    #[var]
51    pub count: i32,
52    pub components: DataComponentExactPredicate,
53}
54impl ItemCost {
55    pub fn into_item_stack(self) -> ItemStackData {
56        let mut component_patch = DataComponentPatch::default();
57        for component in self.components.expected {
58            let component = ManuallyDrop::new(component);
59            // SAFETY: DataComponentUnion does not run any destructors unless it's dropped
60            // through drop_as, so since TypedDataComponent is now ManuallyDrop, the value
61            // will stay in memory.
62            let value = unsafe { std::ptr::read(&component.value) };
63            unsafe {
64                component_patch.unchecked_insert_component(component.kind, Some(value));
65            }
66        }
67        // TODO: add a fast way to iterate over default components, and insert the ones
68        // that aren't present as None
69
70        ItemStackData {
71            kind: self.item,
72            count: self.count,
73            component_patch,
74        }
75    }
76}
77
78/// Similar to [`DataComponentPatch`], but it's only additive, meaning that
79/// there are no `None` values.
80///
81/// If you got this from [`ItemCost`], consider using
82/// [`ItemCost::into_item_stack`] for a better API instead.
83#[derive(AzBuf, Clone, Debug, PartialEq)]
84pub struct DataComponentExactPredicate {
85    pub expected: Vec<TypedDataComponent>,
86}
87
88pub struct TypedDataComponent {
89    kind: DataComponentKind,
90    value: DataComponentUnion,
91}
92impl TypedDataComponent {
93    pub fn kind(&self) -> &DataComponentKind {
94        &self.kind
95    }
96    pub fn value(&self) -> &DataComponentUnion {
97        &self.value
98    }
99    pub fn as_dyn(&self) -> &dyn components::EncodableDataComponent {
100        // SAFETY: the kind is correct because we got it from azalea_read_as, and the
101        // kind isn't mutable
102        unsafe { self.value.as_kind(self.kind) }
103    }
104    pub fn get<T: components::DataComponentTrait>(&self) -> Option<&T> {
105        let component = self.as_dyn();
106        let component_any = component as &dyn Any;
107        component_any.downcast_ref::<T>()
108    }
109}
110impl AzBuf for TypedDataComponent {
111    fn azalea_read(buf: &mut Cursor<&[u8]>) -> Result<Self, BufReadError> {
112        let kind = DataComponentKind::azalea_read(buf)?;
113        let value = DataComponentUnion::azalea_read_as(kind, buf)?;
114        Ok(Self { kind, value })
115    }
116    fn azalea_write(&self, buf: &mut impl Write) -> io::Result<()> {
117        self.kind.azalea_write(buf)?;
118        unsafe { self.value.azalea_write_as(self.kind, buf) }
119    }
120}
121impl Debug for TypedDataComponent {
122    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
123        f.debug_struct("TypedDataComponent")
124            .field("kind", &self.kind)
125            .finish()
126    }
127}
128impl Clone for TypedDataComponent {
129    fn clone(&self) -> Self {
130        Self {
131            kind: self.kind,
132            value: unsafe { self.value.clone_as(self.kind) },
133        }
134    }
135}
136impl Drop for TypedDataComponent {
137    fn drop(&mut self) {
138        unsafe { self.value.drop_as(self.kind) };
139    }
140}
141impl PartialEq for TypedDataComponent {
142    fn eq(&self, other: &Self) -> bool {
143        if self.kind != other.kind {
144            return false;
145        }
146        self.as_dyn().eq(other.as_dyn())
147    }
148}