From 2121fd00afcc1359b070ed910201e4945d38b2e3 Mon Sep 17 00:00:00 2001 From: Arthur Uzulin Date: Sat, 6 Jun 2020 16:07:19 +1000 Subject: [PATCH 1/5] Implemented proper item insertion logic for drawbridge + a workaround for hopper aggressiveness Fixes #102 --- .../tileentity/DrawbridgeTileEntity.java | 78 ++++++++++++++++++- .../common/inventory/FragmentedInventory.java | 2 +- 2 files changed, 77 insertions(+), 3 deletions(-) diff --git a/src/main/java/slimeknights/tmechworks/common/blocks/tileentity/DrawbridgeTileEntity.java b/src/main/java/slimeknights/tmechworks/common/blocks/tileentity/DrawbridgeTileEntity.java index 29e0f2d..b35d784 100644 --- a/src/main/java/slimeknights/tmechworks/common/blocks/tileentity/DrawbridgeTileEntity.java +++ b/src/main/java/slimeknights/tmechworks/common/blocks/tileentity/DrawbridgeTileEntity.java @@ -17,7 +17,9 @@ import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockRayTraceResult; import net.minecraft.util.math.Vec3d; -import net.minecraft.util.text.*; +import net.minecraft.util.text.ITextComponent; +import net.minecraft.util.text.StringTextComponent; +import net.minecraft.util.text.TranslationTextComponent; import net.minecraft.world.World; import net.minecraft.world.server.ServerWorld; import net.minecraft.world.storage.loot.LootContext; @@ -25,11 +27,13 @@ import net.minecraftforge.common.ToolType; import net.minecraftforge.common.util.BlockSnapshot; import net.minecraftforge.common.util.FakePlayer; +import net.minecraftforge.common.util.LazyOptional; import net.minecraftforge.event.ForgeEventFactory; -import slimeknights.tmechworks.common.config.MechworksConfig; +import net.minecraftforge.items.wrapper.InvWrapper; import slimeknights.tmechworks.common.MechworksContent; import slimeknights.tmechworks.common.blocks.DrawbridgeBlock; import slimeknights.tmechworks.common.blocks.RedstoneMachineBlock; +import slimeknights.tmechworks.common.config.MechworksConfig; import slimeknights.tmechworks.common.inventory.DrawbridgeContainer; import slimeknights.tmechworks.common.inventory.FragmentedInventory; import slimeknights.tmechworks.common.items.MachineUpgradeItem; @@ -62,6 +66,8 @@ public class DrawbridgeTileEntity extends RedstoneMachineTileEntity implements I private int extendedLength; private float cooldown; + private static boolean isCapAccess; + private long lastWorldTime; public DrawbridgeTileEntity() { @@ -69,6 +75,10 @@ public DrawbridgeTileEntity() { upgrades = new FragmentedInventory(this, 0, UPGRADES_SIZE).overrideStackLimit(1).setValidItemsPredicate(stack -> stack.getItem() instanceof MachineUpgradeItem); slots = new FragmentedInventory(this, UPGRADES_SIZE, 1).setValidItemsPredicate(stack -> stack.getItem() instanceof BlockItem && !DrawbridgeBlock.BLACKLIST.contains(Block.getBlockFromItem(stack.getItem()))).overrideStackLimit(64); + + itemHandlerCap.invalidate(); + itemHandler = new DrawbridgeItemHandler(this); + itemHandlerCap = LazyOptional.of(() -> itemHandler); } @Override @@ -537,6 +547,17 @@ public void setInventorySlotContents(int slot, @Nonnull ItemStack itemstack) { computeStats(); } + @Override + public boolean isItemValidForSlot(int slot, @Nonnull ItemStack stack) { + if(!isCapAccess) + return false; + + if(!slots.isSlotInInventory(slot - slots.getStartSlot())) + return false; + + return super.isItemValidForSlot(slot, stack) && slots.isItemValidForSlot(slot - slots.getStartSlot(), stack); + } + public FakePlayer getFakePlayer(BlockPos pos) { updateFakePlayer(pos); @@ -684,6 +705,59 @@ public BlockPos getPos() { } } + private static class DrawbridgeItemHandler extends InvWrapper { + private DrawbridgeTileEntity te; + + public DrawbridgeItemHandler(DrawbridgeTileEntity inv) { + super(inv); + + te = inv; + } + + @Nonnull + @Override + public ItemStack insertItem(int slotAbs, @Nonnull ItemStack stack, boolean simulate) { + int slot = slotAbs - te.slots.getStartSlot(); + + // Disallow inserting anywhere but in main inventory slots + if(slot < 0 || slot >= te.slots.getSizeInventory()) + return stack; + + isCapAccess = true; + ItemStack out = super.insertItem(slotAbs, stack, simulate); + isCapAccess = false; + return out; + } + + @Nonnull + @Override + public ItemStack extractItem(int slotAbs, int amount, boolean simulate) { + int slot = slotAbs - te.slots.getStartSlot(); + + // Disallow inserting anywhere but in main inventory slots + if(slot < 0 || slot >= te.slots.getSizeInventory()) + return ItemStack.EMPTY; + + isCapAccess = true; + ItemStack out = super.extractItem(slotAbs, amount, simulate); + isCapAccess = false; + return out; + } + + @Override + public boolean isItemValid(int slot, @Nonnull ItemStack stack) { + isCapAccess = true; + boolean out = super.isItemValid(slot, stack); + isCapAccess = false; + return out; + } + + @Override + public int getSlotLimit(int slot) { + return te.slots.getInventoryStackLimit(); + } + } + private static class DrawbridgeTools { public static final ItemStack PICKAXE; public static final ItemStack AXE; diff --git a/src/main/java/slimeknights/tmechworks/common/inventory/FragmentedInventory.java b/src/main/java/slimeknights/tmechworks/common/inventory/FragmentedInventory.java index f8b976b..cc6b7f8 100644 --- a/src/main/java/slimeknights/tmechworks/common/inventory/FragmentedInventory.java +++ b/src/main/java/slimeknights/tmechworks/common/inventory/FragmentedInventory.java @@ -133,7 +133,7 @@ public void closeInventory(PlayerEntity player) { @Override public boolean isItemValidForSlot(int slot, ItemStack itemStack) { - return validItems.test(itemStack) && parent.isItemValidForSlot(getSlot(slot), itemStack); + return isItemValidForValidatingSlot(slot, itemStack); } public boolean isSlotInInventory(int i) { From e6956d47a12ac31546e9dca089f7074bb992cd26 Mon Sep 17 00:00:00 2001 From: Arthur Uzulin Date: Sat, 6 Jun 2020 16:15:09 +1000 Subject: [PATCH 2/5] Fixed sending null to onPlayerDestroyItem in drawbridge - Code was based on ForgeHooks code, where this issue was also resolved recently (MinecraftForge/MinecraftForge#6633) Fixes #101 --- .../common/blocks/tileentity/DrawbridgeTileEntity.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/slimeknights/tmechworks/common/blocks/tileentity/DrawbridgeTileEntity.java b/src/main/java/slimeknights/tmechworks/common/blocks/tileentity/DrawbridgeTileEntity.java index b35d784..37feccd 100644 --- a/src/main/java/slimeknights/tmechworks/common/blocks/tileentity/DrawbridgeTileEntity.java +++ b/src/main/java/slimeknights/tmechworks/common/blocks/tileentity/DrawbridgeTileEntity.java @@ -619,7 +619,7 @@ public ActionResultType doPlaceBlock(DrawbridgeItemUseContext context) { if (!(itemstack.getItem() instanceof BucketItem)) // if not bucket world.captureBlockSnapshots = true; - ItemStack copy = itemstack.isDamageable() ? itemstack.copy() : null; + ItemStack copy = itemstack.isDamageable() ? itemstack.copy() : ItemStack.EMPTY; ActionResultType ret = item.tryPlace(context); if (itemstack.isEmpty()) ForgeEventFactory.onPlayerDestroyItem(player, copy, context.getHand()); From 962394a1538eba2a1f58997c0ea77349320686d4 Mon Sep 17 00:00:00 2001 From: Arthur Uzulin Date: Sat, 6 Jun 2020 16:20:18 +1000 Subject: [PATCH 3/5] Fixed inconsistency with ForgeHooks --- .../common/blocks/tileentity/DrawbridgeTileEntity.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/slimeknights/tmechworks/common/blocks/tileentity/DrawbridgeTileEntity.java b/src/main/java/slimeknights/tmechworks/common/blocks/tileentity/DrawbridgeTileEntity.java index 37feccd..24b17c2 100644 --- a/src/main/java/slimeknights/tmechworks/common/blocks/tileentity/DrawbridgeTileEntity.java +++ b/src/main/java/slimeknights/tmechworks/common/blocks/tileentity/DrawbridgeTileEntity.java @@ -619,7 +619,7 @@ public ActionResultType doPlaceBlock(DrawbridgeItemUseContext context) { if (!(itemstack.getItem() instanceof BucketItem)) // if not bucket world.captureBlockSnapshots = true; - ItemStack copy = itemstack.isDamageable() ? itemstack.copy() : ItemStack.EMPTY; + ItemStack copy = itemstack.copy(); ActionResultType ret = item.tryPlace(context); if (itemstack.isEmpty()) ForgeEventFactory.onPlayerDestroyItem(player, copy, context.getHand()); From 40763ba1106102611a26f7f68b56d19d1a94fb8e Mon Sep 17 00:00:00 2001 From: Arthur Uzulin Date: Sat, 6 Jun 2020 17:20:07 +1000 Subject: [PATCH 4/5] Fake player singleton, and use consistent UUID for fake player Closes #100 --- .../slimeknights/tmechworks/TMechworks.java | 4 ++ .../tileentity/DrawbridgeTileEntity.java | 4 +- .../common/entities/MechworksFakePlayer.java | 53 +++++++++++++++++++ .../event/DrawbridgeSoundEventListener.java | 3 +- .../slimeknights/tmechworks/library/Util.java | 9 ++-- 5 files changed, 63 insertions(+), 10 deletions(-) create mode 100644 src/main/java/slimeknights/tmechworks/common/entities/MechworksFakePlayer.java diff --git a/src/main/java/slimeknights/tmechworks/TMechworks.java b/src/main/java/slimeknights/tmechworks/TMechworks.java index c0f726c..216dec0 100644 --- a/src/main/java/slimeknights/tmechworks/TMechworks.java +++ b/src/main/java/slimeknights/tmechworks/TMechworks.java @@ -1,6 +1,7 @@ package slimeknights.tmechworks; import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.eventbus.api.IEventBus; import net.minecraftforge.fml.DistExecutor; import net.minecraftforge.fml.common.Mod; @@ -15,6 +16,7 @@ import slimeknights.tmechworks.common.CommonProxy; import slimeknights.tmechworks.common.MechworksContent; import slimeknights.tmechworks.common.config.MechworksConfig; +import slimeknights.tmechworks.common.entities.MechworksFakePlayer; import slimeknights.tmechworks.common.network.PacketHandler; import java.nio.file.Path; @@ -43,6 +45,8 @@ public TMechworks() { bus.addListener(this::postInit); bus.addListener(this::setupClient); + MinecraftForge.EVENT_BUS.addListener(MechworksFakePlayer::onWorldUnload); + content = new MechworksContent(); bus.register(content); } diff --git a/src/main/java/slimeknights/tmechworks/common/blocks/tileentity/DrawbridgeTileEntity.java b/src/main/java/slimeknights/tmechworks/common/blocks/tileentity/DrawbridgeTileEntity.java index 24b17c2..80c8412 100644 --- a/src/main/java/slimeknights/tmechworks/common/blocks/tileentity/DrawbridgeTileEntity.java +++ b/src/main/java/slimeknights/tmechworks/common/blocks/tileentity/DrawbridgeTileEntity.java @@ -479,9 +479,7 @@ public void setPlaceDirectionRelativeToBlock(Direction direction) { } public void updateFakePlayer(BlockPos pos) { - if (fakePlayer == null || fakePlayer.get() == null) { - fakePlayer = Util.createFakePlayer(world); - } + fakePlayer = Util.getFakePlayer(world); if (fakePlayer == null) { return; diff --git a/src/main/java/slimeknights/tmechworks/common/entities/MechworksFakePlayer.java b/src/main/java/slimeknights/tmechworks/common/entities/MechworksFakePlayer.java new file mode 100644 index 0000000..be2a95f --- /dev/null +++ b/src/main/java/slimeknights/tmechworks/common/entities/MechworksFakePlayer.java @@ -0,0 +1,53 @@ +package slimeknights.tmechworks.common.entities; + +import com.mojang.authlib.GameProfile; +import net.minecraft.potion.EffectInstance; +import net.minecraft.world.IWorld; +import net.minecraft.world.server.ServerWorld; +import net.minecraftforge.common.util.FakePlayer; +import net.minecraftforge.event.world.WorldEvent; +import slimeknights.tmechworks.TMechworks; + +import java.lang.ref.WeakReference; +import java.util.UUID; + +@SuppressWarnings("EntityConstructor") +public class MechworksFakePlayer extends FakePlayer { + public static final String NAME = "MechworksWorker"; + public static final UUID ID = UUID.nameUUIDFromBytes((TMechworks.modId + ".FakePlayer").getBytes()); + public static final GameProfile PROFILE = new GameProfile(ID, NAME); + + private static MechworksFakePlayer instance; + + private MechworksFakePlayer(ServerWorld world, GameProfile name) { + super(world, name); + } + + public static WeakReference getInstance(ServerWorld world) { + if (instance == null) { + instance = new MechworksFakePlayer(world, PROFILE); + } + + instance.world = world; + return new WeakReference<>(instance); + } + + private static void releaseInstance(IWorld world) { + // If the fake player has a reference to the world getting unloaded, + // null out the fake player so that the world can unload + if (instance != null && instance.world == world) { + instance = null; + } + } + + @Override + public boolean isPotionApplicable(EffectInstance potioneffectIn) { + return false; + } + + public static void onWorldUnload(WorldEvent.Unload event) { + if (event.getWorld() instanceof ServerWorld) { + releaseInstance(event.getWorld()); + } + } +} diff --git a/src/main/java/slimeknights/tmechworks/common/event/DrawbridgeSoundEventListener.java b/src/main/java/slimeknights/tmechworks/common/event/DrawbridgeSoundEventListener.java index bc9f811..e74ac57 100644 --- a/src/main/java/slimeknights/tmechworks/common/event/DrawbridgeSoundEventListener.java +++ b/src/main/java/slimeknights/tmechworks/common/event/DrawbridgeSoundEventListener.java @@ -7,6 +7,7 @@ import net.minecraftforge.fml.common.Mod; import org.apache.commons.lang3.StringUtils; import slimeknights.tmechworks.TMechworks; +import slimeknights.tmechworks.common.entities.MechworksFakePlayer; import slimeknights.tmechworks.library.Util; @Mod.EventBusSubscriber(modid = TMechworks.modId) @@ -18,7 +19,7 @@ public class DrawbridgeSoundEventListener { @SubscribeEvent public static void onSound(PlaySoundAtEntityEvent event){ Entity entity = event.getEntity(); - if(entity instanceof FakePlayer && StringUtils.equals(((FakePlayer) entity).getGameProfile().getName(), Util.FAKEPLAYER_NAME)) + if(entity instanceof FakePlayer && StringUtils.equals(((FakePlayer) entity).getGameProfile().getName(), MechworksFakePlayer.NAME)) event.setCanceled(true); } } diff --git a/src/main/java/slimeknights/tmechworks/library/Util.java b/src/main/java/slimeknights/tmechworks/library/Util.java index d5e8759..1afc47c 100644 --- a/src/main/java/slimeknights/tmechworks/library/Util.java +++ b/src/main/java/slimeknights/tmechworks/library/Util.java @@ -1,33 +1,30 @@ package slimeknights.tmechworks.library; -import com.mojang.authlib.GameProfile; import net.minecraft.util.ResourceLocation; import net.minecraft.world.World; import net.minecraft.world.server.ServerWorld; import net.minecraftforge.common.util.FakePlayer; -import net.minecraftforge.common.util.FakePlayerFactory; import org.apache.commons.lang3.StringUtils; import slimeknights.tmechworks.TMechworks; +import slimeknights.tmechworks.common.entities.MechworksFakePlayer; import java.lang.ref.WeakReference; import java.util.Locale; import java.util.Random; -import java.util.UUID; public class Util { public static final String RESOURCE = TMechworks.modId; public static final Random rand = new Random(); - public static final String FAKEPLAYER_NAME = "MechworksWorker"; - public static WeakReference createFakePlayer (World world) + public static WeakReference getFakePlayer(World world) { if (!(world instanceof ServerWorld)) { return null; } - return new WeakReference<>(FakePlayerFactory.get((ServerWorld) world, new GameProfile(UUID.randomUUID(), FAKEPLAYER_NAME))); + return MechworksFakePlayer.getInstance((ServerWorld)world); } /** From e7f6fb11c3e56f80ee24a1e98609953f95551ae1 Mon Sep 17 00:00:00 2001 From: Arthur Uzulin Date: Sat, 6 Jun 2020 18:15:10 +1000 Subject: [PATCH 5/5] Bump version to 2.1.1 --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index a004d9f..0d08bb2 100644 --- a/gradle.properties +++ b/gradle.properties @@ -3,7 +3,7 @@ org.gradle.jvmargs=-Xmx3G org.gradle.daemon=false -mod_version=2.1.0 +mod_version=2.1.1 minecraft_version=1.15.2 minecraft_version_short=1.15