From 2259220c93793d287650c0c5a9e91cfb89061729 Mon Sep 17 00:00:00 2001 From: Technici4n <13494793+Technici4n@users.noreply.github.com> Date: Mon, 11 Dec 2023 19:15:28 +0100 Subject: [PATCH 1/6] Implement fluid handler support for cauldrons --- .../block/AbstractCauldronBlock.java.patch | 24 +++ .../neoforge/common/NeoForgeMod.java | 2 + .../neoforge/fluids/CauldronFluidContent.java | 169 ++++++++++++++++++ .../RegisterCauldronFluidContentEvent.java | 48 +++++ .../capability/wrappers/CauldronWrapper.java | 139 ++++++++++++++ .../neoforge/internal/RegistrationEvents.java | 4 +- .../capabilities/VanillaHandlersTests.java | 146 +++++++++++++++ .../capabilities/CapabilitiesTest.java | 23 --- .../capabilities/VanillaItemHandlerTests.java | 69 ------- tests/src/main/resources/META-INF/mods.toml | 2 - 10 files changed, 531 insertions(+), 95 deletions(-) create mode 100644 patches/net/minecraft/world/level/block/AbstractCauldronBlock.java.patch create mode 100644 src/main/java/net/neoforged/neoforge/fluids/CauldronFluidContent.java create mode 100644 src/main/java/net/neoforged/neoforge/fluids/RegisterCauldronFluidContentEvent.java create mode 100644 src/main/java/net/neoforged/neoforge/fluids/capability/wrappers/CauldronWrapper.java create mode 100644 tests/src/main/java/net/neoforged/neoforge/debug/capabilities/VanillaHandlersTests.java delete mode 100644 tests/src/main/java/net/neoforged/neoforge/oldtest/capabilities/CapabilitiesTest.java delete mode 100644 tests/src/main/java/net/neoforged/neoforge/oldtest/capabilities/VanillaItemHandlerTests.java diff --git a/patches/net/minecraft/world/level/block/AbstractCauldronBlock.java.patch b/patches/net/minecraft/world/level/block/AbstractCauldronBlock.java.patch new file mode 100644 index 0000000000..2cc9a096a6 --- /dev/null +++ b/patches/net/minecraft/world/level/block/AbstractCauldronBlock.java.patch @@ -0,0 +1,24 @@ +--- a/net/minecraft/world/level/block/AbstractCauldronBlock.java ++++ b/net/minecraft/world/level/block/AbstractCauldronBlock.java +@@ -102,4 +_,21 @@ + + protected void receiveStalactiteDrip(BlockState p_151975_, Level p_151976_, BlockPos p_151977_, Fluid p_151978_) { + } ++ ++ @Override ++ public void onPlace(BlockState p_51978_, Level p_51979_, BlockPos p_51980_, BlockState p_51981_, boolean p_51982_) { ++ // Neo: Invalidate cauldron capabilities when a cauldron is added ++ if (net.neoforged.neoforge.fluids.CauldronFluidContent.getForBlock(p_51981_.getBlock()) == null) { ++ p_51979_.invalidateCapabilities(p_51980_); ++ } ++ } ++ ++ @Override ++ public void onRemove(BlockState p_60515_, Level p_60516_, BlockPos p_60517_, BlockState p_60518_, boolean p_60519_) { ++ super.onRemove(p_60515_, p_60516_, p_60517_, p_60518_, p_60519_); ++ // Neo: Invalidate cauldron capabilities when a cauldron is removed ++ if (net.neoforged.neoforge.fluids.CauldronFluidContent.getForBlock(p_60518_.getBlock()) == null) { ++ p_60516_.invalidateCapabilities(p_60517_); ++ } ++ } + } diff --git a/src/main/java/net/neoforged/neoforge/common/NeoForgeMod.java b/src/main/java/net/neoforged/neoforge/common/NeoForgeMod.java index 8e5337efee..a7392a6d5d 100644 --- a/src/main/java/net/neoforged/neoforge/common/NeoForgeMod.java +++ b/src/main/java/net/neoforged/neoforge/common/NeoForgeMod.java @@ -124,6 +124,7 @@ import net.neoforged.neoforge.data.event.GatherDataEvent; import net.neoforged.neoforge.event.server.ServerStoppingEvent; import net.neoforged.neoforge.fluids.BaseFlowingFluid; +import net.neoforged.neoforge.fluids.CauldronFluidContent; import net.neoforged.neoforge.fluids.FluidType; import net.neoforged.neoforge.forge.snapshots.ForgeSnapshotsMod; import net.neoforged.neoforge.internal.versions.neoforge.NeoForgeVersion; @@ -507,6 +508,7 @@ public NeoForgeMod(IEventBus modEventBus, Dist dist) { DualStackUtils.initialise(); modEventBus.addListener(CapabilityHooks::registerVanillaProviders); + modEventBus.addListener(CauldronFluidContent::registerCapabilities); // These 3 listeners use the default priority for now, can be re-evaluated later. NeoForge.EVENT_BUS.addListener(CapabilityHooks::invalidateCapsOnChunkLoad); NeoForge.EVENT_BUS.addListener(CapabilityHooks::invalidateCapsOnChunkUnload); diff --git a/src/main/java/net/neoforged/neoforge/fluids/CauldronFluidContent.java b/src/main/java/net/neoforged/neoforge/fluids/CauldronFluidContent.java new file mode 100644 index 0000000000..f9552bce04 --- /dev/null +++ b/src/main/java/net/neoforged/neoforge/fluids/CauldronFluidContent.java @@ -0,0 +1,169 @@ +package net.neoforged.neoforge.fluids; + +import java.util.Collection; +import java.util.IdentityHashMap; +import java.util.Map; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.LayeredCauldronBlock; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.block.state.properties.IntegerProperty; +import net.minecraft.world.level.material.Fluid; +import net.minecraft.world.level.material.Fluids; +import net.neoforged.fml.ModLoader; +import net.neoforged.neoforge.capabilities.Capabilities; +import net.neoforged.neoforge.capabilities.RegisterCapabilitiesEvent; +import net.neoforged.neoforge.fluids.capability.wrappers.CauldronWrapper; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Nullable; + +/** + * Fluid content information for cauldrons. + * + *
Empty, water and lava cauldrons are registered by default, + * and additional cauldrons must be registered with {@link RegisterCauldronFluidContentEvent}. + * Contents can be queried with {@link #getForBlock} and {@link #getForFluid}. + * + *
The {@code CauldronFluidContent} itself defines: + *
Registering cauldrons is done by calling {@link CauldronFluidContent#register} + * and allows all cauldrons registered in this way to interoperate with each other + * when accessed via the {@link Capabilities.FluidHandler#BLOCK} capability. + */ +public class RegisterCauldronFluidContentEvent extends Event implements IModBusEvent { + RegisterCauldronFluidContentEvent() {} + + /** + * Register a new cauldron, allowing it to be filled and emptied through the standard capability. + * In both cases, return the content of the cauldron, either the existing one, or the newly registered one. + * + *
If the block is not a subclass of {@link AbstractCauldronBlock},
+ * {@link BlockBehaviour#onPlace(BlockState, Level, BlockPos, BlockState, boolean)}
+ * and {@link BlockBehaviour#onRemove(BlockState, Level, BlockPos, BlockState, boolean)}
+ * must be overridden to invalidate capabilities when the block changes!
+ * See how NeoForge patches {@link AbstractCauldronBlock} for reference.
+ *
+ * @param block the block of the cauldron
+ * @param fluid the fluid stored in this cauldron
+ * @param totalAmount how much fluid can fit in the cauldron at maximum capacity, in {@linkplain FluidStack millibuckets}
+ * @param levelProperty the property used by the cauldron to store its levels, or {@code null} if the cauldron only has one level
+ */
+ public void register(Block block, Fluid fluid, int totalAmount, @Nullable IntegerProperty levelProperty) {
+ Objects.requireNonNull(block, "Block may not be null");
+ Objects.requireNonNull(fluid, "Fluid may not be null");
+
+ CauldronFluidContent.register(block, fluid, totalAmount, levelProperty);
+ }
+}
diff --git a/src/main/java/net/neoforged/neoforge/fluids/capability/wrappers/CauldronWrapper.java b/src/main/java/net/neoforged/neoforge/fluids/capability/wrappers/CauldronWrapper.java
new file mode 100644
index 0000000000..3d3194fb64
--- /dev/null
+++ b/src/main/java/net/neoforged/neoforge/fluids/capability/wrappers/CauldronWrapper.java
@@ -0,0 +1,139 @@
+package net.neoforged.neoforge.fluids.capability.wrappers;
+
+import com.google.common.math.IntMath;
+import java.util.Objects;
+import net.minecraft.core.BlockPos;
+import net.minecraft.world.level.Level;
+import net.minecraft.world.level.block.Blocks;
+import net.minecraft.world.level.block.state.BlockState;
+import net.minecraft.world.level.material.Fluids;
+import net.neoforged.neoforge.fluids.CauldronFluidContent;
+import net.neoforged.neoforge.fluids.FluidStack;
+import net.neoforged.neoforge.fluids.capability.IFluidHandler;
+import org.jetbrains.annotations.ApiStatus;
+
+@ApiStatus.Internal
+public class CauldronWrapper implements IFluidHandler {
+ private final Level level;
+ private final BlockPos pos;
+
+ public CauldronWrapper(Level level, BlockPos pos) {
+ this.level = level;
+ this.pos = pos.immutable();
+ }
+
+ @Override
+ public int getTanks() {
+ return 1;
+ }
+
+ private CauldronFluidContent getContent(BlockState state) {
+ CauldronFluidContent content = CauldronFluidContent.getForBlock(state.getBlock());
+ if (content == null) {
+ throw new IllegalStateException("Unexpected error: no cauldron at location " + pos);
+ }
+ return content;
+ }
+
+ @Override
+ public FluidStack getFluidInTank(int tank) {
+ BlockState state = level.getBlockState(pos);
+ CauldronFluidContent contents = getContent(state);
+ return new FluidStack(contents.fluid, contents.totalAmount * contents.currentLevel(state) / contents.maxLevel);
+ }
+
+ @Override
+ public int getTankCapacity(int tank) {
+ BlockState state = level.getBlockState(pos);
+ CauldronFluidContent contents = getContent(state);
+ return contents.totalAmount;
+ }
+
+ @Override
+ public boolean isFluidValid(int tank, FluidStack stack) {
+ return CauldronFluidContent.getForFluid(stack.getFluid()) != null;
+ }
+
+ // Called by fill and drain to update the block state.
+ private void updateLevel(CauldronFluidContent newContent, int level, FluidAction action) {
+ if (action.execute()) {
+ BlockState newState = newContent.block.defaultBlockState();
+
+ if (newContent.levelProperty != null) {
+ newState = newState.setValue(newContent.levelProperty, level);
+ }
+
+ this.level.setBlockAndUpdate(pos, newState);
+ }
+ }
+
+ @Override
+ public int fill(FluidStack resource, FluidAction action) {
+ if (resource.isEmpty()) {
+ return 0;
+ }
+
+ CauldronFluidContent insertContent = CauldronFluidContent.getForFluid(resource.getFluid());
+ if (insertContent == null) {
+ return 0;
+ }
+
+ BlockState state = level.getBlockState(pos);
+ CauldronFluidContent currentContent = getContent(state);
+ if (currentContent.fluid != Fluids.EMPTY && currentContent.fluid != resource.getFluid()) {
+ // Fluid mismatch
+ return 0;
+ }
+
+ // We can only insert increments based on the GCD between the number of levels and the total amount.
+ int d = IntMath.gcd(insertContent.maxLevel, insertContent.totalAmount);
+ int amountIncrements = insertContent.totalAmount / d;
+ int levelIncrements = insertContent.maxLevel / d;
+
+ int insertedIncrements = Math.min(resource.getAmount() / amountIncrements, (insertContent.maxLevel - currentContent.currentLevel(state)) / levelIncrements);
+ if (insertedIncrements > 0) {
+ updateLevel(insertContent, currentContent.currentLevel(state) + insertedIncrements * levelIncrements, action);
+ }
+
+ return insertedIncrements * amountIncrements;
+ }
+
+ @Override
+ public FluidStack drain(FluidStack resource, FluidAction action) {
+ FluidStack current = getFluidInTank(0);
+ if (current.isFluidEqual(resource) && Objects.equals(current.getTag(), resource.getTag())) {
+ return drain(resource.getAmount(), action);
+ } else {
+ return FluidStack.EMPTY;
+ }
+ }
+
+ @Override
+ public FluidStack drain(int maxDrain, FluidAction action) {
+ if (maxDrain <= 0) {
+ return FluidStack.EMPTY;
+ }
+
+ BlockState state = level.getBlockState(pos);
+ CauldronFluidContent content = getContent(state);
+
+ // We can only extract increments based on the GCD between the number of levels and the total amount.
+ int d = IntMath.gcd(content.maxLevel, content.totalAmount);
+ int amountIncrements = content.totalAmount / d;
+ int levelIncrements = content.maxLevel / d;
+
+ int extractedIncrements = Math.min(maxDrain / amountIncrements, content.currentLevel(state) / levelIncrements);
+ if (extractedIncrements > 0) {
+ int newLevel = content.currentLevel(state) - extractedIncrements * levelIncrements;
+ if (newLevel == 0) {
+ // Fully extract -> back to empty cauldron
+ level.setBlockAndUpdate(pos, Blocks.CAULDRON.defaultBlockState());
+ } else {
+ // Otherwise just decrease levels
+ updateLevel(content, content.currentLevel(state) - extractedIncrements * levelIncrements, action);
+ }
+ }
+
+ return new FluidStack(content.fluid, extractedIncrements * amountIncrements);
+ }
+}
diff --git a/src/main/java/net/neoforged/neoforge/internal/RegistrationEvents.java b/src/main/java/net/neoforged/neoforge/internal/RegistrationEvents.java
index 5d1a3eba36..6273fb11ac 100644
--- a/src/main/java/net/neoforged/neoforge/internal/RegistrationEvents.java
+++ b/src/main/java/net/neoforged/neoforge/internal/RegistrationEvents.java
@@ -7,10 +7,12 @@
import net.neoforged.neoforge.capabilities.CapabilityHooks;
import net.neoforged.neoforge.common.world.chunk.ForcedChunkManager;
+import net.neoforged.neoforge.fluids.CauldronFluidContent;
class RegistrationEvents {
public static void init() {
- CapabilityHooks.init();
+ CauldronFluidContent.init(); // must be before capability event
+ CapabilityHooks.init(); // must be after cauldron event
ForcedChunkManager.init();
}
}
diff --git a/tests/src/main/java/net/neoforged/neoforge/debug/capabilities/VanillaHandlersTests.java b/tests/src/main/java/net/neoforged/neoforge/debug/capabilities/VanillaHandlersTests.java
new file mode 100644
index 0000000000..3eca70616a
--- /dev/null
+++ b/tests/src/main/java/net/neoforged/neoforge/debug/capabilities/VanillaHandlersTests.java
@@ -0,0 +1,146 @@
+package net.neoforged.neoforge.debug.capabilities;
+
+import static net.neoforged.neoforge.fluids.capability.IFluidHandler.FluidAction.EXECUTE;
+import static net.neoforged.neoforge.fluids.capability.IFluidHandler.FluidAction.SIMULATE;
+
+import net.minecraft.core.BlockPos;
+import net.minecraft.core.Direction;
+import net.minecraft.gametest.framework.GameTest;
+import net.minecraft.world.level.block.Blocks;
+import net.minecraft.world.level.block.LayeredCauldronBlock;
+import net.minecraft.world.level.material.Fluids;
+import net.neoforged.neoforge.capabilities.BlockCapabilityCache;
+import net.neoforged.neoforge.capabilities.Capabilities;
+import net.neoforged.neoforge.eventtest.internal.TestsMod;
+import net.neoforged.neoforge.fluids.FluidStack;
+import net.neoforged.testframework.DynamicTest;
+import net.neoforged.testframework.annotation.ForEachTest;
+import net.neoforged.testframework.annotation.TestHolder;
+import org.apache.commons.lang3.mutable.MutableInt;
+
+@ForEachTest(groups = "capabilities.vanillahandlers")
+public class VanillaHandlersTests {
+ @TestHolder(description = {
+ "Tests that composter capabilities get invalidated correctly"
+ })
+ @GameTest(template = TestsMod.TEMPLATE_3x3)
+ public static void testComposterInvalidation(DynamicTest test) {
+ test.onGameTest(helper -> {
+ var composterPos = new BlockPos(1, 1, 1);
+
+ MutableInt invalidationCount = new MutableInt();
+ var capCache = BlockCapabilityCache.create(
+ Capabilities.ItemHandler.BLOCK,
+ helper.getLevel(),
+ helper.absolutePos(composterPos),
+ Direction.UP,
+ () -> true,
+ invalidationCount::increment);
+
+ if (capCache.getCapability() != null)
+ helper.fail("Expected no capability", composterPos);
+ if (capCache.getCapability() != null) // check again just in case
+ helper.fail("Expected no capability", composterPos);
+ if (invalidationCount.getValue() != 0)
+ helper.fail("Should not have been invalidated yet", composterPos);
+
+ // The cache should only be invalidated once until it is queried again
+ helper.setBlock(composterPos, Blocks.COMPOSTER.defaultBlockState());
+ if (invalidationCount.getValue() != 1)
+ helper.fail("Should have invalidated once");
+
+ helper.setBlock(composterPos, Blocks.AIR.defaultBlockState());
+ if (invalidationCount.getValue() != 1) // capability not re-queried, so no invalidation
+ helper.fail("Should have invalidated once");
+
+ helper.setBlock(composterPos, Blocks.COMPOSTER.defaultBlockState());
+ if (invalidationCount.getValue() != 1) // capability not re-queried, so no invalidation
+ helper.fail("Should have invalidated once");
+
+ // Should be ok to query now
+ if (capCache.getCapability() == null)
+ helper.fail("Expected capability", composterPos);
+ if (invalidationCount.getValue() != 1)
+ helper.fail("Should have invalidated once");
+
+ // Should be notified of disappearance if the composter is removed
+ helper.setBlock(composterPos, Blocks.AIR.defaultBlockState());
+
+ if (invalidationCount.getValue() != 2)
+ helper.fail("Should have invalidated a second time");
+ if (capCache.getCapability() != null)
+ helper.fail("Expected no capability", composterPos);
+
+ helper.succeed();
+ });
+ }
+
+ @TestHolder(description = "Test cauldron interactions via the fluid handler capability")
+ @GameTest(template = TestsMod.TEMPLATE_3x3)
+ public static void testCauldronCapability(DynamicTest test) {
+ test.onGameTest(helper -> {
+ var cauldronPos = new BlockPos(1, 1, 1);
+
+ MutableInt invalidationCount = new MutableInt();
+ var capCache = BlockCapabilityCache.create(
+ Capabilities.FluidHandler.BLOCK,
+ helper.getLevel(),
+ helper.absolutePos(cauldronPos),
+ Direction.UP,
+ () -> true,
+ invalidationCount::increment);
+
+ // Capability should be absent
+ helper.assertTrue(capCache.getCapability() == null, "Expected no capability");
+
+ // Should invalidate once when setting the block
+ helper.setBlock(cauldronPos, Blocks.CAULDRON);
+ var wrapper = capCache.getCapability();
+ helper.assertTrue(wrapper != null, "Expected fluid handler");
+ helper.assertTrue(invalidationCount.intValue() == 1, "Expected 1 invalidation only");
+
+ helper.assertTrue(wrapper.getTanks() == 1, "Got %d tanks".formatted(wrapper.getTanks()));
+
+ // Simulate filling with water
+ var fillResult = wrapper.fill(new FluidStack(Fluids.WATER, 2000), SIMULATE);
+ helper.assertTrue(fillResult == 1000, "Filled " + fillResult);
+ helper.assertBlockPresent(Blocks.CAULDRON, cauldronPos);
+ // Can't fill with less than 1000 though...
+ helper.assertTrue(wrapper.fill(new FluidStack(Fluids.WATER, 999), SIMULATE) == 0, "Expected 0 fill result");
+
+ // Action!
+ fillResult = wrapper.fill(new FluidStack(Fluids.WATER, 2000), EXECUTE);
+ helper.assertTrue(fillResult == 1000, "Filled " + fillResult);
+ helper.assertBlockState(cauldronPos, state -> state.is(Blocks.WATER_CAULDRON) && state.getValue(LayeredCauldronBlock.LEVEL) == 3, () -> "Expected level 3 cauldron");
+
+ helper.assertTrue(wrapper.getFluidInTank(0).equals(new FluidStack(Fluids.WATER, 1000)), "Expected 1000 water");
+
+ // Try to empty as well
+ helper.assertTrue(wrapper.drain(new FluidStack(Fluids.LAVA, 1000), EXECUTE).isEmpty(), "Cannot drain lava");
+ helper.assertTrue(wrapper.drain(new FluidStack(Fluids.WATER, 999), EXECUTE).isEmpty(), "Cannot drain less than 1000 water");
+ helper.assertTrue(wrapper.drain(new FluidStack(Fluids.WATER, 1000), EXECUTE).equals(new FluidStack(Fluids.WATER, 1000)), "Expected drain of 1000 water");
+
+ helper.assertBlockPresent(Blocks.CAULDRON, cauldronPos);
+
+ // Try lava cauldron
+ helper.setBlock(cauldronPos, Blocks.LAVA_CAULDRON);
+ helper.assertTrue(wrapper.getFluidInTank(0).equals(new FluidStack(Fluids.LAVA, 1000)), "Expected 1000 lava");
+ helper.assertTrue(wrapper.drain(1000, EXECUTE).equals(new FluidStack(Fluids.LAVA, 1000)), "Expected drain of 1000 lava");
+ helper.assertBlockPresent(Blocks.CAULDRON, cauldronPos);
+
+ // Try partial water filling
+ helper.setBlock(cauldronPos, Blocks.WATER_CAULDRON.defaultBlockState().setValue(LayeredCauldronBlock.LEVEL, 2));
+ helper.assertTrue(wrapper.getFluidInTank(0).equals(new FluidStack(Fluids.WATER, 666)), "Expected 666 water");
+ helper.assertTrue(wrapper.drain(1000, EXECUTE).isEmpty(), "Expected no water drain from partial cauldron");
+ helper.assertTrue(wrapper.fill(new FluidStack(Fluids.WATER, 1000), EXECUTE) == 0, "Expected no water fill to partial cauldron");
+
+ // None of this should have invalidated the capability
+ helper.assertTrue(invalidationCount.intValue() == 1, "Expected 1 invalidation only after the whole test");
+ // But if we change the block to a non-cauldron, it should invalidate
+ helper.destroyBlock(cauldronPos);
+ helper.assertTrue(invalidationCount.intValue() == 2, "Expected a second invalidation after cauldron destruction");
+
+ helper.succeed();
+ });
+ }
+}
diff --git a/tests/src/main/java/net/neoforged/neoforge/oldtest/capabilities/CapabilitiesTest.java b/tests/src/main/java/net/neoforged/neoforge/oldtest/capabilities/CapabilitiesTest.java
deleted file mode 100644
index 4f50b5c720..0000000000
--- a/tests/src/main/java/net/neoforged/neoforge/oldtest/capabilities/CapabilitiesTest.java
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * Copyright (c) NeoForged and contributors
- * SPDX-License-Identifier: LGPL-2.1-only
- */
-
-package net.neoforged.neoforge.oldtest.capabilities;
-
-import net.neoforged.bus.api.IEventBus;
-import net.neoforged.fml.common.Mod;
-import net.neoforged.neoforge.event.RegisterGameTestsEvent;
-
-@Mod(CapabilitiesTest.MODID)
-public class CapabilitiesTest {
- public static final String MODID = "capabilities_test";
-
- public CapabilitiesTest(IEventBus modBus) {
- modBus.addListener(CapabilitiesTest::registerGameTests);
- }
-
- private static void registerGameTests(RegisterGameTestsEvent event) {
- event.register(VanillaItemHandlerTests.class);
- }
-}
diff --git a/tests/src/main/java/net/neoforged/neoforge/oldtest/capabilities/VanillaItemHandlerTests.java b/tests/src/main/java/net/neoforged/neoforge/oldtest/capabilities/VanillaItemHandlerTests.java
deleted file mode 100644
index 7783583d36..0000000000
--- a/tests/src/main/java/net/neoforged/neoforge/oldtest/capabilities/VanillaItemHandlerTests.java
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * Copyright (c) NeoForged and contributors
- * SPDX-License-Identifier: LGPL-2.1-only
- */
-
-package net.neoforged.neoforge.oldtest.capabilities;
-
-import net.minecraft.core.BlockPos;
-import net.minecraft.core.Direction;
-import net.minecraft.gametest.framework.GameTest;
-import net.minecraft.gametest.framework.GameTestHelper;
-import net.minecraft.world.level.block.Blocks;
-import net.neoforged.neoforge.capabilities.BlockCapabilityCache;
-import net.neoforged.neoforge.capabilities.Capabilities;
-import net.neoforged.neoforge.gametest.PrefixGameTestTemplate;
-import org.apache.commons.lang3.mutable.MutableInt;
-
-@PrefixGameTestTemplate(false)
-public class VanillaItemHandlerTests {
- @GameTest(templateNamespace = CapabilitiesTest.MODID, template = "empty3x3x3")
- public static void testComposterInvalidation(GameTestHelper helper) {
- var composterPos = new BlockPos(1, 1, 1);
-
- MutableInt invalidationCount = new MutableInt();
- var capCache = BlockCapabilityCache.create(
- Capabilities.ItemHandler.BLOCK,
- helper.getLevel(),
- helper.absolutePos(composterPos),
- Direction.UP,
- () -> true,
- invalidationCount::increment);
-
- if (capCache.getCapability() != null)
- helper.fail("Expected no capability", composterPos);
- if (capCache.getCapability() != null) // check again just in case
- helper.fail("Expected no capability", composterPos);
- if (invalidationCount.getValue() != 0)
- helper.fail("Should not have been invalidated yet", composterPos);
-
- // The cache should only be invalidated once until it is queried again
- helper.setBlock(composterPos, Blocks.COMPOSTER.defaultBlockState());
- if (invalidationCount.getValue() != 1)
- helper.fail("Should have invalidated once");
-
- helper.setBlock(composterPos, Blocks.AIR.defaultBlockState());
- if (invalidationCount.getValue() != 1) // capability not re-queried, so no invalidation
- helper.fail("Should have invalidated once");
-
- helper.setBlock(composterPos, Blocks.COMPOSTER.defaultBlockState());
- if (invalidationCount.getValue() != 1) // capability not re-queried, so no invalidation
- helper.fail("Should have invalidated once");
-
- // Should be ok to query now
- if (capCache.getCapability() == null)
- helper.fail("Expected capability", composterPos);
- if (invalidationCount.getValue() != 1)
- helper.fail("Should have invalidated once");
-
- // Should be notified of disappearance if the composter is removed
- helper.setBlock(composterPos, Blocks.AIR.defaultBlockState());
-
- if (invalidationCount.getValue() != 2)
- helper.fail("Should have invalidated a second time");
- if (capCache.getCapability() != null)
- helper.fail("Expected no capability", composterPos);
-
- helper.succeed();
- }
-}
diff --git a/tests/src/main/resources/META-INF/mods.toml b/tests/src/main/resources/META-INF/mods.toml
index 081bdff82f..6451ae9a63 100644
--- a/tests/src/main/resources/META-INF/mods.toml
+++ b/tests/src/main/resources/META-INF/mods.toml
@@ -156,8 +156,6 @@ modId="permissiontest"
[[mods]]
modId="part_entity_test"
[[mods]]
-modId="capabilities_test"
-[[mods]]
modId="custom_mob_bucket_test"
[[mods]]
modId="custom_armor_model_test"
From aed6d0b8381b21cbc1bcf564493474a2fb655aab Mon Sep 17 00:00:00 2001
From: Technici4n <13494793+Technici4n@users.noreply.github.com>
Date: Mon, 11 Dec 2023 19:43:47 +0100
Subject: [PATCH 2/6] Licenses!
---
.../neoforged/neoforge/fluids/CauldronFluidContent.java | 5 +++++
.../neoforge/fluids/RegisterCauldronFluidContentEvent.java | 5 +++++
.../fluids/capability/wrappers/CauldronWrapper.java | 5 +++++
.../neoforge/debug/capabilities/VanillaHandlersTests.java | 7 +++++--
4 files changed, 20 insertions(+), 2 deletions(-)
diff --git a/src/main/java/net/neoforged/neoforge/fluids/CauldronFluidContent.java b/src/main/java/net/neoforged/neoforge/fluids/CauldronFluidContent.java
index f9552bce04..a332ba043b 100644
--- a/src/main/java/net/neoforged/neoforge/fluids/CauldronFluidContent.java
+++ b/src/main/java/net/neoforged/neoforge/fluids/CauldronFluidContent.java
@@ -1,3 +1,8 @@
+/*
+ * Copyright (c) NeoForged and contributors
+ * SPDX-License-Identifier: LGPL-2.1-only
+ */
+
package net.neoforged.neoforge.fluids;
import java.util.Collection;
diff --git a/src/main/java/net/neoforged/neoforge/fluids/RegisterCauldronFluidContentEvent.java b/src/main/java/net/neoforged/neoforge/fluids/RegisterCauldronFluidContentEvent.java
index 3c124ad179..57c5bb3845 100644
--- a/src/main/java/net/neoforged/neoforge/fluids/RegisterCauldronFluidContentEvent.java
+++ b/src/main/java/net/neoforged/neoforge/fluids/RegisterCauldronFluidContentEvent.java
@@ -1,3 +1,8 @@
+/*
+ * Copyright (c) NeoForged and contributors
+ * SPDX-License-Identifier: LGPL-2.1-only
+ */
+
package net.neoforged.neoforge.fluids;
import java.util.Objects;
diff --git a/src/main/java/net/neoforged/neoforge/fluids/capability/wrappers/CauldronWrapper.java b/src/main/java/net/neoforged/neoforge/fluids/capability/wrappers/CauldronWrapper.java
index 3d3194fb64..0a60f9cdd8 100644
--- a/src/main/java/net/neoforged/neoforge/fluids/capability/wrappers/CauldronWrapper.java
+++ b/src/main/java/net/neoforged/neoforge/fluids/capability/wrappers/CauldronWrapper.java
@@ -1,3 +1,8 @@
+/*
+ * Copyright (c) NeoForged and contributors
+ * SPDX-License-Identifier: LGPL-2.1-only
+ */
+
package net.neoforged.neoforge.fluids.capability.wrappers;
import com.google.common.math.IntMath;
diff --git a/tests/src/main/java/net/neoforged/neoforge/debug/capabilities/VanillaHandlersTests.java b/tests/src/main/java/net/neoforged/neoforge/debug/capabilities/VanillaHandlersTests.java
index 0e1f758d97..9c09ae9bc1 100644
--- a/tests/src/main/java/net/neoforged/neoforge/debug/capabilities/VanillaHandlersTests.java
+++ b/tests/src/main/java/net/neoforged/neoforge/debug/capabilities/VanillaHandlersTests.java
@@ -1,3 +1,8 @@
+/*
+ * Copyright (c) NeoForged and contributors
+ * SPDX-License-Identifier: LGPL-2.1-only
+ */
+
package net.neoforged.neoforge.debug.capabilities;
import static net.neoforged.neoforge.fluids.capability.IFluidHandler.FluidAction.EXECUTE;
@@ -11,9 +16,7 @@
import net.minecraft.world.level.material.Fluids;
import net.neoforged.neoforge.capabilities.BlockCapabilityCache;
import net.neoforged.neoforge.capabilities.Capabilities;
-import net.neoforged.neoforge.eventtest.internal.TestsMod;
import net.neoforged.neoforge.fluids.FluidStack;
-import net.neoforged.testframework.DynamicTest;
import net.neoforged.testframework.annotation.ForEachTest;
import net.neoforged.testframework.annotation.TestHolder;
import net.neoforged.testframework.gametest.EmptyTemplate;
From 79836c35a9fb9315e6e5dfa820bfc6ff02948b06 Mon Sep 17 00:00:00 2001
From: Technici4n <13494793+Technici4n@users.noreply.github.com>
Date: Mon, 11 Dec 2023 20:49:31 +0100
Subject: [PATCH 3/6] Fixes
---
.../block/AbstractCauldronBlock.java.patch | 3 +-
.../capabilities/BlockCapability.java | 3 ++
.../neoforge/fluids/CauldronFluidContent.java | 6 ++--
.../capability/wrappers/CauldronWrapper.java | 34 +++++++++++++------
4 files changed, 31 insertions(+), 15 deletions(-)
diff --git a/patches/net/minecraft/world/level/block/AbstractCauldronBlock.java.patch b/patches/net/minecraft/world/level/block/AbstractCauldronBlock.java.patch
index 2cc9a096a6..3ca300683b 100644
--- a/patches/net/minecraft/world/level/block/AbstractCauldronBlock.java.patch
+++ b/patches/net/minecraft/world/level/block/AbstractCauldronBlock.java.patch
@@ -1,12 +1,13 @@
--- a/net/minecraft/world/level/block/AbstractCauldronBlock.java
+++ b/net/minecraft/world/level/block/AbstractCauldronBlock.java
-@@ -102,4 +_,21 @@
+@@ -102,4 +_,22 @@
protected void receiveStalactiteDrip(BlockState p_151975_, Level p_151976_, BlockPos p_151977_, Fluid p_151978_) {
}
+
+ @Override
+ public void onPlace(BlockState p_51978_, Level p_51979_, BlockPos p_51980_, BlockState p_51981_, boolean p_51982_) {
++ super.onPlace(p_51978_, p_51979_, p_51980_, p_51981_, p_51982_);
+ // Neo: Invalidate cauldron capabilities when a cauldron is added
+ if (net.neoforged.neoforge.fluids.CauldronFluidContent.getForBlock(p_51981_.getBlock()) == null) {
+ p_51979_.invalidateCapabilities(p_51980_);
diff --git a/src/main/java/net/neoforged/neoforge/capabilities/BlockCapability.java b/src/main/java/net/neoforged/neoforge/capabilities/BlockCapability.java
index 564283229f..52b7aa223c 100644
--- a/src/main/java/net/neoforged/neoforge/capabilities/BlockCapability.java
+++ b/src/main/java/net/neoforged/neoforge/capabilities/BlockCapability.java
@@ -137,6 +137,9 @@ private BlockCapability(ResourceLocation name, Class