|
|
|
|
@@ -14,18 +14,15 @@ import dan200.computercraft.api.peripheral.IPeripheral;
|
|
|
|
|
import dan200.computercraft.api.peripheral.PeripheralType;
|
|
|
|
|
import dan200.computercraft.shared.peripheral.generic.SidedGenericPeripheral;
|
|
|
|
|
import dan200.computercraft.shared.platform.FabricContainerTransfer;
|
|
|
|
|
import net.fabricmc.fabric.api.transfer.v1.item.InventoryStorage;
|
|
|
|
|
import net.fabricmc.fabric.api.transfer.v1.item.ItemStorage;
|
|
|
|
|
import net.fabricmc.fabric.api.transfer.v1.item.ItemVariant;
|
|
|
|
|
import net.fabricmc.fabric.api.transfer.v1.storage.Storage;
|
|
|
|
|
import net.fabricmc.fabric.api.transfer.v1.storage.SlottedStorage;
|
|
|
|
|
import net.fabricmc.fabric.api.transfer.v1.storage.base.SingleSlotStorage;
|
|
|
|
|
import net.minecraft.core.BlockPos;
|
|
|
|
|
import net.minecraft.core.Direction;
|
|
|
|
|
import net.minecraft.world.item.ItemStack;
|
|
|
|
|
import net.minecraft.world.level.Level;
|
|
|
|
|
import net.minecraft.world.level.block.ChestBlock;
|
|
|
|
|
import net.minecraft.world.level.block.entity.BlockEntity;
|
|
|
|
|
import net.minecraft.world.level.block.entity.ChestBlockEntity;
|
|
|
|
|
import net.minecraft.world.level.block.state.BlockState;
|
|
|
|
|
|
|
|
|
|
import javax.annotation.Nullable;
|
|
|
|
|
@@ -50,15 +47,25 @@ public class InventoryMethods implements GenericPeripheral {
|
|
|
|
|
return ComputerCraftAPI.MOD_ID + ":inventory";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@LuaFunction(mainThread = true)
|
|
|
|
|
public static int size(InventoryStorage inventory) {
|
|
|
|
|
return inventory.getSlots().size();
|
|
|
|
|
/**
|
|
|
|
|
* Wrapper over a {@link SlottedStorage}.
|
|
|
|
|
* <p>
|
|
|
|
|
* The generic peripheral system doesn't (currently) support generics, and so we need put the inventory in a box.
|
|
|
|
|
*
|
|
|
|
|
* @param storage The underlying storage
|
|
|
|
|
*/
|
|
|
|
|
public record StorageWrapper(SlottedStorage<ItemVariant> storage) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@LuaFunction(mainThread = true)
|
|
|
|
|
public static Map<Integer, Map<String, ?>> list(InventoryStorage inventory) {
|
|
|
|
|
public static int size(StorageWrapper inventory) {
|
|
|
|
|
return inventory.storage().getSlots().size();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@LuaFunction(mainThread = true)
|
|
|
|
|
public static Map<Integer, Map<String, ?>> list(StorageWrapper inventory) {
|
|
|
|
|
Map<Integer, Map<String, ?>> result = new HashMap<>();
|
|
|
|
|
var slots = inventory.getSlots();
|
|
|
|
|
var slots = inventory.storage().getSlots();
|
|
|
|
|
var size = slots.size();
|
|
|
|
|
for (var i = 0; i < size; i++) {
|
|
|
|
|
var stack = toStack(slots.get(i));
|
|
|
|
|
@@ -70,22 +77,22 @@ public class InventoryMethods implements GenericPeripheral {
|
|
|
|
|
|
|
|
|
|
@Nullable
|
|
|
|
|
@LuaFunction(mainThread = true)
|
|
|
|
|
public static Map<String, ?> getItemDetail(InventoryStorage inventory, int slot) throws LuaException {
|
|
|
|
|
assertBetween(slot, 1, inventory.getSlots().size(), "Slot out of range (%s)");
|
|
|
|
|
public static Map<String, ?> getItemDetail(StorageWrapper inventory, int slot) throws LuaException {
|
|
|
|
|
assertBetween(slot, 1, inventory.storage().getSlotCount(), "Slot out of range (%s)");
|
|
|
|
|
|
|
|
|
|
var stack = toStack(inventory.getSlot(slot - 1));
|
|
|
|
|
var stack = toStack(inventory.storage().getSlot(slot - 1));
|
|
|
|
|
return stack.isEmpty() ? null : VanillaDetailRegistries.ITEM_STACK.getDetails(stack);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@LuaFunction(mainThread = true)
|
|
|
|
|
public static long getItemLimit(InventoryStorage inventory, int slot) throws LuaException {
|
|
|
|
|
assertBetween(slot, 1, inventory.getSlots().size(), "Slot out of range (%s)");
|
|
|
|
|
return inventory.getSlot(slot - 1).getCapacity();
|
|
|
|
|
public static long getItemLimit(StorageWrapper inventory, int slot) throws LuaException {
|
|
|
|
|
assertBetween(slot, 1, inventory.storage().getSlotCount(), "Slot out of range (%s)");
|
|
|
|
|
return inventory.storage().getSlot(slot - 1).getCapacity();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@LuaFunction(mainThread = true)
|
|
|
|
|
public static int pushItems(
|
|
|
|
|
InventoryStorage from, IComputerAccess computer,
|
|
|
|
|
StorageWrapper from, IComputerAccess computer,
|
|
|
|
|
String toName, int fromSlot, Optional<Integer> limit, Optional<Integer> toSlot
|
|
|
|
|
) throws LuaException {
|
|
|
|
|
// Find location to transfer to
|
|
|
|
|
@@ -95,65 +102,68 @@ public class InventoryMethods implements GenericPeripheral {
|
|
|
|
|
var to = extractHandler(location);
|
|
|
|
|
if (to == null) throw new LuaException("Target '" + toName + "' is not an inventory");
|
|
|
|
|
|
|
|
|
|
var fromStorage = from.storage();
|
|
|
|
|
|
|
|
|
|
// Validate slots
|
|
|
|
|
int actualLimit = limit.orElse(Integer.MAX_VALUE);
|
|
|
|
|
assertBetween(fromSlot, 1, from.getSlots().size(), "From slot out of range (%s)");
|
|
|
|
|
assertBetween(fromSlot, 1, fromStorage.getSlotCount(), "From slot out of range (%s)");
|
|
|
|
|
if (toSlot.isPresent()) assertBetween(toSlot.get(), 1, to.getSlots().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, to, toSlot.orElse(0) - 1, actualLimit);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@LuaFunction(mainThread = true)
|
|
|
|
|
public static int pullItems(
|
|
|
|
|
InventoryStorage to, IComputerAccess computer,
|
|
|
|
|
StorageWrapper to, IComputerAccess computer,
|
|
|
|
|
String fromName, int fromSlot, Optional<Integer> limit, Optional<Integer> toSlot
|
|
|
|
|
) throws LuaException {
|
|
|
|
|
// Find location to transfer to
|
|
|
|
|
var location = computer.getAvailablePeripheral(fromName);
|
|
|
|
|
if (location == null) throw new LuaException("Source '" + fromName + "' does not exist");
|
|
|
|
|
|
|
|
|
|
var toStorage = to.storage();
|
|
|
|
|
|
|
|
|
|
var from = extractHandler(location);
|
|
|
|
|
if (from == null) throw new LuaException("Source '" + fromName + "' is not an inventory");
|
|
|
|
|
|
|
|
|
|
// Validate slots
|
|
|
|
|
int actualLimit = limit.orElse(Integer.MAX_VALUE);
|
|
|
|
|
assertBetween(fromSlot, 1, from.getSlots().size(), "From slot out of range (%s)");
|
|
|
|
|
if (toSlot.isPresent()) assertBetween(toSlot.get(), 1, to.getSlots().size(), "To slot out of range (%s)");
|
|
|
|
|
if (toSlot.isPresent()) assertBetween(toSlot.get(), 1, toStorage.getSlotCount(), "To slot out of range (%s)");
|
|
|
|
|
|
|
|
|
|
if (actualLimit <= 0) return 0;
|
|
|
|
|
return moveItem(from, fromSlot - 1, to, toSlot.orElse(0) - 1, actualLimit);
|
|
|
|
|
return moveItem(from, fromSlot - 1, toStorage, toSlot.orElse(0) - 1, actualLimit);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static @Nullable StorageWrapper extractContainer(Level level, BlockPos pos, BlockState state, @Nullable BlockEntity blockEntity, @Nullable Direction direction) {
|
|
|
|
|
var storage = extractContainerImpl(level, pos, state, blockEntity, direction);
|
|
|
|
|
return storage == null ? null : new StorageWrapper(storage);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@SuppressWarnings("NullAway") // FIXME: Doesn't cope with @Nullable type parameter.
|
|
|
|
|
public static @Nullable Storage<ItemVariant> extractContainer(Level level, BlockPos pos, BlockState state, @Nullable BlockEntity blockEntity, @Nullable Direction direction) {
|
|
|
|
|
// ItemStorage returns a CombinedStorage rather than an InventoryStorage for double chests.
|
|
|
|
|
if (blockEntity instanceof ChestBlockEntity && state.getBlock() instanceof ChestBlock chestBlock) {
|
|
|
|
|
var inventory = ChestBlock.getContainer(chestBlock, state, level, pos, true);
|
|
|
|
|
return inventory == null ? null : InventoryStorage.of(inventory, null);
|
|
|
|
|
private static @Nullable SlottedStorage<ItemVariant> extractContainerImpl(Level level, BlockPos pos, BlockState state, @Nullable BlockEntity blockEntity, @Nullable Direction direction) {
|
|
|
|
|
var internal = ItemStorage.SIDED.find(level, pos, state, blockEntity, null);
|
|
|
|
|
if (internal instanceof SlottedStorage<ItemVariant> storage) return storage;
|
|
|
|
|
|
|
|
|
|
if (direction != null) {
|
|
|
|
|
var external = ItemStorage.SIDED.find(level, pos, state, blockEntity, direction);
|
|
|
|
|
if (external instanceof SlottedStorage<ItemVariant> storage) return storage;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var internal = ItemStorage.SIDED.find(level, pos, state, blockEntity, null);
|
|
|
|
|
if (internal instanceof InventoryStorage || direction == null) return internal;
|
|
|
|
|
|
|
|
|
|
var external = ItemStorage.SIDED.find(level, pos, state, blockEntity, direction);
|
|
|
|
|
if (external instanceof InventoryStorage) return external;
|
|
|
|
|
|
|
|
|
|
return internal != null ? internal : external;
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Nullable
|
|
|
|
|
private static InventoryStorage extractHandler(IPeripheral peripheral) {
|
|
|
|
|
private static SlottedStorage<ItemVariant> extractHandler(IPeripheral peripheral) {
|
|
|
|
|
var object = peripheral.getTarget();
|
|
|
|
|
var direction = peripheral instanceof SidedGenericPeripheral sided ? sided.direction() : null;
|
|
|
|
|
|
|
|
|
|
if (object instanceof BlockEntity blockEntity && blockEntity.isRemoved()) return null;
|
|
|
|
|
|
|
|
|
|
if (object instanceof InventoryStorage storage) return storage;
|
|
|
|
|
|
|
|
|
|
if (object instanceof BlockEntity blockEntity) {
|
|
|
|
|
var found = extractContainer(blockEntity.getLevel(), blockEntity.getBlockPos(), blockEntity.getBlockState(), blockEntity, direction);
|
|
|
|
|
if (found instanceof InventoryStorage storage) return storage;
|
|
|
|
|
if (blockEntity.isRemoved()) return null;
|
|
|
|
|
|
|
|
|
|
var found = extractContainerImpl(blockEntity.getLevel(), blockEntity.getBlockPos(), blockEntity.getBlockState(), blockEntity, direction);
|
|
|
|
|
if (found != null) return found;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return null;
|
|
|
|
|
@@ -169,7 +179,7 @@ public class InventoryMethods implements GenericPeripheral {
|
|
|
|
|
* @param limit The max number to move. {@link Integer#MAX_VALUE} for no limit.
|
|
|
|
|
* @return The number of items moved.
|
|
|
|
|
*/
|
|
|
|
|
private static int moveItem(InventoryStorage from, int fromSlot, InventoryStorage to, int toSlot, final int limit) {
|
|
|
|
|
private static int moveItem(SlottedStorage<ItemVariant> from, int fromSlot, SlottedStorage<ItemVariant> to, int toSlot, final int limit) {
|
|
|
|
|
var fromWrapper = FabricContainerTransfer.of(from).singleSlot(fromSlot);
|
|
|
|
|
var toWrapper = FabricContainerTransfer.of(to);
|
|
|
|
|
|
|
|
|
|
|