From 219d725a4e56e44a660bbadff89f5b35cd94d326 Mon Sep 17 00:00:00 2001 From: alpha Date: Sun, 19 May 2024 20:35:40 -0500 Subject: [PATCH] Add FluidInteractionRegistry --- modules/fluids/build.gradle | 1 + .../fluids/FluidInteractionRegistry.java | 223 ++++++++++++++++++ .../fluids/extensions/FluidExtension.java | 2 + .../extensions/FluidStateExtension.java | 3 + .../porting_lib/fluids/mixin/FluidMixin.java | 12 +- .../fluids/mixin/LiquidBlockMixin.java | 29 +++ .../MergingFluidAttributeFluidType.java | 20 +- .../resources/porting_lib_fluids.mixins.json | 3 +- .../testmod/PortingLibFluidsTestmod.java | 16 ++ .../src/testmod/resources/fabric.mod.json | 17 ++ .../assets/porting_lib/models/item/ring.obj | 4 +- 11 files changed, 311 insertions(+), 19 deletions(-) create mode 100644 modules/fluids/src/main/java/io/github/fabricators_of_create/porting_lib/fluids/FluidInteractionRegistry.java create mode 100644 modules/fluids/src/main/java/io/github/fabricators_of_create/porting_lib/fluids/mixin/LiquidBlockMixin.java create mode 100644 modules/fluids/src/testmod/java/io/github/fabricators_of_create/porting_lib/fluids/testmod/PortingLibFluidsTestmod.java create mode 100644 modules/fluids/src/testmod/resources/fabric.mod.json diff --git a/modules/fluids/build.gradle b/modules/fluids/build.gradle index e69de29bb..52bbd2ac5 100644 --- a/modules/fluids/build.gradle +++ b/modules/fluids/build.gradle @@ -0,0 +1 @@ +portingLib.enableTestMod() diff --git a/modules/fluids/src/main/java/io/github/fabricators_of_create/porting_lib/fluids/FluidInteractionRegistry.java b/modules/fluids/src/main/java/io/github/fabricators_of_create/porting_lib/fluids/FluidInteractionRegistry.java new file mode 100644 index 000000000..0fb7a3744 --- /dev/null +++ b/modules/fluids/src/main/java/io/github/fabricators_of_create/porting_lib/fluids/FluidInteractionRegistry.java @@ -0,0 +1,223 @@ +package io.github.fabricators_of_create.porting_lib.fluids; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.Function; + +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.LevelEvent; +import net.minecraft.world.level.block.LiquidBlock; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.material.FluidState; + +/** + * A registry which defines the interactions a source fluid can have with its + * surroundings. Each possible flow direction is checked for all interactions with + * the source. + * + *

Fluid interactions mimic the behavior of {@code LiquidBlock#shouldSpreadLiquid}. + * As such, all directions, besides {@link Direction#DOWN} is tested and then replaced. + * Any fluids which cause a change in the down interaction must be handled in + * {@code FlowingFluid#spreadTo} and not by this interaction manager. + */ +public final class FluidInteractionRegistry { + private static final Map> INTERACTIONS = new HashMap<>(); + + /** + * Adds an interaction between a source and its surroundings. + * + * @param source the source of the interaction, this will be replaced if the interaction occurs + * @param interaction the interaction data to check and perform + */ + public static synchronized void addInteraction(FluidType source, InteractionInformation interaction) { + INTERACTIONS.computeIfAbsent(source, s -> new ArrayList<>()).add(interaction); + } + + /** + * Performs all potential fluid interactions at a given position. + * + *

Note: Only the first interaction check that succeeds will occur. + * + * @param level the level the interactions take place in + * @param pos the position of the source fluid + * @return {@code true} if an interaction took place, {@code false} otherwise + */ + public static boolean canInteract(Level level, BlockPos pos) { + return canInteract(level, pos, true); + } + + /** + * Performs all potential fluid interactions at a given position. + * + *

Note: Only the first interaction check that succeeds will occur. + * + * @param level the level the interactions take place in + * @param pos the position of the source fluid + * @return {@code true} if an interaction took place, {@code false} otherwise + */ + public static boolean canInteract(Level level, BlockPos pos, boolean overrideVanilla) { + FluidState state = level.getFluidState(pos); + for (Direction direction : LiquidBlock.POSSIBLE_FLOW_DIRECTIONS) { + BlockPos relativePos = pos.relative(direction.getOpposite()); + List interactions = INTERACTIONS.getOrDefault(state.getFluidType(), Collections.emptyList()); + for (InteractionInformation interaction : interactions) { + if (interaction.isVanilla() && !overrideVanilla) + continue; + if (interaction.predicate().test(level, pos, relativePos, state)) { + interaction.interaction().interact(level, pos, relativePos, state); + return true; + } + } + } + + return false; + } + + static { + // Lava + Water = Obsidian (Source Lava) / Cobblestone (Flowing Lava) + addInteraction(PortingLibFluids.LAVA_TYPE, new InteractionInformation( + PortingLibFluids.WATER_TYPE, + fluidState -> fluidState.isSource() ? Blocks.OBSIDIAN.defaultBlockState() : Blocks.COBBLESTONE.defaultBlockState(), + true + )); + + // Lava + Soul Soil (Below) + Blue Ice = Basalt + addInteraction(PortingLibFluids.LAVA_TYPE, new InteractionInformation( + (level, currentPos, relativePos, currentState) -> level.getBlockState(currentPos.below()).is(Blocks.SOUL_SOIL) && level.getBlockState(relativePos).is(Blocks.BLUE_ICE), + Blocks.BASALT.defaultBlockState(), + true + )); + } + + /** + * Holds the interaction data for a given source type on when to succeed + * and what to perform. + * + * @param predicate a test to see whether an interaction can occur + * @param interaction the interaction to perform + */ + public record InteractionInformation(HasFluidInteraction predicate, FluidInteraction interaction, + boolean isVanilla) { + + public InteractionInformation(HasFluidInteraction predicate, FluidInteraction interaction) { + this(predicate, interaction, false); + } + + /** + * Constructor which checks the surroundings fluids for a specific type + * and then transforms the source state into a block. + * + * @param type the type of the fluid that must be surrounding the source + * @param state the state of the block replacing the source + */ + public InteractionInformation(FluidType type, BlockState state) { + this(type, fluidState -> state); + } + + /** + * Constructor which transforms the source state into a block. + * + * @param predicate a test to see whether an interaction can occur + * @param state the state of the block replacing the source + */ + public InteractionInformation(HasFluidInteraction predicate, BlockState state) { + this(predicate, state, false); + } + + /** + * Constructor which transforms the source state into a block. + * + * @param predicate a test to see whether an interaction can occur + * @param state the state of the block replacing the source + */ + public InteractionInformation(HasFluidInteraction predicate, BlockState state, boolean isVanilla) { + this(predicate, fluidState -> state, isVanilla); + } + + /** + * Constructor which checks the surroundings fluids for a specific type + * and then transforms the source state into a block. + * + * @param type the type of the fluid that must be surrounding the source + * @param getState a function to transform the source fluid into a block state + */ + public InteractionInformation(FluidType type, Function getState) { + this((level, currentPos, relativePos, currentState) -> level.getFluidState(relativePos).getFluidType() == type, getState); + } + + /** + * Constructor which checks the surroundings fluids for a specific type + * and then transforms the source state into a block. + * + * @param type the type of the fluid that must be surrounding the source + * @param getState a function to transform the source fluid into a block state + */ + public InteractionInformation(FluidType type, Function getState, boolean isVanilla) { + this((level, currentPos, relativePos, currentState) -> level.getFluidState(relativePos).getFluidType() == type, getState, isVanilla); + } + + /** + * Constructor which transforms the source state into a block. + * + * @param predicate a test to see whether an interaction can occur + * @param getState a function to transform the source fluid into a block state + */ + public InteractionInformation(HasFluidInteraction predicate, Function getState) { + this(predicate, getState, false); + } + + /** + * Constructor which transforms the source state into a block. + * + * @param predicate a test to see whether an interaction can occur + * @param getState a function to transform the source fluid into a block state + */ + public InteractionInformation(HasFluidInteraction predicate, Function getState, boolean isVanilla) { + this(predicate, (level, currentPos, relativePos, currentState) -> + { + level.setBlockAndUpdate(currentPos, getState.apply(currentState)/*ForgeEventFactory.fireFluidPlaceBlockEvent(level, currentPos, currentPos, getState.apply(currentState))*/); + level.levelEvent(LevelEvent.LAVA_FIZZ, currentPos, 0); + }, isVanilla); + } + } + + /** + * An interface which tests whether a source fluid can interact with its + * surroundings. + */ + @FunctionalInterface + public interface HasFluidInteraction { + /** + * Returns whether the interaction can occur. + * + * @param level the level the interaction takes place in + * @param currentPos the position of the source + * @param relativePos a position surrounding the source + * @param currentState the state of the fluid surrounding the source + * @return {@code true} if an interaction can occur, {@code false} otherwise + */ + boolean test(Level level, BlockPos currentPos, BlockPos relativePos, FluidState currentState); + } + + /** + * An interface which performs an interaction for a source. + */ + @FunctionalInterface + public interface FluidInteraction { + /** + * Performs the interaction between the source and the surrounding data. + * + * @param level the level the interaction takes place in + * @param currentPos the position of the source + * @param relativePos a position surrounding the source + * @param currentState the state of the fluid surrounding the source + */ + void interact(Level level, BlockPos currentPos, BlockPos relativePos, FluidState currentState); + } +} diff --git a/modules/fluids/src/main/java/io/github/fabricators_of_create/porting_lib/fluids/extensions/FluidExtension.java b/modules/fluids/src/main/java/io/github/fabricators_of_create/porting_lib/fluids/extensions/FluidExtension.java index e57ac60a7..9dfa20dae 100644 --- a/modules/fluids/src/main/java/io/github/fabricators_of_create/porting_lib/fluids/extensions/FluidExtension.java +++ b/modules/fluids/src/main/java/io/github/fabricators_of_create/porting_lib/fluids/extensions/FluidExtension.java @@ -1,8 +1,10 @@ package io.github.fabricators_of_create.porting_lib.fluids.extensions; import io.github.fabricators_of_create.porting_lib.fluids.FluidType; +import org.jetbrains.annotations.Nullable; public interface FluidExtension { + @Nullable default FluidType getFluidType() { throw new RuntimeException(); } diff --git a/modules/fluids/src/main/java/io/github/fabricators_of_create/porting_lib/fluids/extensions/FluidStateExtension.java b/modules/fluids/src/main/java/io/github/fabricators_of_create/porting_lib/fluids/extensions/FluidStateExtension.java index b8f323fcd..0214e8baf 100644 --- a/modules/fluids/src/main/java/io/github/fabricators_of_create/porting_lib/fluids/extensions/FluidStateExtension.java +++ b/modules/fluids/src/main/java/io/github/fabricators_of_create/porting_lib/fluids/extensions/FluidStateExtension.java @@ -3,12 +3,15 @@ import io.github.fabricators_of_create.porting_lib.fluids.FluidType; import net.minecraft.world.level.material.FluidState; +import org.jetbrains.annotations.Nullable; + public interface FluidStateExtension { /** * Returns the type of this fluid. * * @return the type of this fluid */ + @Nullable default FluidType getFluidType() { return ((FluidState) this).getType().getFluidType(); } diff --git a/modules/fluids/src/main/java/io/github/fabricators_of_create/porting_lib/fluids/mixin/FluidMixin.java b/modules/fluids/src/main/java/io/github/fabricators_of_create/porting_lib/fluids/mixin/FluidMixin.java index c5e75d1c1..6d84a00cd 100644 --- a/modules/fluids/src/main/java/io/github/fabricators_of_create/porting_lib/fluids/mixin/FluidMixin.java +++ b/modules/fluids/src/main/java/io/github/fabricators_of_create/porting_lib/fluids/mixin/FluidMixin.java @@ -19,18 +19,14 @@ public class FluidMixin implements FluidExtension { @Override public FluidType getFluidType() { var fluid = (Fluid) (Object) this; - var handler = FluidVariantAttributes.getHandler(fluid); - if (portingLibFluidType == null && handler == null) { + if (portingLibFluidType == null) { if (fluid == Fluids.EMPTY) - portingLibFluidType = new MergingFluidAttributeFluidType(PortingLibFluids.EMPTY_TYPE, FluidVariant.of(fluid), handler); + portingLibFluidType = PortingLibFluids.EMPTY_TYPE; if (fluid == Fluids.WATER || fluid == Fluids.FLOWING_WATER) - portingLibFluidType = new MergingFluidAttributeFluidType(PortingLibFluids.WATER_TYPE, FluidVariant.of(fluid), handler); + portingLibFluidType = PortingLibFluids.WATER_TYPE;//new MergingFluidAttributeFluidType(PortingLibFluids.WATER_TYPE, FluidVariant.of(fluid), FluidVariantAttributes.getHandler(Fluids.WATER)); if (fluid == Fluids.LAVA || fluid == Fluids.FLOWING_LAVA) - return new MergingFluidAttributeFluidType(PortingLibFluids.LAVA_TYPE, FluidVariant.of(fluid), handler); - portingLibFluidType = new FluidAttributeFluidType(FluidVariant.of(fluid), handler); + portingLibFluidType = PortingLibFluids.LAVA_TYPE;//new MergingFluidAttributeFluidType(PortingLibFluids.LAVA_TYPE, FluidVariant.of(fluid), FluidVariantAttributes.getHandler(Fluids.LAVA)); } - if (portingLibFluidType == null) - throw new RuntimeException("Mod fluids must override getFluidType."); return this.portingLibFluidType; } } diff --git a/modules/fluids/src/main/java/io/github/fabricators_of_create/porting_lib/fluids/mixin/LiquidBlockMixin.java b/modules/fluids/src/main/java/io/github/fabricators_of_create/porting_lib/fluids/mixin/LiquidBlockMixin.java new file mode 100644 index 000000000..38856d547 --- /dev/null +++ b/modules/fluids/src/main/java/io/github/fabricators_of_create/porting_lib/fluids/mixin/LiquidBlockMixin.java @@ -0,0 +1,29 @@ +package io.github.fabricators_of_create.porting_lib.fluids.mixin; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; + +import com.llamalad7.mixinextras.injector.ModifyExpressionValue; + +import io.github.fabricators_of_create.porting_lib.fluids.FluidInteractionRegistry; +import net.minecraft.core.BlockPos; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.LiquidBlock; +import net.minecraft.world.level.block.state.BlockState; + +@Mixin(LiquidBlock.class) +public class LiquidBlockMixin { + @ModifyExpressionValue(method = "onPlace", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/block/LiquidBlock;shouldSpreadLiquid(Lnet/minecraft/world/level/Level;Lnet/minecraft/core/BlockPos;Lnet/minecraft/world/level/block/state/BlockState;)Z")) + private boolean canPlaceCustom(boolean original, BlockState blockState, Level level, BlockPos blockPos) { + if (!FluidInteractionRegistry.canInteract(level, blockPos, false)) + return true; + return original; + } + + @ModifyExpressionValue(method = "neighborChanged", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/block/LiquidBlock;shouldSpreadLiquid(Lnet/minecraft/world/level/Level;Lnet/minecraft/core/BlockPos;Lnet/minecraft/world/level/block/state/BlockState;)Z")) + private boolean neighborChangedCustom(boolean original, BlockState blockState, Level level, BlockPos blockPos) { + if (!FluidInteractionRegistry.canInteract(level, blockPos, false)) + return true; + return original; + } +} diff --git a/modules/fluids/src/main/java/io/github/fabricators_of_create/porting_lib/fluids/wrapper/MergingFluidAttributeFluidType.java b/modules/fluids/src/main/java/io/github/fabricators_of_create/porting_lib/fluids/wrapper/MergingFluidAttributeFluidType.java index 2b94e41d5..e1ba59de5 100644 --- a/modules/fluids/src/main/java/io/github/fabricators_of_create/porting_lib/fluids/wrapper/MergingFluidAttributeFluidType.java +++ b/modules/fluids/src/main/java/io/github/fabricators_of_create/porting_lib/fluids/wrapper/MergingFluidAttributeFluidType.java @@ -33,19 +33,23 @@ public class MergingFluidAttributeFluidType extends FluidType { private final FluidVariantAttributeHandler handler; private final FluidType superType; public MergingFluidAttributeFluidType(FluidType superType, FluidVariant variant, FluidVariantAttributeHandler handler) { - super(Properties.create() - .viscosity(handler.getViscosity(variant, null)) - .temperature(handler.getTemperature(variant)) - .lightLevel(handler.getLuminance(variant)) - .sound(SoundActions.BUCKET_FILL, handler.getFillSound(variant).get()) - .sound(SoundActions.BUCKET_EMPTY, handler.getEmptySound(variant).get()) - .density(handler.isLighterThanAir(variant) ? -1 : 1) - ); + super(createProperties(variant, handler)); this.variant = variant; this.handler = handler; this.superType = superType; } + public static Properties createProperties(FluidVariant variant, FluidVariantAttributeHandler handler) { + Properties properties = Properties.create() + .viscosity(handler.getViscosity(variant, null)) + .temperature(handler.getTemperature(variant)) + .lightLevel(handler.getLuminance(variant)) + .density(handler.isLighterThanAir(variant) ? -1 : 1); + handler.getFillSound(variant).ifPresent(soundEvent -> properties.sound(SoundActions.BUCKET_FILL, soundEvent)); + handler.getEmptySound(variant).ifPresent(soundEvent -> properties.sound(SoundActions.BUCKET_EMPTY, soundEvent)); + return properties; + } + @Override public Component getDescription() { return handler.getName(variant); diff --git a/modules/fluids/src/main/resources/porting_lib_fluids.mixins.json b/modules/fluids/src/main/resources/porting_lib_fluids.mixins.json index 452b6f547..d819b7d33 100644 --- a/modules/fluids/src/main/resources/porting_lib_fluids.mixins.json +++ b/modules/fluids/src/main/resources/porting_lib_fluids.mixins.json @@ -8,6 +8,7 @@ }, "mixins": [ "FluidMixin", - "FluidStateMixin" + "FluidStateMixin", + "LiquidBlockMixin" ] } diff --git a/modules/fluids/src/testmod/java/io/github/fabricators_of_create/porting_lib/fluids/testmod/PortingLibFluidsTestmod.java b/modules/fluids/src/testmod/java/io/github/fabricators_of_create/porting_lib/fluids/testmod/PortingLibFluidsTestmod.java new file mode 100644 index 000000000..9f118c5dc --- /dev/null +++ b/modules/fluids/src/testmod/java/io/github/fabricators_of_create/porting_lib/fluids/testmod/PortingLibFluidsTestmod.java @@ -0,0 +1,16 @@ +package io.github.fabricators_of_create.porting_lib.fluids.testmod; + +import io.github.fabricators_of_create.porting_lib.fluids.FluidInteractionRegistry; +import io.github.fabricators_of_create.porting_lib.fluids.PortingLibFluids; +import net.fabricmc.api.ModInitializer; +import net.minecraft.world.level.block.Blocks; + +public class PortingLibFluidsTestmod implements ModInitializer { + @Override + public void onInitialize() { + FluidInteractionRegistry.addInteraction(PortingLibFluids.WATER_TYPE, new FluidInteractionRegistry.InteractionInformation( + (level, currentPos, relativePos, currentState) -> level.getBlockState(currentPos.below()).is(Blocks.BAMBOO_BLOCK) && level.getBlockState(relativePos).is(Blocks.MAGMA_BLOCK), + Blocks.EMERALD_BLOCK.defaultBlockState() + )); + } +} diff --git a/modules/fluids/src/testmod/resources/fabric.mod.json b/modules/fluids/src/testmod/resources/fabric.mod.json new file mode 100644 index 000000000..304360000 --- /dev/null +++ b/modules/fluids/src/testmod/resources/fabric.mod.json @@ -0,0 +1,17 @@ +{ + "schemaVersion": 1, + "id": "porting_lib_fluids_testmod", + "version": "${version}", + "name": "Porting Lib Fluids Testmod", + "description": "Tests the fluids module", + "authors": [ + "The Create Fabric Team" + ], + "license": "LGPL", + "environment": "*", + "entrypoints": { + "main": [ + "io.github.fabricators_of_create.porting_lib.fluids.testmod.PortingLibFluidsTestmod" + ] + } +} diff --git a/modules/obj_loader/src/testmod/resources/assets/porting_lib/models/item/ring.obj b/modules/obj_loader/src/testmod/resources/assets/porting_lib/models/item/ring.obj index b10d1e6c9..88946f708 100644 --- a/modules/obj_loader/src/testmod/resources/assets/porting_lib/models/item/ring.obj +++ b/modules/obj_loader/src/testmod/resources/assets/porting_lib/models/item/ring.obj @@ -1,7 +1,7 @@ # Made in Blockbench 4.7.4 mtllib ring.mtl -o torus +io torus v 0.03277173140761911 0.2502682088509125 0.49999999999999994 v 0.03856500978500702 0.21357799994557292 0.5883883476483184 v 0.04096466425952389 0.125 0.625 @@ -2690,4 +2690,4 @@ f 380/1517/380 381/1518/380 5/1519/380 4/1520/380 f 381/1521/381 382/1522/381 6/1523/381 5/1524/381 f 382/1525/382 383/1526/382 7/1527/382 6/1528/382 f 383/1529/383 384/1530/383 8/1531/383 7/1532/383 -f 384/1533/384 377/1534/384 1/1535/384 8/1536/384 \ No newline at end of file +f 384/1533/384 377/1534/384 1/1535/384 8/1536/384