package net.minecraft.world.inventory; import com.google.common.base.Suppliers; import com.google.common.collect.HashBasedTable; import com.google.common.collect.Lists; import com.google.common.collect.Sets; import com.google.common.collect.Table; import com.mojang.logging.LogUtils; import it.unimi.dsi.fastutil.ints.IntArrayList; import it.unimi.dsi.fastutil.ints.IntList; import java.util.ArrayList; import java.util.List; import java.util.Optional; import java.util.OptionalInt; import java.util.Set; import java.util.function.Supplier; import net.minecraft.CrashReport; import net.minecraft.CrashReportCategory; import net.minecraft.CrashReportDetail; import net.minecraft.ReportedException; import net.minecraft.core.NonNullList; import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.network.HashedStack; import net.minecraft.server.level.ServerPlayer; import net.minecraft.util.Mth; import net.minecraft.world.Container; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.SlotAccess; import net.minecraft.world.entity.player.Inventory; import net.minecraft.world.entity.player.Player; import net.minecraft.world.flag.FeatureFlagSet; import net.minecraft.world.item.BundleItem; import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.entity.BlockEntity; import org.jetbrains.annotations.Nullable; import org.slf4j.Logger; public abstract class AbstractContainerMenu { private static final Logger LOGGER = LogUtils.getLogger(); public static final int SLOT_CLICKED_OUTSIDE = -999; public static final int QUICKCRAFT_TYPE_CHARITABLE = 0; public static final int QUICKCRAFT_TYPE_GREEDY = 1; public static final int QUICKCRAFT_TYPE_CLONE = 2; public static final int QUICKCRAFT_HEADER_START = 0; public static final int QUICKCRAFT_HEADER_CONTINUE = 1; public static final int QUICKCRAFT_HEADER_END = 2; public static final int CARRIED_SLOT_SIZE = Integer.MAX_VALUE; public static final int SLOTS_PER_ROW = 9; public static final int SLOT_SIZE = 18; private final NonNullList lastSlots = NonNullList.create(); public final NonNullList slots = NonNullList.create(); private final List dataSlots = Lists.newArrayList(); private ItemStack carried = ItemStack.EMPTY; private final NonNullList remoteSlots = NonNullList.create(); private final IntList remoteDataSlots = new IntArrayList(); private RemoteSlot remoteCarried = RemoteSlot.PLACEHOLDER; private int stateId; @Nullable private final MenuType menuType; public final int containerId; private int quickcraftType = -1; private int quickcraftStatus; private final Set quickcraftSlots = Sets.newHashSet(); private final List containerListeners = Lists.newArrayList(); @Nullable private ContainerSynchronizer synchronizer; private boolean suppressRemoteUpdates; protected AbstractContainerMenu(@Nullable MenuType menuType, int containerId) { this.menuType = menuType; this.containerId = containerId; } protected void addInventoryHotbarSlots(Container container, int x, int y) { for (int i = 0; i < 9; i++) { this.addSlot(new Slot(container, i, x + i * 18, y)); } } protected void addInventoryExtendedSlots(Container container, int x, int y) { for (int i = 0; i < 3; i++) { for (int j = 0; j < 9; j++) { this.addSlot(new Slot(container, j + (i + 1) * 9, x + j * 18, y + i * 18)); } } } protected void addStandardInventorySlots(Container container, int x, int y) { this.addInventoryExtendedSlots(container, x, y); int i = 4; int j = 58; this.addInventoryHotbarSlots(container, x, y + 58); } protected static boolean stillValid(ContainerLevelAccess access, Player player, Block targetBlock) { return access.evaluate((level, blockPos) -> !level.getBlockState(blockPos).is(targetBlock) ? false : player.canInteractWithBlock(blockPos, 4.0), true); } public MenuType getType() { if (this.menuType == null) { throw new UnsupportedOperationException("Unable to construct this menu by type"); } else { return this.menuType; } } protected static void checkContainerSize(Container container, int minSize) { int i = container.getContainerSize(); if (i < minSize) { throw new IllegalArgumentException("Container size " + i + " is smaller than expected " + minSize); } } protected static void checkContainerDataCount(ContainerData intArray, int minSize) { int i = intArray.getCount(); if (i < minSize) { throw new IllegalArgumentException("Container data count " + i + " is smaller than expected " + minSize); } } public boolean isValidSlotIndex(int slotIndex) { return slotIndex == -1 || slotIndex == -999 || slotIndex < this.slots.size(); } /** * Adds an item slot to this container */ protected Slot addSlot(Slot slot) { slot.index = this.slots.size(); this.slots.add(slot); this.lastSlots.add(ItemStack.EMPTY); this.remoteSlots.add(this.synchronizer != null ? this.synchronizer.createSlot() : RemoteSlot.PLACEHOLDER); return slot; } protected DataSlot addDataSlot(DataSlot intValue) { this.dataSlots.add(intValue); this.remoteDataSlots.add(0); return intValue; } protected void addDataSlots(ContainerData array) { for (int i = 0; i < array.getCount(); i++) { this.addDataSlot(DataSlot.forContainer(array, i)); } } public void addSlotListener(ContainerListener listener) { if (!this.containerListeners.contains(listener)) { this.containerListeners.add(listener); this.broadcastChanges(); } } public void setSynchronizer(ContainerSynchronizer synchronizer) { this.synchronizer = synchronizer; this.remoteCarried = synchronizer.createSlot(); this.remoteSlots.replaceAll(remoteSlot -> synchronizer.createSlot()); this.sendAllDataToRemote(); } public void sendAllDataToRemote() { List list = new ArrayList(this.slots.size()); int i = 0; for (int j = this.slots.size(); i < j; i++) { ItemStack itemStack = this.slots.get(i).getItem(); list.add(itemStack.copy()); this.remoteSlots.get(i).force(itemStack); } ItemStack itemStack2 = this.getCarried(); this.remoteCarried.force(itemStack2); int j = 0; for (int k = this.dataSlots.size(); j < k; j++) { this.remoteDataSlots.set(j, ((DataSlot)this.dataSlots.get(j)).get()); } if (this.synchronizer != null) { this.synchronizer.sendInitialData(this, list, itemStack2.copy(), this.remoteDataSlots.toIntArray()); } } /** * Remove the given Listener. Method name is for legacy. */ public void removeSlotListener(ContainerListener listener) { this.containerListeners.remove(listener); } /** * Returns a list if {@code ItemStacks}, for each slot. */ public NonNullList getItems() { NonNullList nonNullList = NonNullList.create(); for (Slot slot : this.slots) { nonNullList.add(slot.getItem()); } return nonNullList; } /** * Looks for changes made in the container, sends them to every listener. */ public void broadcastChanges() { for (int i = 0; i < this.slots.size(); i++) { ItemStack itemStack = this.slots.get(i).getItem(); Supplier supplier = Suppliers.memoize(itemStack::copy); this.triggerSlotListeners(i, itemStack, supplier); this.synchronizeSlotToRemote(i, itemStack, supplier); } this.synchronizeCarriedToRemote(); for (int i = 0; i < this.dataSlots.size(); i++) { DataSlot dataSlot = (DataSlot)this.dataSlots.get(i); int j = dataSlot.get(); if (dataSlot.checkAndClearUpdateFlag()) { this.updateDataSlotListeners(i, j); } this.synchronizeDataSlotToRemote(i, j); } } public void broadcastFullState() { for (int i = 0; i < this.slots.size(); i++) { ItemStack itemStack = this.slots.get(i).getItem(); this.triggerSlotListeners(i, itemStack, itemStack::copy); } for (int i = 0; i < this.dataSlots.size(); i++) { DataSlot dataSlot = (DataSlot)this.dataSlots.get(i); if (dataSlot.checkAndClearUpdateFlag()) { this.updateDataSlotListeners(i, dataSlot.get()); } } this.sendAllDataToRemote(); } private void updateDataSlotListeners(int slotIndex, int value) { for (ContainerListener containerListener : this.containerListeners) { containerListener.dataChanged(this, slotIndex, value); } } private void triggerSlotListeners(int slotIndex, ItemStack stack, Supplier supplier) { ItemStack itemStack = this.lastSlots.get(slotIndex); if (!ItemStack.matches(itemStack, stack)) { ItemStack itemStack2 = (ItemStack)supplier.get(); this.lastSlots.set(slotIndex, itemStack2); for (ContainerListener containerListener : this.containerListeners) { containerListener.slotChanged(this, slotIndex, itemStack2); } } } private void synchronizeSlotToRemote(int slotIndex, ItemStack stack, Supplier supplier) { if (!this.suppressRemoteUpdates) { RemoteSlot remoteSlot = this.remoteSlots.get(slotIndex); if (!remoteSlot.matches(stack)) { remoteSlot.force(stack); if (this.synchronizer != null) { this.synchronizer.sendSlotChange(this, slotIndex, (ItemStack)supplier.get()); } } } } private void synchronizeDataSlotToRemote(int slotIndex, int value) { if (!this.suppressRemoteUpdates) { int i = this.remoteDataSlots.getInt(slotIndex); if (i != value) { this.remoteDataSlots.set(slotIndex, value); if (this.synchronizer != null) { this.synchronizer.sendDataChange(this, slotIndex, value); } } } } private void synchronizeCarriedToRemote() { if (!this.suppressRemoteUpdates) { ItemStack itemStack = this.getCarried(); if (!this.remoteCarried.matches(itemStack)) { this.remoteCarried.force(itemStack); if (this.synchronizer != null) { this.synchronizer.sendCarriedChange(this, itemStack.copy()); } } } } public void setRemoteSlot(int slot, ItemStack stack) { this.remoteSlots.get(slot).force(stack); } public void setRemoteSlotUnsafe(int slot, HashedStack stack) { if (slot >= 0 && slot < this.remoteSlots.size()) { this.remoteSlots.get(slot).receive(stack); } else { LOGGER.debug("Incorrect slot index: {} available slots: {}", slot, this.remoteSlots.size()); } } public void setRemoteCarried(HashedStack stack) { this.remoteCarried.receive(stack); } /** * Handles the given Button-click on the server, currently only used by enchanting. Name is for legacy. */ public boolean clickMenuButton(Player player, int id) { return false; } public Slot getSlot(int slotId) { return this.slots.get(slotId); } /** * Handle when the stack in slot {@code index} is shift-clicked. Normally this moves the stack between the player inventory and the other inventory(s). */ public abstract ItemStack quickMoveStack(Player player, int index); public void setSelectedBundleItemIndex(int slotIndex, int bundleItemIndex) { if (slotIndex >= 0 && slotIndex < this.slots.size()) { ItemStack itemStack = this.slots.get(slotIndex).getItem(); BundleItem.toggleSelectedItem(itemStack, bundleItemIndex); } } public void clicked(int slotId, int button, ClickType clickType, Player player) { try { this.doClick(slotId, button, clickType, player); } catch (Exception var8) { CrashReport crashReport = CrashReport.forThrowable(var8, "Container click"); CrashReportCategory crashReportCategory = crashReport.addCategory("Click info"); crashReportCategory.setDetail( "Menu Type", (CrashReportDetail)(() -> this.menuType != null ? BuiltInRegistries.MENU.getKey(this.menuType).toString() : "") ); crashReportCategory.setDetail("Menu Class", (CrashReportDetail)(() -> this.getClass().getCanonicalName())); crashReportCategory.setDetail("Slot Count", this.slots.size()); crashReportCategory.setDetail("Slot", slotId); crashReportCategory.setDetail("Button", button); crashReportCategory.setDetail("Type", clickType); throw new ReportedException(crashReport); } } private void doClick(int slotId, int button, ClickType clickType, Player player) { Inventory inventory = player.getInventory(); if (clickType == ClickType.QUICK_CRAFT) { int i = this.quickcraftStatus; this.quickcraftStatus = getQuickcraftHeader(button); if ((i != 1 || this.quickcraftStatus != 2) && i != this.quickcraftStatus) { this.resetQuickCraft(); } else if (this.getCarried().isEmpty()) { this.resetQuickCraft(); } else if (this.quickcraftStatus == 0) { this.quickcraftType = getQuickcraftType(button); if (isValidQuickcraftType(this.quickcraftType, player)) { this.quickcraftStatus = 1; this.quickcraftSlots.clear(); } else { this.resetQuickCraft(); } } else if (this.quickcraftStatus == 1) { Slot slot = this.slots.get(slotId); ItemStack itemStack = this.getCarried(); if (canItemQuickReplace(slot, itemStack, true) && slot.mayPlace(itemStack) && (this.quickcraftType == 2 || itemStack.getCount() > this.quickcraftSlots.size()) && this.canDragTo(slot)) { this.quickcraftSlots.add(slot); } } else if (this.quickcraftStatus == 2) { if (!this.quickcraftSlots.isEmpty()) { if (this.quickcraftSlots.size() == 1) { int j = ((Slot)this.quickcraftSlots.iterator().next()).index; this.resetQuickCraft(); this.doClick(j, this.quickcraftType, ClickType.PICKUP, player); return; } ItemStack itemStack2 = this.getCarried().copy(); if (itemStack2.isEmpty()) { this.resetQuickCraft(); return; } int k = this.getCarried().getCount(); for (Slot slot2 : this.quickcraftSlots) { ItemStack itemStack3 = this.getCarried(); if (slot2 != null && canItemQuickReplace(slot2, itemStack3, true) && slot2.mayPlace(itemStack3) && (this.quickcraftType == 2 || itemStack3.getCount() >= this.quickcraftSlots.size()) && this.canDragTo(slot2)) { int l = slot2.hasItem() ? slot2.getItem().getCount() : 0; int m = Math.min(itemStack2.getMaxStackSize(), slot2.getMaxStackSize(itemStack2)); int n = Math.min(getQuickCraftPlaceCount(this.quickcraftSlots, this.quickcraftType, itemStack2) + l, m); k -= n - l; slot2.setByPlayer(itemStack2.copyWithCount(n)); } } itemStack2.setCount(k); this.setCarried(itemStack2); } this.resetQuickCraft(); } else { this.resetQuickCraft(); } } else if (this.quickcraftStatus != 0) { this.resetQuickCraft(); } else if ((clickType == ClickType.PICKUP || clickType == ClickType.QUICK_MOVE) && (button == 0 || button == 1)) { ClickAction clickAction = button == 0 ? ClickAction.PRIMARY : ClickAction.SECONDARY; if (slotId == -999) { if (!this.getCarried().isEmpty()) { if (clickAction == ClickAction.PRIMARY) { player.drop(this.getCarried(), true); this.setCarried(ItemStack.EMPTY); } else { player.drop(this.getCarried().split(1), true); } } } else if (clickType == ClickType.QUICK_MOVE) { if (slotId < 0) { return; } Slot slot = this.slots.get(slotId); if (!slot.mayPickup(player)) { return; } ItemStack itemStack = this.quickMoveStack(player, slotId); while (!itemStack.isEmpty() && ItemStack.isSameItem(slot.getItem(), itemStack)) { itemStack = this.quickMoveStack(player, slotId); } } else { if (slotId < 0) { return; } Slot slot = this.slots.get(slotId); ItemStack itemStack = slot.getItem(); ItemStack itemStack4 = this.getCarried(); player.updateTutorialInventoryAction(itemStack4, slot.getItem(), clickAction); if (!this.tryItemClickBehaviourOverride(player, clickAction, slot, itemStack, itemStack4)) { if (itemStack.isEmpty()) { if (!itemStack4.isEmpty()) { int o = clickAction == ClickAction.PRIMARY ? itemStack4.getCount() : 1; this.setCarried(slot.safeInsert(itemStack4, o)); } } else if (slot.mayPickup(player)) { if (itemStack4.isEmpty()) { int o = clickAction == ClickAction.PRIMARY ? itemStack.getCount() : (itemStack.getCount() + 1) / 2; Optional optional = slot.tryRemove(o, Integer.MAX_VALUE, player); optional.ifPresent(itemStackx -> { this.setCarried(itemStackx); slot.onTake(player, itemStackx); }); } else if (slot.mayPlace(itemStack4)) { if (ItemStack.isSameItemSameComponents(itemStack, itemStack4)) { int o = clickAction == ClickAction.PRIMARY ? itemStack4.getCount() : 1; this.setCarried(slot.safeInsert(itemStack4, o)); } else if (itemStack4.getCount() <= slot.getMaxStackSize(itemStack4)) { this.setCarried(itemStack); slot.setByPlayer(itemStack4); } } else if (ItemStack.isSameItemSameComponents(itemStack, itemStack4)) { Optional optional2 = slot.tryRemove(itemStack.getCount(), itemStack4.getMaxStackSize() - itemStack4.getCount(), player); optional2.ifPresent(itemStack2x -> { itemStack4.grow(itemStack2x.getCount()); slot.onTake(player, itemStack2x); }); } } } slot.setChanged(); } } else if (clickType == ClickType.SWAP && (button >= 0 && button < 9 || button == 40)) { ItemStack itemStack5 = inventory.getItem(button); Slot slot = this.slots.get(slotId); ItemStack itemStack = slot.getItem(); if (!itemStack5.isEmpty() || !itemStack.isEmpty()) { if (itemStack5.isEmpty()) { if (slot.mayPickup(player)) { inventory.setItem(button, itemStack); slot.onSwapCraft(itemStack.getCount()); slot.setByPlayer(ItemStack.EMPTY); slot.onTake(player, itemStack); } } else if (itemStack.isEmpty()) { if (slot.mayPlace(itemStack5)) { int p = slot.getMaxStackSize(itemStack5); if (itemStack5.getCount() > p) { slot.setByPlayer(itemStack5.split(p)); } else { inventory.setItem(button, ItemStack.EMPTY); slot.setByPlayer(itemStack5); } } } else if (slot.mayPickup(player) && slot.mayPlace(itemStack5)) { int p = slot.getMaxStackSize(itemStack5); if (itemStack5.getCount() > p) { slot.setByPlayer(itemStack5.split(p)); slot.onTake(player, itemStack); if (!inventory.add(itemStack)) { player.drop(itemStack, true); } } else { inventory.setItem(button, itemStack); slot.setByPlayer(itemStack5); slot.onTake(player, itemStack); } } } } else if (clickType == ClickType.CLONE && player.hasInfiniteMaterials() && this.getCarried().isEmpty() && slotId >= 0) { Slot slot3 = this.slots.get(slotId); if (slot3.hasItem()) { ItemStack itemStack2 = slot3.getItem(); this.setCarried(itemStack2.copyWithCount(itemStack2.getMaxStackSize())); } } else if (clickType == ClickType.THROW && this.getCarried().isEmpty() && slotId >= 0) { Slot slot3 = this.slots.get(slotId); int j = button == 0 ? 1 : slot3.getItem().getCount(); if (!player.canDropItems()) { return; } ItemStack itemStack = slot3.safeTake(j, Integer.MAX_VALUE, player); player.drop(itemStack, true); player.handleCreativeModeItemDrop(itemStack); if (button == 1) { while (!itemStack.isEmpty() && ItemStack.isSameItem(slot3.getItem(), itemStack)) { if (!player.canDropItems()) { return; } itemStack = slot3.safeTake(j, Integer.MAX_VALUE, player); player.drop(itemStack, true); player.handleCreativeModeItemDrop(itemStack); } } } else if (clickType == ClickType.PICKUP_ALL && slotId >= 0) { Slot slot3x = this.slots.get(slotId); ItemStack itemStack2 = this.getCarried(); if (!itemStack2.isEmpty() && (!slot3x.hasItem() || !slot3x.mayPickup(player))) { int k = button == 0 ? 0 : this.slots.size() - 1; int p = button == 0 ? 1 : -1; for (int o = 0; o < 2; o++) { for (int q = k; q >= 0 && q < this.slots.size() && itemStack2.getCount() < itemStack2.getMaxStackSize(); q += p) { Slot slot4 = this.slots.get(q); if (slot4.hasItem() && canItemQuickReplace(slot4, itemStack2, true) && slot4.mayPickup(player) && this.canTakeItemForPickAll(itemStack2, slot4)) { ItemStack itemStack6 = slot4.getItem(); if (o != 0 || itemStack6.getCount() != itemStack6.getMaxStackSize()) { ItemStack itemStack7 = slot4.safeTake(itemStack6.getCount(), itemStack2.getMaxStackSize() - itemStack2.getCount(), player); itemStack2.grow(itemStack7.getCount()); } } } } } } } private boolean tryItemClickBehaviourOverride(Player player, ClickAction action, Slot slot, ItemStack clickedItem, ItemStack carriedItem) { FeatureFlagSet featureFlagSet = player.level().enabledFeatures(); return carriedItem.isItemEnabled(featureFlagSet) && carriedItem.overrideStackedOnOther(slot, action, player) ? true : clickedItem.isItemEnabled(featureFlagSet) && clickedItem.overrideOtherStackedOnMe(carriedItem, slot, action, player, this.createCarriedSlotAccess()); } private SlotAccess createCarriedSlotAccess() { return new SlotAccess() { @Override public ItemStack get() { return AbstractContainerMenu.this.getCarried(); } @Override public boolean set(ItemStack carried) { AbstractContainerMenu.this.setCarried(carried); return true; } }; } /** * Called to determine if the current slot is valid for the stack merging (double-click) code. The stack passed in is null for the initial slot that was double-clicked. */ public boolean canTakeItemForPickAll(ItemStack stack, Slot slot) { return true; } /** * Called when the container is closed. */ public void removed(Player player) { if (player instanceof ServerPlayer) { ItemStack itemStack = this.getCarried(); if (!itemStack.isEmpty()) { dropOrPlaceInInventory(player, itemStack); this.setCarried(ItemStack.EMPTY); } } } private static void dropOrPlaceInInventory(Player player, ItemStack stack) { boolean bl = player.isRemoved() && player.getRemovalReason() != Entity.RemovalReason.CHANGED_DIMENSION; boolean bl2 = player instanceof ServerPlayer serverPlayer && serverPlayer.hasDisconnected(); if (bl || bl2) { player.drop(stack, false); } else if (player instanceof ServerPlayer) { player.getInventory().placeItemBackInInventory(stack); } } protected void clearContainer(Player player, Container container) { for (int i = 0; i < container.getContainerSize(); i++) { dropOrPlaceInInventory(player, container.removeItemNoUpdate(i)); } } /** * Callback for when the crafting matrix is changed. */ public void slotsChanged(Container container) { this.broadcastChanges(); } /** * Puts an ItemStack in a slot. */ public void setItem(int slotId, int stateId, ItemStack stack) { this.getSlot(slotId).set(stack); this.stateId = stateId; } public void initializeContents(int stateId, List items, ItemStack carried) { for (int i = 0; i < items.size(); i++) { this.getSlot(i).set((ItemStack)items.get(i)); } this.carried = carried; this.stateId = stateId; } public void setData(int id, int data) { ((DataSlot)this.dataSlots.get(id)).set(data); } /** * Determines whether supplied player can use this container */ public abstract boolean stillValid(Player player); /** * Merges provided ItemStack with the first available one in the container/player inventor between minIndex (included) and maxIndex (excluded). Args : stack, minIndex, maxIndex, negativDirection. [!] the Container implementation do not check if the item is valid for the slot */ protected boolean moveItemStackTo(ItemStack stack, int startIndex, int endIndex, boolean reverseDirection) { boolean bl = false; int i = startIndex; if (reverseDirection) { i = endIndex - 1; } if (stack.isStackable()) { while (!stack.isEmpty() && (reverseDirection ? i >= startIndex : i < endIndex)) { Slot slot = this.slots.get(i); ItemStack itemStack = slot.getItem(); if (!itemStack.isEmpty() && ItemStack.isSameItemSameComponents(stack, itemStack)) { int j = itemStack.getCount() + stack.getCount(); int k = slot.getMaxStackSize(itemStack); if (j <= k) { stack.setCount(0); itemStack.setCount(j); slot.setChanged(); bl = true; } else if (itemStack.getCount() < k) { stack.shrink(k - itemStack.getCount()); itemStack.setCount(k); slot.setChanged(); bl = true; } } if (reverseDirection) { i--; } else { i++; } } } if (!stack.isEmpty()) { if (reverseDirection) { i = endIndex - 1; } else { i = startIndex; } while (reverseDirection ? i >= startIndex : i < endIndex) { Slot slotx = this.slots.get(i); ItemStack itemStackx = slotx.getItem(); if (itemStackx.isEmpty() && slotx.mayPlace(stack)) { int j = slotx.getMaxStackSize(stack); slotx.setByPlayer(stack.split(Math.min(stack.getCount(), j))); slotx.setChanged(); bl = true; break; } if (reverseDirection) { i--; } else { i++; } } } return bl; } /** * Extracts the drag mode. Args : eventButton. Return (0 : evenly split, 1 : one item by slot, 2 : not used ?) */ public static int getQuickcraftType(int eventButton) { return eventButton >> 2 & 3; } /** * Args : clickedButton, Returns (0 : start drag, 1 : add slot, 2 : end drag) */ public static int getQuickcraftHeader(int clickedButton) { return clickedButton & 3; } public static int getQuickcraftMask(int quickCraftingHeader, int quickCraftingType) { return quickCraftingHeader & 3 | (quickCraftingType & 3) << 2; } public static boolean isValidQuickcraftType(int dragMode, Player player) { if (dragMode == 0) { return true; } else { return dragMode == 1 ? true : dragMode == 2 && player.hasInfiniteMaterials(); } } /** * Reset the drag fields */ protected void resetQuickCraft() { this.quickcraftStatus = 0; this.quickcraftSlots.clear(); } /** * Checks if it's possible to add the given itemstack to the given slot. */ public static boolean canItemQuickReplace(@Nullable Slot slot, ItemStack stack, boolean stackSizeMatters) { boolean bl = slot == null || !slot.hasItem(); return !bl && ItemStack.isSameItemSameComponents(stack, slot.getItem()) ? slot.getItem().getCount() + (stackSizeMatters ? 0 : stack.getCount()) <= stack.getMaxStackSize() : bl; } public static int getQuickCraftPlaceCount(Set slots, int type, ItemStack stack) { return switch (type) { case 0 -> Mth.floor((float)stack.getCount() / slots.size()); case 1 -> 1; case 2 -> stack.getMaxStackSize(); default -> stack.getCount(); }; } /** * Returns {@code true} if the player can "drag-spilt" items into this slot. Returns {@code true} by default. Called to check if the slot can be added to a list of Slots to split the held ItemStack across. */ public boolean canDragTo(Slot slot) { return true; } /** * Like the version that takes an inventory. If the given BlockEntity is not an Inventory, 0 is returned instead. */ public static int getRedstoneSignalFromBlockEntity(@Nullable BlockEntity blockEntity) { return blockEntity instanceof Container ? getRedstoneSignalFromContainer((Container)blockEntity) : 0; } public static int getRedstoneSignalFromContainer(@Nullable Container container) { if (container == null) { return 0; } else { float f = 0.0F; for (int i = 0; i < container.getContainerSize(); i++) { ItemStack itemStack = container.getItem(i); if (!itemStack.isEmpty()) { f += (float)itemStack.getCount() / container.getMaxStackSize(itemStack); } } f /= container.getContainerSize(); return Mth.lerpDiscrete(f, 0, 15); } } public void setCarried(ItemStack stack) { this.carried = stack; } public ItemStack getCarried() { return this.carried; } public void suppressRemoteUpdates() { this.suppressRemoteUpdates = true; } public void resumeRemoteUpdates() { this.suppressRemoteUpdates = false; } public void transferState(AbstractContainerMenu menu) { Table table = HashBasedTable.create(); for (int i = 0; i < menu.slots.size(); i++) { Slot slot = menu.slots.get(i); table.put(slot.container, slot.getContainerSlot(), i); } for (int i = 0; i < this.slots.size(); i++) { Slot slot = this.slots.get(i); Integer integer = table.get(slot.container, slot.getContainerSlot()); if (integer != null) { this.lastSlots.set(i, menu.lastSlots.get(integer)); RemoteSlot remoteSlot = menu.remoteSlots.get(integer); RemoteSlot remoteSlot2 = this.remoteSlots.get(i); if (remoteSlot instanceof RemoteSlot.Synchronized synchronized_ && remoteSlot2 instanceof RemoteSlot.Synchronized synchronized2) { synchronized2.copyFrom(synchronized_); } } } } public OptionalInt findSlot(Container container, int slotIndex) { for (int i = 0; i < this.slots.size(); i++) { Slot slot = this.slots.get(i); if (slot.container == container && slotIndex == slot.getContainerSlot()) { return OptionalInt.of(i); } } return OptionalInt.empty(); } public int getStateId() { return this.stateId; } public int incrementStateId() { this.stateId = this.stateId + 1 & 32767; return this.stateId; } }