Default IItemHandler capability for shulker box itemstacks (#8827)

Co-authored-by: LexManos <LexManos@gmail.com>
This commit is contained in:
Luke BemishandLexManos authored and GitHub committed 2022-09-03 20:16:25 +01:00
1 parent aab0e4c82e
commit a2b35cae92
2 files changed
+241 -1

No files matched your search

@@ -41,6 +41,7 @@ import net.minecraft.network.chat.Component;
import net.minecraft.world.level.Level;
import net.minecraftforge.common.ToolAction;
import net.minecraftforge.common.ToolActions;
import net.minecraftforge.items.wrapper.ShulkerItemStackInvWrapper;
import net.minecraftforge.registries.IForgeRegistry;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -655,7 +656,8 @@ public interface IForgeItem
@Nullable
default net.minecraftforge.common.capabilities.ICapabilityProvider initCapabilities(ItemStack stack, @Nullable CompoundTag nbt)
{
return null;
var ret = ShulkerItemStackInvWrapper.createDefaultProvider(stack);
return ret;
}
/**
@@ -0,0 +1,238 @@
/*
* Copyright (c) Forge Development LLC and contributors
* SPDX-License-Identifier: LGPL-2.1-only
*/
package net.minecraftforge.items.wrapper;
import net.minecraft.core.Direction;
import net.minecraft.core.NonNullList;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.ContainerHelper;
import net.minecraft.world.item.BlockItem;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Items;
import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraftforge.common.capabilities.Capability;
import net.minecraftforge.common.capabilities.ICapabilityProvider;
import net.minecraftforge.common.util.LazyOptional;
import net.minecraftforge.items.CapabilityItemHandler;
import net.minecraftforge.items.IItemHandler;
import net.minecraftforge.items.IItemHandlerModifiable;
import net.minecraftforge.items.ItemHandlerHelper;
import net.minecraftforge.registries.ForgeRegistries;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Objects;
import java.util.Set;
@ApiStatus.Internal
public class ShulkerItemStackInvWrapper implements IItemHandlerModifiable, ICapabilityProvider
{
@ApiStatus.Internal
@Nullable
public static ICapabilityProvider createDefaultProvider(ItemStack itemStack)
{
var item = itemStack.getItem();
if (item == Items.SHULKER_BOX ||
item == Items.BLACK_SHULKER_BOX ||
item == Items.BLUE_SHULKER_BOX ||
item == Items.BROWN_SHULKER_BOX ||
item == Items.CYAN_SHULKER_BOX ||
item == Items.GRAY_SHULKER_BOX ||
item == Items.GREEN_SHULKER_BOX ||
item == Items.LIGHT_BLUE_SHULKER_BOX ||
item == Items.LIGHT_GRAY_SHULKER_BOX ||
item == Items.LIME_SHULKER_BOX ||
item == Items.MAGENTA_SHULKER_BOX ||
item == Items.ORANGE_SHULKER_BOX ||
item == Items.PINK_SHULKER_BOX ||
item == Items.PURPLE_SHULKER_BOX ||
item == Items.RED_SHULKER_BOX ||
item == Items.WHITE_SHULKER_BOX ||
item == Items.YELLOW_SHULKER_BOX
)
return new ShulkerItemStackInvWrapper(itemStack);
return null;
}
private final ItemStack stack;
private final LazyOptional<IItemHandler> holder = LazyOptional.of(() -> this);
private CompoundTag cachedTag;
private NonNullList<ItemStack> itemStacksCache;
private ShulkerItemStackInvWrapper(ItemStack stack)
{
this.stack = stack;
}
@Override
public int getSlots()
{
return 27;
}
@Override
@NotNull
public ItemStack getStackInSlot(int slot)
{
validateSlotIndex(slot);
return getItemList().get(slot);
}
@Override
@NotNull
public ItemStack insertItem(int slot, @NotNull ItemStack stack, boolean simulate)
{
if (stack.isEmpty())
return ItemStack.EMPTY;
if (!isItemValid(slot, stack))
return stack;
validateSlotIndex(slot);
NonNullList<ItemStack> itemStacks = getItemList();
ItemStack existing = itemStacks.get(slot);
int limit = Math.min(getSlotLimit(slot), stack.getMaxStackSize());
if (!existing.isEmpty())
{
if (!ItemHandlerHelper.canItemStacksStack(stack, existing))
return stack;
limit -= existing.getCount();
}
if (limit <= 0)
return stack;
boolean reachedLimit = stack.getCount() > limit;
if (!simulate)
{
if (existing.isEmpty())
{
itemStacks.set(slot, reachedLimit ? ItemHandlerHelper.copyStackWithSize(stack, limit) : stack);
}
else
{
existing.grow(reachedLimit ? limit : stack.getCount());
}
setItemList(itemStacks);
}
return reachedLimit ? ItemHandlerHelper.copyStackWithSize(stack, stack.getCount()- limit) : ItemStack.EMPTY;
}
@Override
@NotNull
public ItemStack extractItem(int slot, int amount, boolean simulate)
{
NonNullList<ItemStack> itemStacks = getItemList();
if (amount == 0)
return ItemStack.EMPTY;
validateSlotIndex(slot);
ItemStack existing = itemStacks.get(slot);
if (existing.isEmpty())
return ItemStack.EMPTY;
int toExtract = Math.min(amount, existing.getMaxStackSize());
if (existing.getCount() <= toExtract)
{
if (!simulate)
{
itemStacks.set(slot, ItemStack.EMPTY);
setItemList(itemStacks);
return existing;
}
else
{
return existing.copy();
}
}
else
{
if (!simulate)
{
itemStacks.set(slot, ItemHandlerHelper.copyStackWithSize(existing, existing.getCount() - toExtract));
setItemList(itemStacks);
}
return ItemHandlerHelper.copyStackWithSize(existing, toExtract);
}
}
private void validateSlotIndex(int slot)
{
if (slot < 0 || slot >= getSlots())
throw new RuntimeException("Slot " + slot + " not in valid range - [0," + getSlots() + ")");
}
@Override
public int getSlotLimit(int slot)
{
return 64;
}
@Override
public boolean isItemValid(int slot, @NotNull ItemStack stack)
{
return stack.getItem().canFitInsideContainerItems();
}
@Override
public void setStackInSlot(int slot, @NotNull ItemStack stack)
{
validateSlotIndex(slot);
if (!isItemValid(slot, stack)) throw new RuntimeException("Invalid stack " + stack + " for slot " + slot + ")");
NonNullList<ItemStack> itemStacks = getItemList();
itemStacks.set(slot, stack);
setItemList(itemStacks);
}
private NonNullList<ItemStack> getItemList()
{
CompoundTag rootTag = BlockItem.getBlockEntityData(this.stack);
if (cachedTag == null || !cachedTag.equals(rootTag))
itemStacksCache = refreshItemList(rootTag);
return itemStacksCache;
}
private NonNullList<ItemStack> refreshItemList(CompoundTag rootTag)
{
NonNullList<ItemStack> itemStacks = NonNullList.withSize(getSlots(), ItemStack.EMPTY);
if (rootTag != null && rootTag.contains("Items", CompoundTag.TAG_LIST))
{
ContainerHelper.loadAllItems(rootTag, itemStacks);
}
cachedTag = rootTag;
return itemStacks;
}
private void setItemList(NonNullList<ItemStack> itemStacks)
{
CompoundTag existing = BlockItem.getBlockEntityData(this.stack);
CompoundTag rootTag = ContainerHelper.saveAllItems(existing == null ? new CompoundTag() : existing, itemStacks);
BlockItem.setBlockEntityData(this.stack, BlockEntityType.SHULKER_BOX, rootTag);
cachedTag = rootTag;
}
@Override
@NotNull
public <T> LazyOptional<T> getCapability(@NotNull Capability<T> cap, @Nullable Direction side)
{
return CapabilityItemHandler.ITEM_HANDLER_CAPABILITY.orEmpty(cap, this.holder);
}
}