minecraft-src/net/minecraft/world/inventory/AbstractContainerMenu.java
2025-07-04 03:45:38 +03:00

881 lines
29 KiB
Java

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<ItemStack> lastSlots = NonNullList.create();
public final NonNullList<Slot> slots = NonNullList.create();
private final List<DataSlot> dataSlots = Lists.<DataSlot>newArrayList();
private ItemStack carried = ItemStack.EMPTY;
private final NonNullList<RemoteSlot> 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<Slot> quickcraftSlots = Sets.<Slot>newHashSet();
private final List<ContainerListener> containerListeners = Lists.<ContainerListener>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<ItemStack> 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<ItemStack> getItems() {
NonNullList<ItemStack> 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<ItemStack> 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<ItemStack> 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<ItemStack> 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<String>)(() -> this.menuType != null ? BuiltInRegistries.MENU.getKey(this.menuType).toString() : "<no type>")
);
crashReportCategory.setDetail("Menu Class", (CrashReportDetail<String>)(() -> 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<ItemStack> 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<ItemStack> 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<ItemStack> 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<Slot> 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<Container, Integer, Integer> 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;
}
}