aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormat <git@matdoes.dev>2025-06-03 17:10:34 -1300
committermat <git@matdoes.dev>2025-06-03 17:10:34 -1300
commitbbfca34133fdb97a732a60a0a11ca4ba3276a63a (patch)
tree28598b793c36c04a7cab512b3d488aa5eaacb3cc
parentf5f50b85e5d427aab6a0ef00570b4076b61babe8 (diff)
downloadazalea-drasl-bbfca34133fdb97a732a60a0a11ca4ba3276a63a.tar.xz
copy player part of container_menu to inventory_menu on close
-rw-r--r--azalea-client/src/plugins/inventory.rs26
-rw-r--r--azalea-inventory/src/lib.rs19
2 files changed, 44 insertions, 1 deletions
diff --git a/azalea-client/src/plugins/inventory.rs b/azalea-client/src/plugins/inventory.rs
index 29a81410..8efc0e71 100644
--- a/azalea-client/src/plugins/inventory.rs
+++ b/azalea-client/src/plugins/inventory.rs
@@ -798,7 +798,31 @@ pub fn handle_client_side_close_container_event(
) {
for event in events.read() {
let mut inventory = query.get_mut(event.entity).unwrap();
- inventory.container_menu = None;
+
+ // copy the Player part of the container_menu to the inventory_menu
+ if let Some(inventory_menu) = inventory.container_menu.take() {
+ // this isn't the same as what vanilla does. i believe vanilla synchronizes the
+ // slots between inventoryMenu and containerMenu by just having the player slots
+ // point to the same ItemStack in memory, but emulating this in rust would
+ // require us to wrap our `ItemStack`s as `Arc<Mutex<ItemStack>>` which would
+ // have kinda terrible ergonomics.
+
+ // the simpler solution i chose to go with here is to only copy the player slots
+ // when the container is closed. this is perfectly fine for vanilla, but it
+ // might cause issues if a server modifies id 0 while we have a container
+ // open...
+
+ // if we do encounter this issue in the wild then the simplest solution would
+ // probably be to just add logic for updating the container_menu when the server
+ // tries to modify id 0 for slots within `inventory`. not implemented for now
+ // because i'm not sure if that's worth worrying about.
+
+ let new_inventory =
+ inventory_menu.slots()[inventory_menu.player_slots_range()].to_vec();
+ let new_inventory = <[ItemStack; 36]>::try_from(new_inventory).unwrap();
+ *inventory.inventory_menu.as_player_mut().inventory = new_inventory;
+ }
+
inventory.id = 0;
inventory.container_menu_title = None;
}
diff --git a/azalea-inventory/src/lib.rs b/azalea-inventory/src/lib.rs
index 91d9f48d..0f74ba3a 100644
--- a/azalea-inventory/src/lib.rs
+++ b/azalea-inventory/src/lib.rs
@@ -32,6 +32,11 @@ impl<const N: usize> Default for SlotList<N> {
SlotList([(); N].map(|_| ItemStack::Empty))
}
}
+impl<const N: usize> SlotList<N> {
+ pub fn new(items: [ItemStack; N]) -> Self {
+ SlotList(items)
+ }
+}
impl Menu {
/// Get the [`Player`] from this [`Menu`].
@@ -46,6 +51,20 @@ impl Menu {
unreachable!("Called `Menu::as_player` on a menu that wasn't `Player`.")
}
}
+
+ /// Same as [`Menu::as_player`], but returns a mutable reference to the
+ /// [`Player`].
+ ///
+ /// # Panics
+ ///
+ /// Will panic if the menu isn't `Menu::Player`.
+ pub fn as_player_mut(&mut self) -> &mut Player {
+ if let Menu::Player(player) = self {
+ player
+ } else {
+ unreachable!("Called `Menu::as_player_mut` on a menu that wasn't `Player`.")
+ }
+ }
}
// the player inventory part is always the last 36 slots (except in the Player