1
0
mirror of https://github.com/SquidDev-CC/CC-Tweaked synced 2025-08-28 16:22:18 +00:00

Turtles handle double chest inventories correctly. Cleaned up generic inventory code.

This commit is contained in:
David Queneau 2021-03-05 13:37:10 -08:00 committed by David Queneau
parent 9ae5636d41
commit d10f297ca0
4 changed files with 114 additions and 156 deletions

View File

@ -28,7 +28,6 @@ public class GenericPeripheralProvider
ArrayList<SaturatedMethod> saturated = new ArrayList<>( 0 );
// This seems to add inventory methods, how???
List<NamedMethod<PeripheralMethod>> tileMethods = PeripheralMethod.GENERATOR.getMethods( tile.getClass() );
if( !tileMethods.isEmpty() ) addSaturated( saturated, tile, tileMethods );

View File

@ -13,6 +13,8 @@ import dan200.computercraft.api.peripheral.IComputerAccess;
import dan200.computercraft.api.peripheral.IPeripheral;
import dan200.computercraft.core.asm.GenericSource;
import dan200.computercraft.shared.peripheral.generic.data.ItemData;
import dan200.computercraft.shared.util.InventoryUtil;
import dan200.computercraft.shared.util.ItemStorage;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.block.ChestBlock;
@ -60,10 +62,7 @@ public class InventoryMethods implements GenericSource
@LuaFunction( mainThread = true )
public static int size( Inventory inventory )
{
// Get appropriate inventory for source peripheral
inventory = extractHandler(inventory);
return inventory.size();
return extractHandler(inventory).size();
}
/**
@ -73,9 +72,13 @@ public class InventoryMethods implements GenericSource
* @return The name of this inventory, or {@code nil} if not present.
*/
@LuaFunction( mainThread = true )
public static String name( Nameable inventory )
public static String name( Inventory inventory )
{
return inventory.hasCustomName() ? inventory.getName().asString() : null;
if ( inventory instanceof Nameable ) {
Nameable i = (Nameable)inventory;
return i.hasCustomName() ? i.getName().asString() : null;
}
return null;
}
/**
@ -95,14 +98,13 @@ public class InventoryMethods implements GenericSource
@LuaFunction( mainThread = true )
public static Map<Integer, Map<String, ?>> list( Inventory inventory )
{
// Get appropriate inventory for source peripheral
inventory = extractHandler(inventory);
ItemStorage itemStorage = extractHandler(inventory);
Map<Integer, Map<String, ?>> result = new HashMap<>();
int size = inventory.size();
int size = itemStorage.size();
for( int i = 0; i < size; i++ )
{
ItemStack stack = inventory.getStack( i );
ItemStack stack = itemStorage.getStack( i );
if( !stack.isEmpty() ) result.put( i + 1, ItemData.fillBasic( new HashMap<>( 4 ), stack ) );
}
@ -122,12 +124,11 @@ public class InventoryMethods implements GenericSource
@LuaFunction( mainThread = true )
public static Map<String, ?> getItemDetail( Inventory inventory, int slot ) throws LuaException
{
// Get appropriate inventory
inventory = extractHandler(inventory);
ItemStorage itemStorage = extractHandler(inventory);
assertBetween( slot, 1, inventory.size(), "Slot out of range (%s)" );
assertBetween( slot, 1, itemStorage.size(), "Slot out of range (%s)" );
ItemStack stack = inventory.getStack( slot - 1 );
ItemStack stack = itemStorage.getStack( slot - 1 );
return stack.isEmpty() ? null : ItemData.fill( new HashMap<>(), stack );
}
@ -162,23 +163,22 @@ public class InventoryMethods implements GenericSource
String toName, int fromSlot, Optional<Integer> limit, Optional<Integer> toSlot
) throws LuaException
{
// Get appropriate inventory for source peripheral
from = extractHandler(from);
ItemStorage fromStorage = extractHandler( from );
// Find location to transfer to
IPeripheral location = computer.getAvailablePeripheral( toName );
if( location == null ) throw new LuaException( "Target '" + toName + "' does not exist" );
Inventory to = extractHandler( location.getTarget() );
if( to == null ) throw new LuaException( "Target '" + toName + "' is not an inventory" );
ItemStorage toStorage = extractHandler( location.getTarget() );
if( toStorage == null ) throw new LuaException( "Target '" + toName + "' is not an inventory" );
// Validate slots
int actualLimit = limit.orElse( Integer.MAX_VALUE );
assertBetween( fromSlot, 1, from.size(), "From slot out of range (%s)" );
if( toSlot.isPresent() ) assertBetween( toSlot.get(), 1, to.size(), "To slot out of range (%s)" );
assertBetween( fromSlot, 1, fromStorage.size(), "From slot out of range (%s)" );
if( toSlot.isPresent() ) assertBetween( toSlot.get(), 1, toStorage.size(), "To slot out of range (%s)" );
if( actualLimit <= 0 ) return 0;
return moveItem( from, fromSlot - 1, to, toSlot.orElse( 0 ) - 1, actualLimit );
return moveItem( fromStorage, fromSlot - 1, toStorage, toSlot.orElse( 0 ) - 1, actualLimit );
}
/**
@ -213,55 +213,36 @@ public class InventoryMethods implements GenericSource
) throws LuaException
{
// Get appropriate inventory for source peripheral
to = extractHandler(to);
ItemStorage toStorage = extractHandler( to );
// Find location to transfer to
IPeripheral location = computer.getAvailablePeripheral( fromName );
if( location == null ) throw new LuaException( "Source '" + fromName + "' does not exist" );
Inventory from = extractHandler( location.getTarget() );
if( from == null ) throw new LuaException( "Source '" + fromName + "' is not an inventory" );
ItemStorage fromStorage = extractHandler( location.getTarget() );
if( fromStorage == null ) throw new LuaException( "Source '" + fromName + "' is not an inventory" );
// Validate slots
int actualLimit = limit.orElse( Integer.MAX_VALUE );
assertBetween( fromSlot, 1, from.size(), "From slot out of range (%s)" );
if( toSlot.isPresent() ) assertBetween( toSlot.get(), 1, to.size(), "To slot out of range (%s)" );
assertBetween( fromSlot, 1, fromStorage.size(), "From slot out of range (%s)" );
if( toSlot.isPresent() ) assertBetween( toSlot.get(), 1, toStorage.size(), "To slot out of range (%s)" );
if( actualLimit <= 0 ) return 0;
return moveItem( from, fromSlot - 1, to, toSlot.orElse( 0 ) - 1, actualLimit );
return moveItem( fromStorage, fromSlot - 1, toStorage, toSlot.orElse( 0 ) - 1, actualLimit );
}
/**
* Extracts the most appropriate inventory from the object
* e.g., the correct inventory for a double chest or a sided inventory.
*
* @param object The handler to move from.
* @return The appropriate Inventory.
*/
@Nullable
private static Inventory extractHandler( @Nullable Object object )
private static ItemStorage extractHandler( @Nullable Object object )
{
Inventory inventory = null;
if (object instanceof BlockEntity ) {
BlockEntity blockEntity = (BlockEntity) object;
World world = blockEntity.getWorld();
BlockPos blockPos = blockEntity.getPos();
BlockState blockState = world.getBlockState(blockPos);
Block block = blockState.getBlock();
if (block instanceof InventoryProvider) {
inventory = ((InventoryProvider)block).getInventory(blockState, world, blockPos);
} else if (blockEntity instanceof Inventory) {
inventory = (Inventory)blockEntity;
if (inventory instanceof ChestBlockEntity && block instanceof ChestBlock) {
inventory = ChestBlock.getInventory((ChestBlock) block, blockState, world, blockPos, true);
}
if ( object instanceof BlockEntity ) {
Inventory inventory = InventoryUtil.getInventory((BlockEntity) object);
if ( inventory != null ) {
return ItemStorage.wrap(inventory);
}
}
return inventory;
return null;
}
/**
@ -274,109 +255,36 @@ public class InventoryMethods implements GenericSource
* @param limit The max number to move. {@link Integer#MAX_VALUE} for no limit.
* @return The number of items moved.
*/
private static int moveItem( Inventory from, int fromSlot, Inventory to, int toSlot, final int limit )
private static int moveItem( ItemStorage from, int fromSlot, ItemStorage to, int toSlot, final int limit )
{
/* ORIGINAL FORGE CODE
// See how much we can get out of this slot
// ItemStack extracted = from.extractItem( fromSlot, limit, true );
if( extracted.isEmpty() ) return 0;
// Limit the amount to extract
int extractCount = Math.min( extracted.getCount(), limit );
extracted.setCount( extractCount );
// ItemStack remainder = toSlot < 0 ? bItemHandlerHelper.insertItem( to, extracted, false ) : to.insertItem( toSlot, extracted, false );
int inserted = remainder.isEmpty() ? extractCount : extractCount - remainder.getCount();
if( inserted <= 0 ) return 0;
// Remove the item from the original inventory. Technically this could fail, but there's little we can do
// about that.
from.extractItem( fromSlot, inserted, false );
*/
// Vanilla minecraft inventory manipulation code
Boolean recurse = false;
ItemStack source = from.getStack( fromSlot );
int count = 0;
// If target slot was selected, only push items to that slot.
if (toSlot >= 0) {
int space = amountStackCanAddFrom(to.getStack(toSlot), source, to);
if (space == 0) return 0;
count = space;
}
// If target slot not selected, push items where they will fit, possibly
// across slots (by recurring on this method).
else if (toSlot < 0) {
recurse = true;
int[] result = getFirstValidSlotAndSpace(source, to);
toSlot = result[0];
if(toSlot < 0) return 0;
count = result[1];
}
// Respect slot restrictions
if (!to.isValid(toSlot, source)) { return 0; }
// Compare count available in target ItemStack to limit specified.
count = Math.min(count, limit);
if (count == 0) return 0;
// Mutate destination and source ItemStack
ItemStack destination = to.getStack(toSlot);
if (destination.isEmpty()) {
ItemStack newStack = source.copy();
newStack.setCount(count);
to.setStack(toSlot, newStack);
} else {
destination.increment(count);
}
source.decrement(count);
if (source.isEmpty()) from.setStack(fromSlot, ItemStack.EMPTY);
to.markDirty();
from.markDirty();
// Recurse if no explicit destination slot and more items exist in source slot
// and limit hasn't been reached. Else, return items moved.
if (recurse && !source.isEmpty()) return count + moveItem(from, fromSlot, to, -1, limit - count);
return count;
}
// Maybe there is a nicer existing way to do this in the minecraft codebase. I couldn't find it.
private static int[] getFirstValidSlotAndSpace(ItemStack fromStack, Inventory inventory) {
for (int i = 0; i < inventory.size(); i++) {
ItemStack stack = inventory.getStack(i);
int space = amountStackCanAddFrom(stack, fromStack, inventory);
if (space > 0) {
return new int[]{i, space};
}
}
return new int[]{-1, 0};
}
private static int amountStackCanAddFrom(ItemStack existingStack, ItemStack fromStack, Inventory inventory) {
if (fromStack.isEmpty()) {
// Moving nothing is easy
if (limit == 0) {
return 0;
}
else if (existingStack.isEmpty()) {
return Math.min(Math.min(existingStack.getMaxCount(),
inventory.getMaxCountPerStack()),
fromStack.getCount());
}
else if (InventoryMethods.areItemsEqual(existingStack, fromStack) &&
existingStack.isStackable() &&
existingStack.getCount() < existingStack.getMaxCount() &&
existingStack.getCount() < inventory.getMaxCountPerStack()) {
int stackSpace = existingStack.getMaxCount() - existingStack.getCount();
int invSpace = inventory.getMaxCountPerStack() - existingStack.getCount();
return Math.min(Math.min(stackSpace, invSpace), fromStack.getCount());
}
return 0;
}
private static boolean areItemsEqual(ItemStack stack1, ItemStack stack2) {
return stack1.getItem() == stack2.getItem() && ItemStack.areTagsEqual(stack1, stack2);
// Get stack to move
ItemStack stack = InventoryUtil.takeItems(limit, from, fromSlot, 1, fromSlot);
if (stack.isEmpty()) {
return 0;
}
int stackCount = stack.getCount();
// Move items in
ItemStack remainder;
if (toSlot < 0) {
remainder = InventoryUtil.storeItems(stack, to);
} else {
remainder = InventoryUtil.storeItems(stack, to, toSlot, 1, toSlot);
}
// Calculate items moved
int count = stackCount - remainder.getCount();
if (!remainder.isEmpty()) {
// Put the remainder back
InventoryUtil.storeItems(remainder, from, fromSlot, 1, fromSlot);
}
return count;
}
}

View File

@ -8,6 +8,11 @@ package dan200.computercraft.shared.util;
import javax.annotation.Nonnull;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.block.ChestBlock;
import net.minecraft.block.InventoryProvider;
import net.minecraft.block.entity.ChestBlockEntity;
import org.apache.commons.lang3.tuple.Pair;
import net.minecraft.block.entity.BlockEntity;
@ -33,9 +38,20 @@ public final class InventoryUtil {
// Look for tile with inventory
int y = pos.getY();
if (y >= 0 && y < world.getHeight()) {
BlockEntity tileEntity = world.getBlockEntity(pos);
if (tileEntity instanceof Inventory) {
return (Inventory) tileEntity;
// Check if block is InventoryProvider
BlockState blockState = world.getBlockState(pos);
Block block = blockState.getBlock();
if (block instanceof InventoryProvider) {
return ((InventoryProvider)block).getInventory(blockState, world, pos);
}
// Check if block is BlockEntity w/ Inventory
if (block.hasBlockEntity()) {
BlockEntity tileEntity = world.getBlockEntity(pos);
Inventory inventory = getInventory(tileEntity);
if (inventory != null) {
return inventory;
}
}
}
@ -55,6 +71,23 @@ public final class InventoryUtil {
return null;
}
public static Inventory getInventory(BlockEntity tileEntity) {
World world = tileEntity.getWorld();
BlockPos pos = tileEntity.getPos();
BlockState blockState = world.getBlockState(pos);
Block block = blockState.getBlock();
if (tileEntity instanceof Inventory) {
Inventory inventory = (Inventory)tileEntity;
if (inventory instanceof ChestBlockEntity && block instanceof ChestBlock) {
return ChestBlock.getInventory((ChestBlock) block, blockState, world, pos, true);
}
return inventory;
}
return null;
}
@Nonnull
public static ItemStack storeItems(@Nonnull ItemStack itemstack, ItemStorage inventory, int begin) {
return storeItems(itemstack, inventory, 0, inventory.size(), begin);

View File

@ -35,6 +35,9 @@ public interface ItemStorage {
int size();
@Nonnull
ItemStack getStack(int slot);
@Nonnull
ItemStack take(int slot, int limit, @Nonnull ItemStack filter, boolean simulate);
@ -57,6 +60,12 @@ public interface ItemStorage {
return this.inventory.size();
}
@Override
@Nonnull
public ItemStack getStack(int slot) {
return this.inventory.getStack(slot);
}
@Override
@Nonnull
public ItemStack take(int slot, int limit, @Nonnull ItemStack filter, boolean simulate) {
@ -203,6 +212,15 @@ public interface ItemStorage {
return this.size;
}
@Override
@Nonnull
public ItemStack getStack(int slot) {
if (slot < this.start || slot >= this.start + this.size) {
return ItemStack.EMPTY;
}
return this.parent.getStack(slot - this.start );
}
@Nonnull
@Override
public ItemStack take(int slot, int limit, @Nonnull ItemStack filter, boolean simulate) {