azalea_inventory/
operations.rs

1use std::ops::RangeInclusive;
2
3use azalea_buf::AzBuf;
4
5use crate::{
6    item::MaxStackSizeExt, AnvilMenuLocation, BeaconMenuLocation, BlastFurnaceMenuLocation,
7    BrewingStandMenuLocation, CartographyTableMenuLocation, Crafter3x3MenuLocation,
8    CraftingMenuLocation, EnchantmentMenuLocation, FurnaceMenuLocation, Generic3x3MenuLocation,
9    Generic9x1MenuLocation, Generic9x2MenuLocation, Generic9x3MenuLocation, Generic9x4MenuLocation,
10    Generic9x5MenuLocation, Generic9x6MenuLocation, GrindstoneMenuLocation, HopperMenuLocation,
11    ItemStack, ItemStackData, LecternMenuLocation, LoomMenuLocation, Menu, MenuLocation,
12    MerchantMenuLocation, Player, PlayerMenuLocation, ShulkerBoxMenuLocation, SmithingMenuLocation,
13    SmokerMenuLocation, StonecutterMenuLocation,
14};
15
16#[derive(Debug, Clone)]
17pub enum ClickOperation {
18    Pickup(PickupClick),
19    QuickMove(QuickMoveClick),
20    Swap(SwapClick),
21    Clone(CloneClick),
22    Throw(ThrowClick),
23    QuickCraft(QuickCraftClick),
24    PickupAll(PickupAllClick),
25}
26
27#[derive(Debug, Clone)]
28pub enum PickupClick {
29    /// Left mouse click. Note that in the protocol, None is represented as
30    /// -999.
31    Left { slot: Option<u16> },
32    /// Right mouse click. Note that in the protocol, None is represented as
33    /// -999.
34    Right { slot: Option<u16> },
35    /// Drop cursor stack.
36    LeftOutside,
37    /// Drop cursor single item.
38    RightOutside,
39}
40impl From<PickupClick> for ClickOperation {
41    fn from(click: PickupClick) -> Self {
42        ClickOperation::Pickup(click)
43    }
44}
45
46/// Shift click
47#[derive(Debug, Clone)]
48pub enum QuickMoveClick {
49    /// Shift + left mouse click
50    Left { slot: u16 },
51    /// Shift + right mouse click (identical behavior)
52    Right { slot: u16 },
53}
54impl From<QuickMoveClick> for ClickOperation {
55    fn from(click: QuickMoveClick) -> Self {
56        ClickOperation::QuickMove(click)
57    }
58}
59
60/// Used when you press number keys or F in an inventory.
61#[derive(Debug, Clone)]
62pub struct SwapClick {
63    pub source_slot: u16,
64    pub target_slot: u8,
65}
66
67impl From<SwapClick> for ClickOperation {
68    fn from(click: SwapClick) -> Self {
69        ClickOperation::Swap(click)
70    }
71}
72/// Middle click, only defined for creative players in non-player
73/// inventories.
74#[derive(Debug, Clone)]
75pub struct CloneClick {
76    pub slot: u16,
77}
78impl From<CloneClick> for ClickOperation {
79    fn from(click: CloneClick) -> Self {
80        ClickOperation::Clone(click)
81    }
82}
83#[derive(Debug, Clone)]
84pub enum ThrowClick {
85    /// Drop key (Q)
86    Single { slot: u16 },
87    /// Ctrl + drop key (Q)
88    All { slot: u16 },
89}
90impl From<ThrowClick> for ClickOperation {
91    fn from(click: ThrowClick) -> Self {
92        ClickOperation::Throw(click)
93    }
94}
95#[derive(Debug, Clone, Eq, PartialEq)]
96pub struct QuickCraftClick {
97    pub kind: QuickCraftKind,
98    pub status: QuickCraftStatus,
99}
100#[derive(Debug, Clone, Eq, PartialEq)]
101pub enum QuickCraftKind {
102    Left,
103    Right,
104    Middle,
105}
106#[derive(Debug, Clone, Eq, PartialEq)]
107pub enum QuickCraftStatusKind {
108    /// Starting drag
109    Start,
110    /// Add slot
111    Add,
112    /// Ending drag
113    End,
114}
115#[derive(Debug, Clone, Eq, PartialEq)]
116pub enum QuickCraftStatus {
117    /// Starting drag
118    Start,
119    /// Add a slot.
120    Add { slot: u16 },
121    /// Ending drag
122    End,
123}
124impl From<QuickCraftStatus> for QuickCraftStatusKind {
125    fn from(status: QuickCraftStatus) -> Self {
126        match status {
127            QuickCraftStatus::Start => QuickCraftStatusKind::Start,
128            QuickCraftStatus::Add { .. } => QuickCraftStatusKind::Add,
129            QuickCraftStatus::End => QuickCraftStatusKind::End,
130        }
131    }
132}
133
134/// Double click
135#[derive(Debug, Clone)]
136pub struct PickupAllClick {
137    /// The slot that we're double clicking on. It should be empty or at least
138    /// not pickup-able (since the carried item is used as the filter).
139    pub slot: u16,
140    /// Impossible in vanilla clients.
141    pub reversed: bool,
142}
143impl From<PickupAllClick> for ClickOperation {
144    fn from(click: PickupAllClick) -> Self {
145        ClickOperation::PickupAll(click)
146    }
147}
148
149impl ClickOperation {
150    /// Return the slot number that this operation is acting on, if any.
151    ///
152    /// Note that in the protocol, "None" is represented as -999.
153    pub fn slot_num(&self) -> Option<u16> {
154        match self {
155            ClickOperation::Pickup(pickup) => match pickup {
156                PickupClick::Left { slot } => *slot,
157                PickupClick::Right { slot } => *slot,
158                PickupClick::LeftOutside => None,
159                PickupClick::RightOutside => None,
160            },
161            ClickOperation::QuickMove(quick_move) => match quick_move {
162                QuickMoveClick::Left { slot } => Some(*slot),
163                QuickMoveClick::Right { slot } => Some(*slot),
164            },
165            ClickOperation::Swap(swap) => Some(swap.source_slot),
166            ClickOperation::Clone(clone) => Some(clone.slot),
167            ClickOperation::Throw(throw) => match throw {
168                ThrowClick::Single { slot } => Some(*slot),
169                ThrowClick::All { slot } => Some(*slot),
170            },
171            ClickOperation::QuickCraft(quick_craft) => match quick_craft.status {
172                QuickCraftStatus::Start => None,
173                QuickCraftStatus::Add { slot } => Some(slot),
174                QuickCraftStatus::End => None,
175            },
176            ClickOperation::PickupAll(pickup_all) => Some(pickup_all.slot),
177        }
178    }
179
180    pub fn button_num(&self) -> u8 {
181        match self {
182            ClickOperation::Pickup(pickup) => match pickup {
183                PickupClick::Left { .. } => 0,
184                PickupClick::Right { .. } => 1,
185                PickupClick::LeftOutside => 0,
186                PickupClick::RightOutside => 1,
187            },
188            ClickOperation::QuickMove(quick_move) => match quick_move {
189                QuickMoveClick::Left { .. } => 0,
190                QuickMoveClick::Right { .. } => 1,
191            },
192            ClickOperation::Swap(swap) => swap.target_slot,
193            ClickOperation::Clone(_) => 2,
194            ClickOperation::Throw(throw) => match throw {
195                ThrowClick::Single { .. } => 0,
196                ThrowClick::All { .. } => 1,
197            },
198            ClickOperation::QuickCraft(quick_craft) => match quick_craft {
199                QuickCraftClick {
200                    kind: QuickCraftKind::Left,
201                    status: QuickCraftStatus::Start,
202                } => 0,
203                QuickCraftClick {
204                    kind: QuickCraftKind::Right,
205                    status: QuickCraftStatus::Start,
206                } => 4,
207                QuickCraftClick {
208                    kind: QuickCraftKind::Middle,
209                    status: QuickCraftStatus::Start,
210                } => 8,
211                QuickCraftClick {
212                    kind: QuickCraftKind::Left,
213                    status: QuickCraftStatus::Add { .. },
214                } => 1,
215                QuickCraftClick {
216                    kind: QuickCraftKind::Right,
217                    status: QuickCraftStatus::Add { .. },
218                } => 5,
219                QuickCraftClick {
220                    kind: QuickCraftKind::Middle,
221                    status: QuickCraftStatus::Add { .. },
222                } => 9,
223                QuickCraftClick {
224                    kind: QuickCraftKind::Left,
225                    status: QuickCraftStatus::End,
226                } => 2,
227                QuickCraftClick {
228                    kind: QuickCraftKind::Right,
229                    status: QuickCraftStatus::End,
230                } => 6,
231                QuickCraftClick {
232                    kind: QuickCraftKind::Middle,
233                    status: QuickCraftStatus::End,
234                } => 10,
235            },
236            ClickOperation::PickupAll(_) => 0,
237        }
238    }
239
240    pub fn click_type(&self) -> ClickType {
241        match self {
242            ClickOperation::Pickup(_) => ClickType::Pickup,
243            ClickOperation::QuickMove(_) => ClickType::QuickMove,
244            ClickOperation::Swap(_) => ClickType::Swap,
245            ClickOperation::Clone(_) => ClickType::Clone,
246            ClickOperation::Throw(_) => ClickType::Throw,
247            ClickOperation::QuickCraft(_) => ClickType::QuickCraft,
248            ClickOperation::PickupAll(_) => ClickType::PickupAll,
249        }
250    }
251}
252
253#[derive(AzBuf, Clone, Copy, Debug)]
254pub enum ClickType {
255    Pickup = 0,
256    QuickMove = 1,
257    Swap = 2,
258    Clone = 3,
259    Throw = 4,
260    QuickCraft = 5,
261    PickupAll = 6,
262}
263
264impl Menu {
265    /// Shift-click a slot in this menu.
266    ///
267    /// Keep in mind that this doesn't send any packets to the server, it just
268    /// mutates this specific `Menu`.
269    pub fn quick_move_stack(&mut self, slot_index: usize) -> ItemStack {
270        let slot = self.slot(slot_index);
271        if slot.is_none() {
272            return ItemStack::Empty;
273        };
274
275        let slot_location = self
276            .location_for_slot(slot_index)
277            .expect("we just checked to make sure the slot is Some above, so this shouldn't be able to error");
278        match slot_location {
279            MenuLocation::Player(l) => match l {
280                PlayerMenuLocation::CraftResult => {
281                    self.try_move_item_to_slots(slot_index, Player::INVENTORY_SLOTS);
282                }
283                PlayerMenuLocation::Craft => {
284                    self.try_move_item_to_slots(slot_index, Player::INVENTORY_SLOTS);
285                }
286                PlayerMenuLocation::Armor => {
287                    self.try_move_item_to_slots(slot_index, Player::INVENTORY_SLOTS);
288                }
289                _ => {
290                    // TODO: armor handling (see quickMoveStack in
291                    // InventoryMenu.java)
292
293                    // if slot.kind().is_armor() &&
294
295                    // also offhand handling
296
297                    if l == PlayerMenuLocation::Inventory {
298                        // shift-clicking in hotbar moves to inventory, and vice versa
299                        if Player::is_hotbar_slot(slot_index) {
300                            self.try_move_item_to_slots(
301                                slot_index,
302                                Player::INVENTORY_WITHOUT_HOTBAR_SLOTS,
303                            );
304                        } else {
305                            self.try_move_item_to_slots(slot_index, Player::HOTBAR_SLOTS);
306                        }
307                    } else {
308                        self.try_move_item_to_slots(slot_index, self.player_slots_range());
309                    }
310                }
311            },
312            MenuLocation::Generic9x1(l) => match l {
313                Generic9x1MenuLocation::Contents => {
314                    self.try_move_item_to_slots(slot_index, self.player_slots_range());
315                }
316                Generic9x1MenuLocation::Player => {
317                    self.try_move_item_to_slots_or_toggle_hotbar(
318                        slot_index,
319                        Menu::GENERIC9X1_CONTENTS_SLOTS,
320                    );
321                }
322            },
323            MenuLocation::Generic9x2(l) => match l {
324                Generic9x2MenuLocation::Contents => {
325                    self.try_move_item_to_slots(slot_index, self.player_slots_range());
326                }
327                Generic9x2MenuLocation::Player => {
328                    self.try_move_item_to_slots_or_toggle_hotbar(
329                        slot_index,
330                        Menu::GENERIC9X2_CONTENTS_SLOTS,
331                    );
332                }
333            },
334            MenuLocation::Generic9x3(l) => match l {
335                Generic9x3MenuLocation::Contents => {
336                    self.try_move_item_to_slots(slot_index, self.player_slots_range());
337                }
338                Generic9x3MenuLocation::Player => {
339                    self.try_move_item_to_slots_or_toggle_hotbar(
340                        slot_index,
341                        Menu::GENERIC9X3_CONTENTS_SLOTS,
342                    );
343                }
344            },
345            MenuLocation::Generic9x4(l) => match l {
346                Generic9x4MenuLocation::Contents => {
347                    self.try_move_item_to_slots(slot_index, self.player_slots_range());
348                }
349                Generic9x4MenuLocation::Player => {
350                    self.try_move_item_to_slots_or_toggle_hotbar(
351                        slot_index,
352                        Menu::GENERIC9X4_CONTENTS_SLOTS,
353                    );
354                }
355            },
356            MenuLocation::Generic9x5(l) => match l {
357                Generic9x5MenuLocation::Contents => {
358                    self.try_move_item_to_slots(slot_index, self.player_slots_range());
359                }
360                Generic9x5MenuLocation::Player => {
361                    self.try_move_item_to_slots_or_toggle_hotbar(
362                        slot_index,
363                        Menu::GENERIC9X5_CONTENTS_SLOTS,
364                    );
365                }
366            },
367            MenuLocation::Generic9x6(l) => match l {
368                Generic9x6MenuLocation::Contents => {
369                    self.try_move_item_to_slots(slot_index, self.player_slots_range());
370                }
371                Generic9x6MenuLocation::Player => {
372                    self.try_move_item_to_slots_or_toggle_hotbar(
373                        slot_index,
374                        Menu::GENERIC9X6_CONTENTS_SLOTS,
375                    );
376                }
377            },
378            MenuLocation::Generic3x3(l) => match l {
379                Generic3x3MenuLocation::Contents => {
380                    self.try_move_item_to_slots(slot_index, self.player_slots_range());
381                }
382                Generic3x3MenuLocation::Player => {
383                    self.try_move_item_to_slots_or_toggle_hotbar(
384                        slot_index,
385                        Menu::GENERIC3X3_CONTENTS_SLOTS,
386                    );
387                }
388            },
389            MenuLocation::Crafter3x3(l) => match l {
390                Crafter3x3MenuLocation::Contents => {
391                    self.try_move_item_to_slots(slot_index, self.player_slots_range());
392                }
393                Crafter3x3MenuLocation::Player => {
394                    self.try_move_item_to_slots_or_toggle_hotbar(
395                        slot_index,
396                        Menu::GENERIC3X3_CONTENTS_SLOTS,
397                    );
398                }
399            },
400            MenuLocation::Anvil(l) => match l {
401                AnvilMenuLocation::Player => {
402                    self.try_move_item_to_slots_or_toggle_hotbar(
403                        slot_index,
404                        Menu::ANVIL_FIRST_SLOT..=Menu::ANVIL_SECOND_SLOT,
405                    );
406                }
407                _ => {
408                    self.try_move_item_to_slots(slot_index, self.player_slots_range());
409                }
410            },
411            MenuLocation::Beacon(l) => match l {
412                BeaconMenuLocation::Payment => {
413                    self.try_move_item_to_slots(slot_index, self.player_slots_range());
414                }
415                BeaconMenuLocation::Player => {
416                    self.try_move_item_to_slots(
417                        slot_index,
418                        Menu::BEACON_PAYMENT_SLOT..=Menu::BEACON_PAYMENT_SLOT,
419                    );
420                }
421            },
422            MenuLocation::BlastFurnace(l) => match l {
423                BlastFurnaceMenuLocation::Player => {
424                    self.try_move_item_to_slots(
425                        slot_index,
426                        Menu::BLAST_FURNACE_INGREDIENT_SLOT..=Menu::BLAST_FURNACE_FUEL_SLOT,
427                    );
428                }
429                _ => {
430                    self.try_move_item_to_slots(slot_index, self.player_slots_range());
431                }
432            },
433            MenuLocation::BrewingStand(l) => match l {
434                BrewingStandMenuLocation::Player => {
435                    self.try_move_item_to_slots(
436                        slot_index,
437                        *Menu::BREWING_STAND_BOTTLES_SLOTS.start()
438                            ..=Menu::BREWING_STAND_INGREDIENT_SLOT,
439                    );
440                }
441                _ => {
442                    self.try_move_item_to_slots(slot_index, self.player_slots_range());
443                }
444            },
445            MenuLocation::Crafting(l) => match l {
446                CraftingMenuLocation::Player => {
447                    self.try_move_item_to_slots_or_toggle_hotbar(
448                        slot_index,
449                        Menu::CRAFTING_GRID_SLOTS,
450                    );
451                }
452                _ => {
453                    self.try_move_item_to_slots(slot_index, self.player_slots_range());
454                }
455            },
456            MenuLocation::Enchantment(l) => match l {
457                EnchantmentMenuLocation::Player => {
458                    self.try_move_item_to_slots_or_toggle_hotbar(
459                        slot_index,
460                        Menu::ENCHANTMENT_ITEM_SLOT..=Menu::ENCHANTMENT_LAPIS_SLOT,
461                    );
462                }
463                _ => {
464                    self.try_move_item_to_slots(slot_index, self.player_slots_range());
465                }
466            },
467            MenuLocation::Furnace(l) => match l {
468                FurnaceMenuLocation::Player => {
469                    self.try_move_item_to_slots(
470                        slot_index,
471                        Menu::FURNACE_INGREDIENT_SLOT..=Menu::FURNACE_FUEL_SLOT,
472                    );
473                }
474                _ => {
475                    self.try_move_item_to_slots(slot_index, self.player_slots_range());
476                }
477            },
478            MenuLocation::Grindstone(l) => match l {
479                GrindstoneMenuLocation::Player => {
480                    self.try_move_item_to_slots_or_toggle_hotbar(
481                        slot_index,
482                        Menu::GRINDSTONE_INPUT_SLOT..=Menu::GRINDSTONE_ADDITIONAL_SLOT,
483                    );
484                }
485                _ => {
486                    self.try_move_item_to_slots(slot_index, self.player_slots_range());
487                }
488            },
489            MenuLocation::Hopper(l) => match l {
490                HopperMenuLocation::Player => {
491                    self.try_move_item_to_slots_or_toggle_hotbar(
492                        slot_index,
493                        Menu::HOPPER_CONTENTS_SLOTS,
494                    );
495                }
496                _ => {
497                    self.try_move_item_to_slots(slot_index, self.player_slots_range());
498                }
499            },
500            MenuLocation::Lectern(l) => match l {
501                LecternMenuLocation::Player => {
502                    self.try_move_item_to_slots_or_toggle_hotbar(
503                        slot_index,
504                        Menu::LECTERN_BOOK_SLOT..=Menu::LECTERN_BOOK_SLOT,
505                    );
506                }
507                _ => {
508                    self.try_move_item_to_slots(slot_index, self.player_slots_range());
509                }
510            },
511            MenuLocation::Loom(l) => match l {
512                LoomMenuLocation::Player => {
513                    self.try_move_item_to_slots_or_toggle_hotbar(
514                        slot_index,
515                        Menu::LOOM_BANNER_SLOT..=Menu::LOOM_PATTERN_SLOT,
516                    );
517                }
518                _ => {
519                    self.try_move_item_to_slots(slot_index, self.player_slots_range());
520                }
521            },
522            MenuLocation::Merchant(l) => match l {
523                MerchantMenuLocation::Player => {
524                    self.try_move_item_to_slots_or_toggle_hotbar(
525                        slot_index,
526                        Menu::MERCHANT_PAYMENTS_SLOTS,
527                    );
528                }
529                _ => {
530                    self.try_move_item_to_slots(slot_index, self.player_slots_range());
531                }
532            },
533            MenuLocation::ShulkerBox(l) => match l {
534                ShulkerBoxMenuLocation::Player => {
535                    self.try_move_item_to_slots_or_toggle_hotbar(
536                        slot_index,
537                        Menu::SHULKER_BOX_CONTENTS_SLOTS,
538                    );
539                }
540                _ => {
541                    self.try_move_item_to_slots(slot_index, self.player_slots_range());
542                }
543            },
544            MenuLocation::Smithing(l) => match l {
545                SmithingMenuLocation::Player => {
546                    self.try_move_item_to_slots_or_toggle_hotbar(
547                        slot_index,
548                        Menu::SMITHING_TEMPLATE_SLOT..=Menu::SMITHING_ADDITIONAL_SLOT,
549                    );
550                }
551                _ => {
552                    self.try_move_item_to_slots(slot_index, self.player_slots_range());
553                }
554            },
555            MenuLocation::Smoker(l) => match l {
556                SmokerMenuLocation::Player => {
557                    self.try_move_item_to_slots(
558                        slot_index,
559                        Menu::SMOKER_INGREDIENT_SLOT..=Menu::SMOKER_FUEL_SLOT,
560                    );
561                }
562                _ => {
563                    self.try_move_item_to_slots(slot_index, self.player_slots_range());
564                }
565            },
566            MenuLocation::CartographyTable(l) => match l {
567                CartographyTableMenuLocation::Player => {
568                    self.try_move_item_to_slots_or_toggle_hotbar(
569                        slot_index,
570                        Menu::CARTOGRAPHY_TABLE_MAP_SLOT..=Menu::CARTOGRAPHY_TABLE_ADDITIONAL_SLOT,
571                    );
572                }
573                _ => {
574                    self.try_move_item_to_slots(slot_index, self.player_slots_range());
575                }
576            },
577            MenuLocation::Stonecutter(l) => match l {
578                StonecutterMenuLocation::Player => {
579                    self.try_move_item_to_slots_or_toggle_hotbar(
580                        slot_index,
581                        Menu::STONECUTTER_INPUT_SLOT..=Menu::STONECUTTER_INPUT_SLOT,
582                    );
583                }
584                _ => {
585                    self.try_move_item_to_slots(slot_index, self.player_slots_range());
586                }
587            },
588        }
589
590        ItemStack::Empty
591    }
592
593    fn try_move_item_to_slots_or_toggle_hotbar(
594        &mut self,
595        slot_index: usize,
596        target_slot_indexes: RangeInclusive<usize>,
597    ) {
598        if !self.try_move_item_to_slots(slot_index, target_slot_indexes) {
599            self.try_move_item_to_slots(
600                slot_index,
601                if self.is_hotbar_slot(slot_index) {
602                    self.player_slots_without_hotbar_range()
603                } else {
604                    self.hotbar_slots_range()
605                },
606            );
607        }
608    }
609
610    /// Whether the given item could be placed in this menu.
611    ///
612    /// TODO: right now this always returns true
613    pub fn may_place(&self, _target_slot_index: usize, _item: &ItemStackData) -> bool {
614        true
615    }
616
617    /// Whether the item in the given slot could be clicked and picked up.
618    /// TODO: right now this always returns true
619    pub fn may_pickup(&self, _source_slot_index: usize) -> bool {
620        true
621    }
622
623    /// Get the maximum number of items that can be placed in this slot.
624    pub fn max_stack_size(&self, _target_slot_index: usize) -> u32 {
625        64
626    }
627
628    /// Try moving an item to a set of slots in this menu.
629    ///
630    /// Returns the updated item slot.
631    fn try_move_item_to_slots(
632        &mut self,
633        item_slot_index: usize,
634        target_slot_indexes: RangeInclusive<usize>,
635    ) -> bool {
636        let mut item_slot = self.slot(item_slot_index).unwrap().clone();
637
638        // first see if we can stack it with another item
639        if item_slot.kind().stackable() {
640            for target_slot_index in target_slot_indexes.clone() {
641                self.move_item_to_slot_if_stackable(&mut item_slot, target_slot_index);
642                if item_slot.is_empty() {
643                    break;
644                }
645            }
646        }
647
648        // and if not then just try putting it in an empty slot
649        if item_slot.is_present() {
650            for target_slot_index in target_slot_indexes {
651                self.move_item_to_slot_if_empty(&mut item_slot, target_slot_index);
652                if item_slot.is_empty() {
653                    break;
654                }
655            }
656        }
657
658        item_slot.is_empty()
659    }
660
661    /// Merge this item slot into the target item slot, only if the target item
662    /// slot is present and the same item.
663    fn move_item_to_slot_if_stackable(
664        &mut self,
665        item_slot: &mut ItemStack,
666        target_slot_index: usize,
667    ) {
668        let ItemStack::Present(item) = item_slot else {
669            return;
670        };
671        let target_slot = self.slot(target_slot_index).unwrap();
672        if let ItemStack::Present(target_item) = target_slot {
673            // the target slot is empty, so we can just move the item there
674            if self.may_place(target_slot_index, item)
675                && target_item.is_same_item_and_components(item)
676            {
677                let slot_item_limit = self.max_stack_size(target_slot_index);
678                let new_target_slot_data = item.split(u32::min(slot_item_limit, item.count as u32));
679
680                // get the target slot again but mut this time so we can update it
681                let target_slot = self.slot_mut(target_slot_index).unwrap();
682                *target_slot = ItemStack::Present(new_target_slot_data);
683
684                item_slot.update_empty();
685            }
686        }
687    }
688
689    fn move_item_to_slot_if_empty(&mut self, item_slot: &mut ItemStack, target_slot_index: usize) {
690        let ItemStack::Present(item) = item_slot else {
691            return;
692        };
693        let target_slot = self.slot(target_slot_index).unwrap();
694        if target_slot.is_empty() && self.may_place(target_slot_index, item) {
695            let slot_item_limit = self.max_stack_size(target_slot_index);
696            let new_target_slot_data = item.split(u32::min(slot_item_limit, item.count as u32));
697
698            let target_slot = self.slot_mut(target_slot_index).unwrap();
699            *target_slot = ItemStack::Present(new_target_slot_data);
700            item_slot.update_empty();
701        }
702    }
703}