Skip to content

Commit

Permalink
Rework itemHandler extraction and add "show resources" to buildTool (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
Nightenom authored Oct 28, 2024
1 parent ea04d71 commit ae55e35
Show file tree
Hide file tree
Showing 18 changed files with 405 additions and 114 deletions.
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ javaVersion=21
useJavaToolChains=true

#The currently running forge.
forgeVersion=21.1.4
forgeVersion=21.1.72

fmlRange=[4,)
forgeRange=[21.0.143,)
Expand Down
264 changes: 171 additions & 93 deletions src/main/java/com/ldtteam/structurize/api/ItemStackUtils.java
Original file line number Diff line number Diff line change
@@ -1,38 +1,36 @@
package com.ldtteam.structurize.api;

import com.ldtteam.common.fakelevel.SingleBlockFakeLevel.SidedSingleBlockFakeLevel;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.core.HolderLookup;
import net.minecraft.core.component.DataComponentMap;
import net.minecraft.core.component.DataComponentType;
import net.minecraft.core.component.DataComponents;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.ListTag;
import net.minecraft.nbt.Tag;
import net.minecraft.world.Container;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.decoration.ArmorStand;
import net.minecraft.world.entity.decoration.ItemFrame;
import net.minecraft.world.entity.vehicle.ContainerEntity;
import net.minecraft.world.entity.item.ItemEntity;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Items;
import net.minecraft.world.level.block.BaseEntityBlock;
import net.minecraft.world.item.SpawnEggItem;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.HitResult;
import net.minecraft.world.phys.Vec3;
import net.neoforged.neoforge.capabilities.Capabilities;
import net.minecraft.world.phys.EntityHitResult;
import net.neoforged.neoforge.capabilities.Capabilities.ItemHandler;
import net.neoforged.neoforge.items.IItemHandler;
import org.jetbrains.annotations.NotNull;
import net.neoforged.neoforge.items.wrapper.InvWrapper;
import org.jetbrains.annotations.Nullable;

import java.util.*;
import java.util.stream.Collectors;
import java.util.function.Consumer;

/**
* Utility methods for the inventories.
*/
public final class ItemStackUtils
{
public static final SidedSingleBlockFakeLevel ITEM_HANDLER_FAKE_LEVEL = new SidedSingleBlockFakeLevel();

/**
* Private constructor to hide the implicit one.
*/
Expand All @@ -44,87 +42,98 @@ private ItemStackUtils()
}

/**
* Get itemStack of tileEntityData. Retrieve the data from the tileEntity.
* Get itemStack of tileEntityData. Retrieve the data from the tileEntity. Including recursive content, eg. shulkers
*
* @param compound the tileEntity stored in a compound.
* @param state the block.
* @param level real vanilla instance for fakeLevel
* @return the list of itemstacks.
* @see #getListOfStackForEntity(Entity, BlockPos)
*/
public static List<ItemStack> getItemStacksOfTileEntity(final CompoundTag compound, final BlockState state, final HolderLookup.Provider provider)
public static List<ItemStack> getItemStacksOfTileEntity(final CompoundTag compound, final BlockState state, final Level level)
{
if (state.getBlock() instanceof BaseEntityBlock && compound.contains("Items"))
if (compound == null)
{
// because we're constructing the BlockEntity out-of-world below, chests (and perhaps a few others)
// can't generate an IItemHandler for us, so we need to read the contents manually.
// this could be removed if we always get a "real" BE from a world, but we're called both from a
// real world and from a schematic non-world, and the latter still breaks.
return getItemStacksFromNbt(compound, provider);
return List.of();
}

BlockPos blockpos = new BlockPos(compound.getInt("x"), compound.getInt("y"), compound.getInt("z"));
final BlockEntity tileEntity = BlockEntity.loadStatic(blockpos, state, compound, provider);
final BlockPos blockpos = new BlockPos(compound.getInt("x"), compound.getInt("y"), compound.getInt("z"));
final BlockEntity tileEntity = BlockEntity.loadStatic(blockpos, state, compound, level.registryAccess());
if (tileEntity == null)
{
return Collections.emptyList();
return List.of();
}

final List<ItemStack> items = new ArrayList<>();
for (final IItemHandler handler : getItemHandlersFromProvider(tileEntity))
{
for (int slot = 0; slot < handler.getSlots(); slot++)
{
final ItemStack stack = handler.getStackInSlot(slot);
if (!ItemStackUtils.isEmpty(stack))
{
items.add(stack);
}
}
}

return items;
return ITEM_HANDLER_FAKE_LEVEL.get(level).useFakeLevelContext(state, tileEntity, level, fakeLevel -> {
final List<ItemStack> items = new ArrayList<>();
getItemHandlersFromProvider(tileEntity, blockpos, state).forEach(itemHandler -> deepExtractItemHandler(itemHandler, items::add));
return items;
});
}

@NotNull
private static List<ItemStack> getItemStacksFromNbt(@NotNull final CompoundTag compound, final HolderLookup.Provider provider)
/**
* @param handler root itemHandler to extract
* @param sink where to put content of all found itemStacks, incl. recursive contents
*/
public static void deepExtractItemHandler(@Nullable final IItemHandler handler, final Consumer<ItemStack> sink)
{
final List<ItemStack> items = new ArrayList<>();
final ListTag listtag = compound.getList("Items", Tag.TAG_COMPOUND);
if (handler == null)
{
return;
}

for (int i = 0; i < listtag.size(); ++i)
for (int slot = 0; slot < handler.getSlots(); slot++)
{
final CompoundTag compoundtag = listtag.getCompound(i);
final ItemStack stack = ItemStack.parseOptional(provider, compoundtag);
if (!stack.isEmpty())
final ItemStack stack = handler.getStackInSlot(slot).copy();
if (!ItemStackUtils.isEmpty(stack))
{
items.add(stack);
sink.accept(stack);
deepExtractItemHandler(stack.getCapability(ItemHandler.ITEM), sink);
}
}

return items;
}

/**
* Method to get all the IItemHandlers from a given Provider.
* Method to get sensible item handlers from blockEntity. Tries to provide whole deduplicated content. However this assumption is
* weak. There still might be content (in returned set) that is not present at all or duplicated.
*
* @param provider The provider to get the IItemHandlers from.
* @return A list with all the unique IItemHandlers a provider has.
*/
public static Set<IItemHandler> getItemHandlersFromProvider(final BlockEntity provider)
public static Set<IItemHandler> getItemHandlersFromProvider(@Nullable final BlockEntity provider, final BlockPos pos, final BlockState state)
{
if (provider == null)
{
return Set.of();
}
if (provider instanceof final IItemHandler itemHandler)
{
// be is itemHandler itself = easy
return Set.of(itemHandler);
}
if (provider instanceof final Container container)
{
// be is vanilla container = itemHandler cap might return SidedInvWrapper with partial inv view
return Set.of(new InvWrapper(container));
}

final IItemHandler unsidedItemHandler = provider.getLevel().getCapability(ItemHandler.BLOCK, pos, state, provider, null);
if (unsidedItemHandler != null)
{
// weak assumption of unsided being partial view only
return Set.of(unsidedItemHandler);
}

final Set<IItemHandler> handlerSet = new HashSet<>();
for (final Direction side : Direction.values())
{
final IItemHandler cap = Capabilities.ItemHandler.BLOCK.getCapability(provider.getLevel(), provider.getBlockPos(), provider.getBlockState(), provider, side);
final IItemHandler cap = provider.getLevel().getCapability(ItemHandler.BLOCK, pos, state, provider, side);
if (cap != null)
{
handlerSet.add(cap);
}
}
final IItemHandler cap = Capabilities.ItemHandler.BLOCK.getCapability(provider.getLevel(), provider.getBlockPos(), provider.getBlockState(), provider, null);
if (cap != null)
{
handlerSet.add(cap);
}
// weakest assumption of sided itemHandler having disjoint sides
return handlerSet;
}

Expand Down Expand Up @@ -158,54 +167,123 @@ public static int getSize(final ItemStack stack)
}

