diff --git a/src/main/java/org/cyclops/evilcraft/EvilCraft.java b/src/main/java/org/cyclops/evilcraft/EvilCraft.java index 71dc1d518b..7f34a9e3a5 100644 --- a/src/main/java/org/cyclops/evilcraft/EvilCraft.java +++ b/src/main/java/org/cyclops/evilcraft/EvilCraft.java @@ -472,6 +472,7 @@ public Class[] getGameTestClasses() { GameTestsEntangledChalice.class, GameTestsItemEternalWater.class, GameTestsItemStacking.class, + GameTestsPendant.class, GameTestsRecipes.class, GameTestsSpiritFurnace.class, GameTestsSpiritReanimator.class, diff --git a/src/main/java/org/cyclops/evilcraft/RegistryEntries.java b/src/main/java/org/cyclops/evilcraft/RegistryEntries.java index c14faf64cc..b57db0ed1e 100644 --- a/src/main/java/org/cyclops/evilcraft/RegistryEntries.java +++ b/src/main/java/org/cyclops/evilcraft/RegistryEntries.java @@ -78,6 +78,8 @@ public class RegistryEntries { public static final DeferredHolder ITEM_BOWL_OF_PROMISES_TIER3 = DeferredHolder.create(Registries.ITEM, Identifier.parse("evilcraft:bowl_of_promises_tier3")); public static final DeferredHolder ITEM_BOX_OF_ETERNAL_CLOSURE = DeferredHolder.create(Registries.ITEM, Identifier.parse("evilcraft:box_of_eternal_closure")); public static final DeferredHolder ITEM_BLOOD_EXTRACTOR = DeferredHolder.create(Registries.ITEM, Identifier.parse("evilcraft:blood_extractor")); + public static final DeferredHolder ITEM_INVIGORATING_PENDANT = DeferredHolder.create(Registries.ITEM, Identifier.parse("evilcraft:invigorating_pendant")); + public static final DeferredHolder ITEM_PRIMED_PENDANT = DeferredHolder.create(Registries.ITEM, Identifier.parse("evilcraft:primed_pendant")); public static final DeferredHolder ITEM_BURNING_GEM_STONE = DeferredHolder.create(Registries.ITEM, Identifier.parse("evilcraft:burning_gem_stone")); public static final DeferredHolder ITEM_ENTANGLED_CHALICE = DeferredHolder.create(Registries.ITEM, Identifier.parse("evilcraft:entangled_chalice")); public static final DeferredHolder ITEM_EXALTED_CRAFTER = DeferredHolder.create(Registries.ITEM, Identifier.parse("evilcraft:exalted_crafter")); diff --git a/src/main/java/org/cyclops/evilcraft/core/item/ItemBloodContainer.java b/src/main/java/org/cyclops/evilcraft/core/item/ItemBloodContainer.java index 8bef520340..c87e1ec115 100644 --- a/src/main/java/org/cyclops/evilcraft/core/item/ItemBloodContainer.java +++ b/src/main/java/org/cyclops/evilcraft/core/item/ItemBloodContainer.java @@ -164,6 +164,14 @@ public boolean doesSneakBypassUse(ItemStack stack, LevelReader world, BlockPos p return true; } + @Override + public boolean canDrain(int amount, ItemStack itemStack) { + // Avoid IllegalArgumentException when the fluid handler has no fluid (empty resource is not allowed in NeoForge extract calls) + FluidStack fluidStack = FluidUtil.getFirstStackContained(itemStack); + if (fluidStack.isEmpty()) return false; + return super.canDrain(amount, itemStack); + } + protected FluidStack drainFromOthers(int amount, ItemStack itemStack, Fluid fluid, Player player, TransactionContext transaction) { PlayerExtendedInventoryIterator it = new PlayerExtendedInventoryIterator(player); int drained = 0; @@ -210,7 +218,8 @@ public FluidStack consume(int amount, ItemStack itemStack, @Nullable Player play try (var tx = Transaction.openRoot()) { ResourceHandler fluidHandler = itemStack.getCapability(Capabilities.Fluid.ITEM, ItemAccess.forStack(itemStack)); FluidResource resource = fluidHandler.getResource(0); - int drained = fluidHandler.extract(resource, amount, tx); + // Avoid IllegalArgumentException when the fluid handler has no fluid (empty resource is not allowed in NeoForge extract calls) + int drained = resource.isEmpty() ? 0 : fluidHandler.extract(resource, amount, tx); if (drained == amount) return resource.toStack(drained); int toDrain = amount - drained; FluidStack otherDrained = player == null ? FluidStack.EMPTY : drainFromOthers(toDrain, itemStack, getFluid(), player, tx); diff --git a/src/main/java/org/cyclops/evilcraft/gametest/GameTestsPendant.java b/src/main/java/org/cyclops/evilcraft/gametest/GameTestsPendant.java new file mode 100644 index 0000000000..fc01cc6a6d --- /dev/null +++ b/src/main/java/org/cyclops/evilcraft/gametest/GameTestsPendant.java @@ -0,0 +1,47 @@ +package org.cyclops.evilcraft.gametest; + +import net.minecraft.gametest.framework.GameTestHelper; +import net.minecraft.network.chat.Component; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.GameType; +import org.cyclops.cyclopscore.gametest.GameTest; +import org.cyclops.evilcraft.Reference; +import org.cyclops.evilcraft.RegistryEntries; + +public class GameTestsPendant { + + public static final String TEMPLATE_EMPTY = Reference.MOD_ID + ":empty10"; + + /** + * Verifies that having an empty invigorating pendant in a player's inventory does not crash. + * The crash was caused by DamageIndicatedItemFluidContainer.canDrain calling extract on an empty + * fluid resource (minecraft:empty), which throws IllegalArgumentException in NeoForge 21.11.38-beta+. + */ + @GameTest(template = TEMPLATE_EMPTY) + public void testInvigoratingPendantInventoryTickEmptyNoCrash(GameTestHelper helper) { + Player player = helper.makeMockPlayer(GameType.SURVIVAL); + ItemStack pendantStack = new ItemStack(RegistryEntries.ITEM_INVIGORATING_PENDANT); + + // Directly invoke clearBadEffects on an empty pendant - should not throw + RegistryEntries.ITEM_INVIGORATING_PENDANT.get().clearBadEffects(pendantStack, player); + + helper.succeed(); + } + + /** + * Verifies that having an empty primed pendant in a player's inventory does not crash. + */ + @GameTest(template = TEMPLATE_EMPTY) + public void testPrimedPendantInventoryTickEmptyNoCrash(GameTestHelper helper) { + Player player = helper.makeMockPlayer(GameType.SURVIVAL); + ItemStack pendantStack = new ItemStack(RegistryEntries.ITEM_PRIMED_PENDANT); + + // canConsume on empty pendant should return false without throwing + boolean canConsume = RegistryEntries.ITEM_PRIMED_PENDANT.get().canConsume(1, pendantStack, player); + helper.assertFalse(canConsume, Component.literal("Empty pendant should not be consumable")); + + helper.succeed(); + } + +}