diff --git a/patches/net/minecraft/world/level/block/BubbleColumnBlock.java.patch b/patches/net/minecraft/world/level/block/BubbleColumnBlock.java.patch new file mode 100644 index 0000000000..526c55e206 --- /dev/null +++ b/patches/net/minecraft/world/level/block/BubbleColumnBlock.java.patch @@ -0,0 +1,26 @@ +--- a/net/minecraft/world/level/block/BubbleColumnBlock.java ++++ b/net/minecraft/world/level/block/BubbleColumnBlock.java +@@ -121,10 +_,12 @@ + private static BlockState getColumnState(BlockState p_152718_) { + if (p_152718_.is(Blocks.BUBBLE_COLUMN)) { + return p_152718_; +- } else if (p_152718_.is(Blocks.SOUL_SAND)) { ++ } ++ net.neoforged.neoforge.common.enums.BubbleColumnDirection bubbleColumnDirection = p_152718_.getBubbleColumnDirection(); // Neo: PR #931 ++ if (bubbleColumnDirection == net.neoforged.neoforge.common.enums.BubbleColumnDirection.UPWARD) { + return Blocks.BUBBLE_COLUMN.defaultBlockState().setValue(DRAG_DOWN, Boolean.valueOf(false)); + } else { +- return p_152718_.is(Blocks.MAGMA_BLOCK) ++ return bubbleColumnDirection == net.neoforged.neoforge.common.enums.BubbleColumnDirection.DOWNWARD + ? Blocks.BUBBLE_COLUMN.defaultBlockState().setValue(DRAG_DOWN, Boolean.valueOf(true)) + : Blocks.WATER.defaultBlockState(); + } +@@ -190,7 +_,7 @@ + @Override + protected boolean canSurvive(BlockState p_50986_, LevelReader p_50987_, BlockPos p_50988_) { + BlockState blockstate = p_50987_.getBlockState(p_50988_.below()); +- return blockstate.is(Blocks.BUBBLE_COLUMN) || blockstate.is(Blocks.MAGMA_BLOCK) || blockstate.is(Blocks.SOUL_SAND); ++ return blockstate.is(Blocks.BUBBLE_COLUMN) || blockstate.getBubbleColumnDirection() != net.neoforged.neoforge.common.enums.BubbleColumnDirection.NONE; // Neo: PR #931 + } + + @Override diff --git a/src/main/java/net/neoforged/neoforge/common/enums/BubbleColumnDirection.java b/src/main/java/net/neoforged/neoforge/common/enums/BubbleColumnDirection.java new file mode 100644 index 0000000000..f4c3a3af5b --- /dev/null +++ b/src/main/java/net/neoforged/neoforge/common/enums/BubbleColumnDirection.java @@ -0,0 +1,12 @@ +/* + * Copyright (c) NeoForged and contributors + * SPDX-License-Identifier: LGPL-2.1-only + */ + +package net.neoforged.neoforge.common.enums; + +public enum BubbleColumnDirection { + UPWARD, + DOWNWARD, + NONE +} diff --git a/src/main/java/net/neoforged/neoforge/common/enums/package-info.java b/src/main/java/net/neoforged/neoforge/common/enums/package-info.java new file mode 100644 index 0000000000..4629e47c07 --- /dev/null +++ b/src/main/java/net/neoforged/neoforge/common/enums/package-info.java @@ -0,0 +1,13 @@ +/* + * Copyright (c) NeoForged and contributors + * SPDX-License-Identifier: LGPL-2.1-only + */ + +@FieldsAreNonnullByDefault +@MethodsReturnNonnullByDefault +@ParametersAreNonnullByDefault +package net.neoforged.neoforge.common.enums; + +import javax.annotation.ParametersAreNonnullByDefault; +import net.minecraft.FieldsAreNonnullByDefault; +import net.minecraft.MethodsReturnNonnullByDefault; diff --git a/src/main/java/net/neoforged/neoforge/common/extensions/IBlockExtension.java b/src/main/java/net/neoforged/neoforge/common/extensions/IBlockExtension.java index 3d9caf7aba..ad35b5f770 100644 --- a/src/main/java/net/neoforged/neoforge/common/extensions/IBlockExtension.java +++ b/src/main/java/net/neoforged/neoforge/common/extensions/IBlockExtension.java @@ -74,6 +74,7 @@ import net.neoforged.neoforge.common.IPlantable; import net.neoforged.neoforge.common.ToolAction; import net.neoforged.neoforge.common.ToolActions; +import net.neoforged.neoforge.common.enums.BubbleColumnDirection; import net.neoforged.neoforge.common.world.AuxiliaryLightManager; import net.neoforged.neoforge.event.EventHooks; import org.jetbrains.annotations.Nullable; @@ -976,4 +977,23 @@ default PushReaction getPistonPushReaction(BlockState state) { default boolean isEmpty(BlockState state) { return state.is(Blocks.AIR) || state.is(Blocks.CAVE_AIR) || state.is(Blocks.VOID_AIR); } + + /** + * Determines if this block can spawn Bubble Columns and if so, what direction the column flows. + *

+ * NOTE: The block itself will still need to call {@link net.minecraft.world.level.block.BubbleColumnBlock#updateColumn(LevelAccessor, BlockPos, BlockState)} in their tick method and schedule a block tick in the block's onPlace. + * Also, schedule a fluid tick in updateShape method if update direction is up. Both are needed in order to get the Bubble Columns to function properly. See {@link net.minecraft.world.level.block.SoulSandBlock} and {@link net.minecraft.world.level.block.MagmaBlock} for example. + * + * @param state The current state + * @return BubbleColumnDirection.NONE for no Bubble Column. Otherwise, will spawn Bubble Column flowing with specified direction + */ + default BubbleColumnDirection getBubbleColumnDirection(BlockState state) { + if (state.is(Blocks.SOUL_SAND)) { + return BubbleColumnDirection.UPWARD; + } else if (state.is(Blocks.MAGMA_BLOCK)) { + return BubbleColumnDirection.DOWNWARD; + } else { + return BubbleColumnDirection.NONE; + } + } } diff --git a/src/main/java/net/neoforged/neoforge/common/extensions/IBlockStateExtension.java b/src/main/java/net/neoforged/neoforge/common/extensions/IBlockStateExtension.java index ea41a4f217..aee4f6741c 100644 --- a/src/main/java/net/neoforged/neoforge/common/extensions/IBlockStateExtension.java +++ b/src/main/java/net/neoforged/neoforge/common/extensions/IBlockStateExtension.java @@ -41,6 +41,7 @@ import net.neoforged.neoforge.common.IPlantable; import net.neoforged.neoforge.common.ToolAction; import net.neoforged.neoforge.common.ToolActions; +import net.neoforged.neoforge.common.enums.BubbleColumnDirection; import net.neoforged.neoforge.common.world.AuxiliaryLightManager; import net.neoforged.neoforge.event.EventHooks; import org.jetbrains.annotations.Nullable; @@ -746,4 +747,16 @@ default BlockState getAppearance(BlockAndTintGetter level, BlockPos pos, Directi default boolean isEmpty() { return self().getBlock().isEmpty(self()); } + + /** + * Determines if this block can spawn Bubble Columns and if so, what direction the column flows. + *

+ * NOTE: The block itself will still need to call {@link net.minecraft.world.level.block.BubbleColumnBlock#updateColumn(LevelAccessor, BlockPos, BlockState)} in their tick method and schedule a block tick in the block's onPlace. + * Also, schedule a fluid tick in updateShape method if update direction is up. Both are needed in order to get the Bubble Columns to function properly. See {@link net.minecraft.world.level.block.SoulSandBlock} and {@link net.minecraft.world.level.block.MagmaBlock} for example. + * + * @return BubbleColumnDirection.NONE for no Bubble Column. Otherwise, will spawn Bubble Column flowing with specified direction + */ + default BubbleColumnDirection getBubbleColumnDirection() { + return self().getBlock().getBubbleColumnDirection(self()); + } } diff --git a/tests/src/main/java/net/neoforged/neoforge/debug/block/BlockTests.java b/tests/src/main/java/net/neoforged/neoforge/debug/block/BlockTests.java index a088831618..bf4977d00b 100644 --- a/tests/src/main/java/net/neoforged/neoforge/debug/block/BlockTests.java +++ b/tests/src/main/java/net/neoforged/neoforge/debug/block/BlockTests.java @@ -11,8 +11,10 @@ import net.minecraft.gametest.framework.GameTest; import net.minecraft.network.protocol.game.ClientboundSoundPacket; import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; import net.minecraft.sounds.SoundEvents; +import net.minecraft.util.RandomSource; import net.minecraft.world.InteractionHand; import net.minecraft.world.ItemInteractionResult; import net.minecraft.world.entity.EntityType; @@ -22,20 +24,24 @@ import net.minecraft.world.item.Items; import net.minecraft.world.level.GameType; import net.minecraft.world.level.Level; +import net.minecraft.world.level.LevelAccessor; import net.minecraft.world.level.LevelReader; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.BubbleColumnBlock; import net.minecraft.world.level.block.FenceGateBlock; import net.minecraft.world.level.block.state.BlockBehaviour; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.phys.BlockHitResult; import net.minecraft.world.phys.Vec3; import net.neoforged.neoforge.client.model.generators.BlockStateProvider; +import net.neoforged.neoforge.common.enums.BubbleColumnDirection; import net.neoforged.testframework.DynamicTest; import net.neoforged.testframework.annotation.ForEachTest; import net.neoforged.testframework.annotation.TestHolder; import net.neoforged.testframework.gametest.EmptyTemplate; import net.neoforged.testframework.gametest.ExtendedGameTestHelper; +import net.neoforged.testframework.gametest.StructureTemplateBuilder; import net.neoforged.testframework.registration.RegistrationHelper; import org.jetbrains.annotations.Nullable; @@ -126,4 +132,65 @@ static void deadBushTerracottaTest(final ExtendedGameTestHelper helper) { .thenSucceed(); } + + @GameTest() + @TestHolder(description = "Adds a block that can sustain Bubble Columns and verify it works") + static void bubbleColumnTest(final DynamicTest test, final RegistrationHelper reg) { + final var upwardBubbleColumnSustainingBlock = reg.blocks() + .registerBlock("upward_bubble_column_sustaining_block", (properties) -> new CustomBubbleColumnSustainingBlock(properties, BubbleColumnDirection.UPWARD), BlockBehaviour.Properties.of()) + .withLang("Upward Bubble Column Sustaining block") + .withDefaultWhiteModel() + .withBlockItem(); + final var downwardBubbleColumnSustainingBlock = reg.blocks() + .registerBlock("downward_bubble_column_sustaining_block", (properties) -> new CustomBubbleColumnSustainingBlock(properties, BubbleColumnDirection.DOWNWARD), BlockBehaviour.Properties.of()) + .withLang("Downward Bubble Column Sustaining block") + .withDefaultWhiteModel() + .withBlockItem(); + + test.registerGameTestTemplate(StructureTemplateBuilder.withSize(3, 2, 3) + .fill(0, 0, 0, 2, 2, 2, Blocks.WATER)); + + test.onGameTest(helper -> helper.startSequence() + .thenExecute(() -> helper.setBlock(new BlockPos(0, 0, 1), upwardBubbleColumnSustainingBlock.get().defaultBlockState())) + .thenExecute(() -> helper.setBlock(new BlockPos(1, 0, 1), downwardBubbleColumnSustainingBlock.get().defaultBlockState())) + .thenExecute(() -> helper.setBlock(new BlockPos(2, 0, 1), Blocks.OAK_PLANKS.defaultBlockState())) + .thenIdle(20) + .thenExecute(() -> helper.assertTrue(helper.getBlockState(new BlockPos(0, 2, 1)).is(Blocks.BUBBLE_COLUMN), "Bubble Column presence was not found where it should be")) + .thenExecute(() -> helper.assertTrue(helper.getBlockState(new BlockPos(1, 2, 1)).is(Blocks.BUBBLE_COLUMN), "Bubble Column presence was not found where it should be")) + .thenExecute(() -> helper.assertFalse(helper.getBlockState(new BlockPos(2, 2, 1)).is(Blocks.BUBBLE_COLUMN), "Bubble Column presence was found where it shouldn't be")) + .thenSucceed()); + } + + private static class CustomBubbleColumnSustainingBlock extends Block { + private final BubbleColumnDirection bubbleColumnDirection; + + public CustomBubbleColumnSustainingBlock(Properties properties, BubbleColumnDirection bubbleColumnDirection1) { + super(properties); + this.bubbleColumnDirection = bubbleColumnDirection1; + } + + @Override + protected void tick(BlockState blockState, ServerLevel serverLevel, BlockPos blockPos, RandomSource randomSource) { + BubbleColumnBlock.updateColumn(serverLevel, blockPos.above(), blockState); + } + + @Override + protected BlockState updateShape(BlockState currentBlockState, Direction direction, BlockState sideBlockState, LevelAccessor levelAccessor, BlockPos currentBlockPos, BlockPos sideBlockPos) { + if (direction == Direction.UP && sideBlockState.is(Blocks.WATER)) { + levelAccessor.scheduleTick(currentBlockPos, this, 1); + } + + return super.updateShape(currentBlockState, direction, sideBlockState, levelAccessor, currentBlockPos, sideBlockPos); + } + + @Override + protected void onPlace(BlockState blockState, Level level, BlockPos blockPos, BlockState oldBlockState, boolean isMoving) { + level.scheduleTick(blockPos, this, 1); + } + + @Override + public BubbleColumnDirection getBubbleColumnDirection(BlockState state) { + return bubbleColumnDirection; + } + } }