/**
* Get the list of required resources for entities.
* @deprecated {@link #getListOfStackForEntity(Entity)}
*/
@Deprecated(forRemoval = true, since = "1.21.1")
public static List<ItemStack> getListOfStackForEntity(final Entity entity, final BlockPos pos)
{
return getListOfStackForEntity(entity);
}

/**
* Get the list of required resources for entities + entity spawning item. Same implementation as blockEntity logic. Including recursive content, eg. shulkers
*
* @param entity the entity object.
* @param pos the placer pos..
* @return a list of stacks.
* @see #getItemStacksOfTileEntity(BlockEntity)
*/
public static List<ItemStack> getListOfStackForEntity(final Entity entity, final BlockPos pos)
public static List<ItemStack> getListOfStackForEntity(final Entity entity)
{
if (entity != null)
if (entity == null)
{
return List.of();
}

final List<ItemStack> request = new ArrayList<>();

// process entity itself
final ItemStack spawnItem = getEntitySpawningItem(entity);
if (spawnItem != null && !(spawnItem.getItem() instanceof SpawnEggItem))
{
final List<ItemStack> request = new ArrayList<>();
if (entity instanceof final ItemFrame itemFrame)
request.add(spawnItem);
}

// process entity contents
request.addAll(getItemStacksOfEntity(entity));

return request.stream().filter(stack -> !stack.isEmpty()).toList();
}

