diff --git a/src/main/java/dan200/computercraft/api/turtle/ITurtleAccess.java b/src/main/java/dan200/computercraft/api/turtle/ITurtleAccess.java index add7c8654..eb379f04d 100644 --- a/src/main/java/dan200/computercraft/api/turtle/ITurtleAccess.java +++ b/src/main/java/dan200/computercraft/api/turtle/ITurtleAccess.java @@ -9,6 +9,9 @@ import com.mojang.authlib.GameProfile; import dan200.computercraft.api.lua.ILuaCallback; import dan200.computercraft.api.lua.MethodResult; import dan200.computercraft.api.peripheral.IPeripheral; +import dan200.computercraft.shared.util.InventoryUtil; +import dan200.computercraft.shared.util.ItemStorage; + import net.minecraft.inventory.Inventory; import net.minecraft.nbt.CompoundTag; import net.minecraft.util.math.BlockPos; @@ -282,4 +285,8 @@ public interface ITurtleAccess * @see #updateUpgradeNBTData(TurtleSide) */ void updateUpgradeNBTData( @Nonnull TurtleSide side ); + + default ItemStorage getItemHandler() { + return ItemStorage.wrap(this.getInventory()); + } } diff --git a/src/main/java/dan200/computercraft/client/ClientRegistry.java b/src/main/java/dan200/computercraft/client/ClientRegistry.java index 99165fc8f..70f0d0dc0 100644 --- a/src/main/java/dan200/computercraft/client/ClientRegistry.java +++ b/src/main/java/dan200/computercraft/client/ClientRegistry.java @@ -34,6 +34,10 @@ import java.util.function.Consumer; /** * Registers textures and models for items. */ +@SuppressWarnings ({ + "MethodCallSideOnly", + "LocalVariableDeclarationSideOnly" +}) public final class ClientRegistry { private static final String[] EXTRA_MODELS = new String[] { @@ -102,8 +106,8 @@ public final class ClientRegistry // Setup turtle colours ColorProviderRegistry.ITEM.register((stack, tintIndex) -> tintIndex == 0 ? ((IColouredItem) stack.getItem()).getColour(stack) : 0xFFFFFF, - ComputerCraft.Blocks.turtleNormal, - ComputerCraft.Blocks.turtleAdvanced); + Registry.ModBlocks.TURTLE_NORMAL, + Registry.ModBlocks.TURTLE_ADVANCED); } private static BakedModel bake(ModelLoader loader, UnbakedModel model) { diff --git a/src/main/java/dan200/computercraft/shared/turtle/core/TurtlePlayer.java b/src/main/java/dan200/computercraft/shared/turtle/core/TurtlePlayer.java index 5db80090e..36f6dceb4 100644 --- a/src/main/java/dan200/computercraft/shared/turtle/core/TurtlePlayer.java +++ b/src/main/java/dan200/computercraft/shared/turtle/core/TurtlePlayer.java @@ -5,6 +5,12 @@ */ package dan200.computercraft.shared.turtle.core; +import java.util.OptionalInt; +import java.util.UUID; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + import com.mojang.authlib.GameProfile; import dan200.computercraft.ComputerCraft; import dan200.computercraft.api.turtle.FakePlayer; @@ -13,6 +19,7 @@ import dan200.computercraft.shared.Registry; import dan200.computercraft.shared.util.FakeNetHandler; import dan200.computercraft.shared.util.InventoryUtil; import dan200.computercraft.shared.util.WorldUtil; + import net.minecraft.block.entity.SignBlockEntity; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityDimensions; @@ -28,13 +35,8 @@ import net.minecraft.util.Hand; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.Direction; import net.minecraft.util.math.Vec3d; -import net.minecraftforge.common.util.FakePlayer; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; -import java.util.OptionalInt; -import java.util.UUID; +@SuppressWarnings ("EntityConstructor") public final class TurtlePlayer extends FakePlayer { private static final GameProfile DEFAULT_PROFILE = new GameProfile( @@ -128,7 +130,7 @@ public final class TurtlePlayer extends FakePlayer @Override public EntityType getType() { - return Registry.ModEntities.TURTLE_PLAYER.get(); + return Registry.ModEntities.TURTLE_PLAYER; } @Override diff --git a/src/main/java/dan200/computercraft/shared/util/FakeNetHandler.java b/src/main/java/dan200/computercraft/shared/util/FakeNetHandler.java index 76285f9a1..8fe3bc6d8 100644 --- a/src/main/java/dan200/computercraft/shared/util/FakeNetHandler.java +++ b/src/main/java/dan200/computercraft/shared/util/FakeNetHandler.java @@ -5,10 +5,19 @@ */ package dan200.computercraft.shared.util; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import javax.crypto.SecretKey; + +import dan200.computercraft.api.turtle.FakePlayer; import io.netty.channel.ChannelHandlerContext; import io.netty.util.concurrent.Future; import io.netty.util.concurrent.GenericFutureListener; -import net.minecraft.network.*; + +import net.minecraft.network.ClientConnection; +import net.minecraft.network.NetworkSide; +import net.minecraft.network.NetworkState; +import net.minecraft.network.Packet; import net.minecraft.network.listener.PacketListener; import net.minecraft.network.packet.c2s.play.AdvancementTabC2SPacket; import net.minecraft.network.packet.c2s.play.BoatPaddleStateC2SPacket; @@ -52,14 +61,8 @@ import net.minecraft.network.packet.c2s.play.UpdateSelectedSlotC2SPacket; import net.minecraft.network.packet.c2s.play.UpdateSignC2SPacket; import net.minecraft.network.packet.c2s.play.UpdateStructureBlockC2SPacket; import net.minecraft.network.packet.c2s.play.VehicleMoveC2SPacket; -import net.minecraft.network.play.client.*; import net.minecraft.server.network.ServerPlayNetworkHandler; import net.minecraft.text.Text; -import net.minecraftforge.common.util.FakePlayer; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; -import javax.crypto.SecretKey; public class FakeNetHandler extends ServerPlayNetworkHandler { diff --git a/src/main/java/dan200/computercraft/shared/util/InventoryUtil.java b/src/main/java/dan200/computercraft/shared/util/InventoryUtil.java index bb0b36761..5171d154a 100644 --- a/src/main/java/dan200/computercraft/shared/util/InventoryUtil.java +++ b/src/main/java/dan200/computercraft/shared/util/InventoryUtil.java @@ -5,25 +5,19 @@ */ package dan200.computercraft.shared.util; +import javax.annotation.Nonnull; + +import org.apache.commons.lang3.tuple.Pair; + import net.minecraft.block.entity.BlockEntity; import net.minecraft.entity.Entity; import net.minecraft.inventory.Inventory; -import net.minecraft.inventory.SidedInventory; import net.minecraft.item.ItemStack; import net.minecraft.nbt.CompoundTag; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.Direction; import net.minecraft.util.math.Vec3d; import net.minecraft.world.World; -import net.minecraftforge.common.util.LazyOptional; -import net.minecraftforge.items.CapabilityItemHandler; -import net.minecraftforge.items.IItemHandler; -import net.minecraftforge.items.ItemHandlerHelper; -import net.minecraftforge.items.wrapper.InvWrapper; -import net.minecraftforge.items.wrapper.SidedInvWrapper; -import org.apache.commons.lang3.tuple.Pair; - -import javax.annotation.Nonnull; public final class InventoryUtil { @@ -37,14 +31,13 @@ public final class InventoryUtil public static boolean areItemsStackable( @Nonnull ItemStack a, @Nonnull ItemStack b ) { - return a == b || ItemHandlerHelper.canItemStacksStack( a, b ); + return a == b || (a.getItem() == b.getItem() && ItemStack.areTagsEqual( a, b )); } /** * Determines if two items are "mostly" equivalent. Namely, they have the same item and damage, and identical * share stacks. * - * This is largely based on {@link net.minecraftforge.common.crafting.IngredientNBT#test(ItemStack)}. It is * sufficient to ensure basic information (such as enchantments) are the same, while not having to worry about * capabilities. * @@ -61,34 +54,32 @@ public final class InventoryUtil // A more expanded form of ItemStack.areShareTagsEqual, but allowing an empty tag to be equal to a // null one. - CompoundTag shareTagA = a.getItem().getShareTag( a ); - CompoundTag shareTagB = b.getItem().getShareTag( b ); + CompoundTag shareTagA = a.getTag(); + CompoundTag shareTagB = b.getTag(); if( shareTagA == shareTagB ) return true; if( shareTagA == null ) return shareTagB.isEmpty(); if( shareTagB == null ) return shareTagA.isEmpty(); return shareTagA.equals( shareTagB ); } + @Nonnull + public static ItemStack copyItem( @Nonnull ItemStack a ) + { + return a.copy(); + } + // Methods for finding inventories: - public static IItemHandler getInventory( World world, BlockPos pos, Direction side ) + public static Inventory getInventory( World world, BlockPos pos, Direction side ) { // Look for tile with inventory - BlockEntity tileEntity = world.getBlockEntity( pos ); - if( tileEntity != null ) + int y = pos.getY(); + if( y >= 0 && y < world.getHeight() ) { - LazyOptional itemHandler = tileEntity.getCapability( CapabilityItemHandler.ITEM_HANDLER_CAPABILITY, side ); - if( itemHandler.isPresent() ) + BlockEntity tileEntity = world.getBlockEntity( pos ); + if( tileEntity instanceof Inventory ) { - return itemHandler.orElseThrow( NullPointerException::new ); - } - else if( side != null && tileEntity instanceof SidedInventory ) - { - return new SidedInvWrapper( (SidedInventory) tileEntity, side ); - } - else if( tileEntity instanceof Inventory ) - { - return new InvWrapper( (Inventory) tileEntity ); + return (Inventory) tileEntity; } } @@ -108,28 +99,34 @@ public final class InventoryUtil Entity entity = hit.getKey(); if( entity instanceof Inventory ) { - return new InvWrapper( (Inventory) entity ); + return (Inventory) entity; } } return null; } + public static ItemStorage getStorage( World world, BlockPos pos, Direction side ) + { + Inventory inventory = getInventory( world, pos, side ); + return inventory == null ? null : ItemStorage.wrap( inventory, side ); + } + // Methods for placing into inventories: @Nonnull - public static ItemStack storeItems( @Nonnull ItemStack itemstack, IItemHandler inventory, int begin ) + public static ItemStack storeItems( @Nonnull ItemStack itemstack, ItemStorage inventory, int begin ) { - return storeItems( itemstack, inventory, 0, inventory.getSlots(), begin ); + return storeItems( itemstack, inventory, 0, inventory.size(), begin ); } @Nonnull - public static ItemStack storeItems( @Nonnull ItemStack itemstack, IItemHandler inventory ) + public static ItemStack storeItems( @Nonnull ItemStack itemstack, ItemStorage inventory ) { - return storeItems( itemstack, inventory, 0, inventory.getSlots(), 0 ); + return storeItems( itemstack, inventory, 0, inventory.size(), 0 ); } @Nonnull - public static ItemStack storeItems( @Nonnull ItemStack stack, IItemHandler inventory, int start, int range, int begin ) + public static ItemStack storeItems( @Nonnull ItemStack stack, ItemStorage inventory, int start, int range, int begin ) { if( stack.isEmpty() ) return ItemStack.EMPTY; @@ -139,7 +136,7 @@ public final class InventoryUtil { int slot = start + (i + begin - start) % range; if( remainder.isEmpty() ) break; - remainder = inventory.insertItem( slot, remainder, false ); + remainder = inventory.store( slot, remainder, false ); } return areItemsEqual( stack, remainder ) ? stack : remainder; } @@ -147,51 +144,42 @@ public final class InventoryUtil // Methods for taking out of inventories @Nonnull - public static ItemStack takeItems( int count, IItemHandler inventory, int begin ) + public static ItemStack takeItems( int count, ItemStorage inventory, int begin ) { - return takeItems( count, inventory, 0, inventory.getSlots(), begin ); + return takeItems( count, inventory, 0, inventory.size(), begin ); } @Nonnull - public static ItemStack takeItems( int count, IItemHandler inventory ) + public static ItemStack takeItems( int count, ItemStorage inventory ) { - return takeItems( count, inventory, 0, inventory.getSlots(), 0 ); + return takeItems( count, inventory, 0, inventory.size(), 0 ); } @Nonnull - public static ItemStack takeItems( int count, IItemHandler inventory, int start, int range, int begin ) + public static ItemStack takeItems( int count, ItemStorage inventory, int start, int range, int begin ) { - // Combine multiple stacks from inventory into one if necessary ItemStack partialStack = ItemStack.EMPTY; for( int i = 0; i < range; i++ ) { int slot = start + (i + begin - start) % range; - // If we've extracted all items, return if( count <= 0 ) break; // If this doesn't slot, abort. - ItemStack stack = inventory.getStackInSlot( slot ); - if( !stack.isEmpty() && (partialStack.isEmpty() || areItemsStackable( stack, partialStack )) ) + ItemStack extracted = inventory.take( slot, count, partialStack, false ); + if( extracted.isEmpty() ) continue; + + count -= extracted.getCount(); + if( partialStack.isEmpty() ) { - ItemStack extracted = inventory.extractItem( slot, count, false ); - if( !extracted.isEmpty() ) - { - if( partialStack.isEmpty() ) - { - // If we've extracted for this first time, then limit the count to the maximum stack size. - partialStack = extracted; - count = Math.min( count, extracted.getMaxCount() ); - } - else - { - partialStack.increment( extracted.getCount() ); - } - - count -= extracted.getCount(); - } + // If we've extracted for this first time, then limit the count to the maximum stack size. + partialStack = extracted; + count = Math.min( count, extracted.getMaxCount() ); + } + else + { + partialStack.increment( extracted.getCount() ); } - } return partialStack; diff --git a/src/main/java/dan200/computercraft/shared/util/ItemStorage.java b/src/main/java/dan200/computercraft/shared/util/ItemStorage.java new file mode 100644 index 000000000..321716f7b --- /dev/null +++ b/src/main/java/dan200/computercraft/shared/util/ItemStorage.java @@ -0,0 +1,229 @@ +/* + * This file is part of ComputerCraft - http://www.computercraft.info + * Copyright Daniel Ratcliffe, 2011-2019. Do not distribute without permission. + * Send enquiries to dratcliffe@gmail.com + */ + +package dan200.computercraft.shared.util; + +import javax.annotation.Nonnull; + +import net.minecraft.inventory.Inventory; +import net.minecraft.inventory.SidedInventory; +import net.minecraft.item.ItemStack; +import net.minecraft.util.math.Direction; + +/** + * The most cutesy alternative of {@code IItemHandler} the world has ever seen. + */ +public interface ItemStorage { + int size(); + + @Nonnull + ItemStack take(int slot, int limit, @Nonnull ItemStack filter, boolean simulate); + + @Nonnull + ItemStack store(int slot, @Nonnull ItemStack stack, boolean simulate); + + default ItemStorage view(int start, int size) { + return new View(this, start, size); + } + + class InventoryWrapper implements ItemStorage { + private final Inventory inventory; + + InventoryWrapper(Inventory inventory) { + this.inventory = inventory; + } + + private void setAndDirty(int slot, @Nonnull ItemStack stack) { + inventory.setStack(slot, stack); + inventory.markDirty(); + } + + protected boolean canExtract(int slot, ItemStack stack) { + return true; + } + + @Override + public int size() { + return inventory.size(); + } + + @Override + @Nonnull + public ItemStack take(int slot, int limit, @Nonnull ItemStack filter, boolean simulate) { + ItemStack existing = inventory.getStack(slot); + if (existing.isEmpty() || !canExtract(slot, existing) || (!filter.isEmpty() && !areStackable(existing, filter))) { + return ItemStack.EMPTY; + } + + if (simulate) { + existing = existing.copy(); + if (existing.getCount() > limit) { + existing.setCount(limit); + } + return existing; + } else if (existing.getCount() < limit) { + setAndDirty(slot, ItemStack.EMPTY); + return existing; + } else { + ItemStack result = existing.split(limit); + setAndDirty(slot, existing); + return result; + } + } + + @Override + @Nonnull + public ItemStack store(int slot, @Nonnull ItemStack stack, boolean simulate) { + if (stack.isEmpty() || !inventory.isValid(slot, stack)) { + return stack; + } + + ItemStack existing = inventory.getStack(slot); + if (existing.isEmpty()) { + int limit = Math.min(stack.getMaxCount(), inventory.getMaxCountPerStack()); + if (limit <= 0) { + return stack; + } + + if (stack.getCount() < limit) { + if (!simulate) { + setAndDirty(slot, stack); + } + return ItemStack.EMPTY; + } else { + stack = stack.copy(); + ItemStack insert = stack.split(limit); + if (!simulate) { + setAndDirty(slot, insert); + } + return stack; + } + } else if (areStackable(stack, existing)) { + int limit = Math.min(existing.getMaxCount(), inventory.getMaxCountPerStack()) - existing.getCount(); + if (limit <= 0) { + return stack; + } + + if (stack.getCount() < limit) { + if (!simulate) { + existing.increment(stack.getCount()); + setAndDirty(slot, existing); + } + return ItemStack.EMPTY; + } else { + stack = stack.copy(); + stack.decrement(limit); + if (!simulate) { + existing.increment(limit); + setAndDirty(slot, existing); + } + return stack; + } + } else { + return stack; + } + } + } + + class SidedInventoryWrapper extends InventoryWrapper { + private final SidedInventory inventory; + private final Direction facing; + + SidedInventoryWrapper(SidedInventory inventory, Direction facing) { + super(inventory); + this.inventory = inventory; + this.facing = facing; + } + + @Override + public int size() { + return inventory.getAvailableSlots(facing).length; + } + + @Override + protected boolean canExtract(int slot, ItemStack stack) { + return super.canExtract(slot, stack) && inventory.canExtract(slot, stack, facing); + } + + @Nonnull + @Override + public ItemStack take(int slot, int limit, @Nonnull ItemStack filter, boolean simulate) { + int[] slots = inventory.getAvailableSlots(facing); + return slot >= 0 && slot < slots.length ? super.take(slots[slot], limit, filter, simulate) : ItemStack.EMPTY; + } + + @Nonnull + @Override + public ItemStack store(int slot, @Nonnull ItemStack stack, boolean simulate) { + int[] slots = inventory.getAvailableSlots(facing); + if (slot < 0 || slot >= slots.length) { + return stack; + } + + int mappedSlot = slots[slot]; + if (!inventory.canInsert(slot, stack, facing)) { + return stack; + } + return super.store(mappedSlot, stack, simulate); + } + } + + class View implements ItemStorage { + private final ItemStorage parent; + private final int start; + private final int size; + + View(ItemStorage parent, int start, int size) { + this.parent = parent; + this.start = start; + this.size = size; + } + + @Override + public int size() { + return size; + } + + @Nonnull + @Override + public ItemStack take(int slot, int limit, @Nonnull ItemStack filter, boolean simulate) { + if (slot < start || slot >= start + size) { + return ItemStack.EMPTY; + } + return parent.take(slot - start, limit, filter, simulate); + } + + @Nonnull + @Override + public ItemStack store(int slot, @Nonnull ItemStack stack, boolean simulate) { + if (slot < start || slot >= start + size) { + return stack; + } + return parent.store(slot - start, stack, simulate); + } + + @Override + public ItemStorage view(int start, int size) { + return new View(this.parent, this.start + start, size); + } + } + + static ItemStorage wrap(Inventory inventory) { + return new InventoryWrapper(inventory); + } + + static ItemStorage wrap(@Nonnull SidedInventory inventory, @Nonnull Direction facing) { + return new SidedInventoryWrapper(inventory, facing); + } + + static ItemStorage wrap(@Nonnull Inventory inventory, @Nonnull Direction facing) { + return inventory instanceof SidedInventory ? new SidedInventoryWrapper((SidedInventory) inventory, facing) : new InventoryWrapper(inventory); + } + + static boolean areStackable(@Nonnull ItemStack a, @Nonnull ItemStack b) { + return a == b || (a.getItem() == b.getItem() && ItemStack.areTagsEqual(a, b)); + } +}