fix: 碎冰规则改为只保留水源变碎冰,流动水不再中断水流

原实现直接拦截 setBlockAndUpdate 并返回 false,导致邻接蓝冰的流动水被 shouldSpreadLiquid 的 return false 短路,水流扩散停止。
改为在 BlockState#is(BLUE_ICE) 判断处做手脚:规则开启且当前水非水源时让 is 返回 false,原版整个变碎冰分支被跳过,shouldSpreadLiquid 正常返回 true,水流照常扩散。
This commit is contained in:
AxTps committed 2026-10-17 15:10:07 +08:00
1 parent 94fafe972c
commit 61ce9891b9
1 file changed
+41 -22
@@ -8,10 +8,12 @@ import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.LiquidBlock; import net.minecraft.world.level.block.LiquidBlock;
import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.material.FlowingFluid;
import net.minecraft.world.level.material.FluidState; import net.minecraft.world.level.material.FluidState;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
/** /**
* 26.4 快照加入的“碎冰”(Brittle Ice)机制: * 26.4 快照加入的“碎冰”(Brittle Ice)机制:
@@ -19,32 +21,49 @@ import org.spongepowered.asm.mixin.injection.At;
* 会把当前水流方块替换为碎冰。 * 会把当前水流方块替换为碎冰。
* *
* 开启 noBrittleIceFromFlowingWater 规则后: * 开启 noBrittleIceFromFlowingWater 规则后:
* 仅当当前水方块处于“下落”状态(FlowingFluid#isFalling,即非水源且流体等级=8)时, * 只有「水源方块」贴蓝冰时照常变碎冰;所有「流动的水」(横向、下落)都不再变碎冰,
* 才允许它被替换为碎冰;纯横向流动的水不再触发该替换,以保护蓝冰水道。 * 且水流扩散不受影响。
* *
* 实现方式:用 WrapOperation 包住 shouldSpreadLiquid 里对 Level#setBlockAndUpdate 的调用, * 实现:原版判断邻居是不是蓝冰用 BlockState#is(Blocks.BLUE_ICE),一旦命中就
* 只在「目标方块是 BRITTLE_ICE」且「当前水不是下落状态」时跳过该次设置,其余调用原样放行。 * 变碎冰并 return false(中断扩散)。本 mixin 让流动水贴蓝冰时这次 is() 返回 false,
* 整个“变碎冰”分支被跳过,shouldSpreadLiquid 正常返回 true,水流照常扩散。
* 位置信息通过 ThreadLocal 从方法入口传入。
*/ */
@Mixin(LiquidBlock.class) @Mixin(LiquidBlock.class)
public class MixinFlowingWaterBrittleIce { public class MixinFlowingWaterBrittleIce {
@WrapOperation( @Unique
method = "shouldSpreadLiquid", private static final ThreadLocal<Level> CURRENT_LEVEL = new ThreadLocal<>();
at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/Level;setBlockAndUpdate(Lnet/minecraft/core/BlockPos;Lnet/minecraft/world/level/block/state/BlockState;)Z") @Unique
) private static final ThreadLocal<BlockPos> CURRENT_POS = new ThreadLocal<>();
private boolean wrapSetBlockAndUpdate(Level level, BlockPos pos, BlockState newState,
Operation<Boolean> original) { @Inject(method = "shouldSpreadLiquid", at = @At("HEAD"))
if (SLSCarpetSettings.noBrittleIceFromFlowingWater private void captureArgs(Level level, BlockPos pos, BlockState state, CallbackInfoReturnable<Boolean> cir) {
&& newState.is(Blocks.BRITTLE_ICE)) { CURRENT_LEVEL.set(level);
FluidState current = level.getFluidState(pos); CURRENT_POS.set(pos);
if (!isFalling(current)) {
return false; // 跳过把这块水替换成碎冰
}
}
return original.call(level, pos, newState);
} }
private static boolean isFalling(FluidState fluid) { @WrapOperation(
if (!(fluid.getType() instanceof FlowingFluid flowing)) return false; method = "shouldSpreadLiquid",
return flowing.isFalling(fluid); at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/block/state/BlockState;is(Ljava/lang/Object;)Z")
)
private boolean wrapIsBlueIce(BlockState instance, Object target, Operation<Boolean> original) {
Boolean result = original.call(instance, target);
if (!result) return false;
// 只有判断目标确实是 BLUE_ICE 时才可能干预
if (!(target instanceof net.minecraft.world.level.block.Block b) || !b.equals(Blocks.BLUE_ICE)) {
return true;
}
if (!SLSCarpetSettings.noBrittleIceFromFlowingWater) return true;
Level level = CURRENT_LEVEL.get();
BlockPos pos = CURRENT_POS.get();
if (level == null || pos == null) return true;
FluidState current = level.getFluidState(pos);
// 流动水(非水源)不让它触发碎冰替换
if (!current.isSource()) {
return false;
}
return true;
} }
} }