/**
* Get the list of required resources for entities. Same implementation as blockEntity logic. Including recursive content, eg. shulkers
*
* @param entity the entity object.
* @return a list of stacks.
* @see #getItemStacksOfTileEntity(BlockEntity)
*/
public static List<ItemStack> getItemStacksOfEntity(final Entity entity)
{
if (entity == null)
{
return List.of();
}

final List<ItemStack> entityContent = new ArrayList<>();

IItemHandler itemHandler = null;
if (entity instanceof final IItemHandler iitemHandler)
{
// entity is itemHandler itself = easy
itemHandler = iitemHandler;
}
else if (entity instanceof final Container container)
{
// entity is vanilla container = itemHandler cap might return SidedInvWrapper with partial inv view
itemHandler = new InvWrapper(container);
}
if (itemHandler == null)
{
itemHandler = entity.getCapability(ItemHandler.ENTITY);
}
if (itemHandler == null)
{
// weak assumption of unsided being partial view only
itemHandler = entity.getCapability(ItemHandler.ENTITY_AUTOMATION, null);
}

if (itemHandler != null)
{
deepExtractItemHandler(itemHandler, entityContent::add);
}
// some vanilla entities "have inventory" but not forge cap yet
else if (entity instanceof final ItemFrame itemFrame)
{
final ItemStack stack = itemFrame.getItem();
entityContent.add(stack);
deepExtractItemHandler(stack.getCapability(ItemHandler.ITEM), entityContent::add);
}
else if (entity instanceof final ItemEntity itemEntity)
{
final ItemStack stack = itemEntity.getItem();
entityContent.add(stack);
deepExtractItemHandler(stack.getCapability(ItemHandler.ITEM), entityContent::add);
}
else // sided item handler
{
for (final Direction side : Direction.values())
{
final ItemStack stack = itemFrame.getItem();
if (!ItemStackUtils.isEmpty(stack))
final IItemHandler cap = entity.getCapability(ItemHandler.ENTITY_AUTOMATION, side);
if (cap != null)
{
stack.setCount(1);
request.add(stack);
deepExtractItemHandler(cap, entityContent::add);
}
request.add(new ItemStack(Items.ITEM_FRAME, 1));
}
else if (entity instanceof final ArmorStand armorStand)
{
request.add(entity.getPickedResult(new HitResult(Vec3.atLowerCornerOf(pos)) {
@Override
public Type getType()
{
return Type.ENTITY;
}
}));
armorStand.getArmorSlots().forEach(request::add);
armorStand.getHandSlots().forEach(request::add);
}
else if (entity instanceof ContainerEntity containerEntity)
{
request.add(entity.getPickedResult(new HitResult(Vec3.atLowerCornerOf(pos)) {
@Override
public Type getType()
{
return Type.ENTITY;
}
}));
request.addAll(containerEntity.getItemStacks());
}
}

return entityContent;
}

return request.stream().filter(stack -> !stack.isEmpty()).collect(Collectors.toList());
/**
* @return item that should spawn given entity
*/
@Nullable
public static ItemStack getEntitySpawningItem(final Entity entity)
{
if (entity instanceof final ItemFrame itemFrame)
{
return itemFrame.getFrameItemStack();
}
return Collections.emptyList();
return entity.getPickedResult(new EntityHitResult(entity));
}

/**
Expand Down
10 changes: 10 additions & 0 deletions src/main/java/com/ldtteam/structurize/api/ItemStorage.java
Original file line number Diff line number Diff line change
Expand Up @@ -190,4 +190,14 @@ public int getDamageValue()
{
return stack.getDamageValue();
}

/**
* Adder for the quantity.
*
* @param amount the amount to be added.
*/
public void addAmount(final int amount)
{
setAmount(getAmount() + amount);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,8 @@ public final class WindowConstants
*/
public static final String REMOVE_FILTERED = "removefiltered";

public static final String BUTTON_CONTENTS = "contents";

/**
* public constructor to hide implicit public one.
*/
Expand Down
Loading

0 comments on commit ae55e35

Please sign in to comment.