diff --git a/src/main/java/com/github/zly2006/carpetslsaddition/mixin/legacy/FallingBlockEntityMixin.java b/src/main/java/com/github/zly2006/carpetslsaddition/mixin/legacy/FallingBlockEntityMixin.java index 11f4e46..81b9c6c 100644 --- a/src/main/java/com/github/zly2006/carpetslsaddition/mixin/legacy/FallingBlockEntityMixin.java +++ b/src/main/java/com/github/zly2006/carpetslsaddition/mixin/legacy/FallingBlockEntityMixin.java @@ -1,25 +1,23 @@ package com.github.zly2006.carpetslsaddition.mixin.legacy; import com.github.zly2006.carpetslsaddition.SLSCarpetSettings; +import com.github.zly2006.carpetslsaddition.util.LegacyBlockChangeRecorder; import net.minecraft.core.BlockPos; -import net.minecraft.core.registries.BuiltInRegistries; -import net.minecraft.resources.Identifier; -import net.minecraft.world.level.Level; -import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.state.BlockState; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.ModifyVariable; /** - * [旧版] 落沙替换的核心注入点。 + * [旧版] 落沙替换的核心注入点——重建 1.12 的"替换窗口"(记录+回放版)。 * - *
26.1.2 的 {@code FallingBlockEntity.fall(Level, BlockPos, BlockState)} 直接使用传入的 state, - * 不像 1.12 那样在构造实体时重新读取世界。本 mixin 用 {@link ModifyVariable} 拦截该 state 参数: - * 当 {@code legacyFallingBlockReplace} 开启且配置了目标方块时,把 state 替换为目标方块的默认状态。 + *
1.12:{@code BlockFalling} 创建实体时【重读世界】,异步线程在"检查→读取"间改块即可替换。 + * 26.1.2 单线程下该窗口物理不存在(tick 内同步 fall,无让出)。 * - *
等价于 1.12 在"检查可下落"与"读取方块状态"之间的窗口替换效果——最终得到一个 - * "装着目标方块"的下落实体,落地后即还原为该目标方块。 + *
本 mixin 改用 {@link LegacyBlockChangeRecorder}: + * 当 {@code legacyFallingBlockReplace} 开启时,若该位置在最近 {@code WINDOW_TICKS} 内被改动过, + * 就用记录的新方块作为下落实体的内容。这样玩家搭建的装置(改变世界方块)会决定下落实体的内容, + * 与 1.12 的"装置驱动"玩法一致。 */ @Mixin(net.minecraft.world.entity.item.FallingBlockEntity.class) public class FallingBlockEntityMixin { @@ -30,18 +28,14 @@ public class FallingBlockEntityMixin { argsOnly = true, index = 2 ) - private static BlockState legacyReplaceFallingState(BlockState original) { + private static BlockState legacyReplaceFallingState(BlockState original, net.minecraft.world.level.Level level, BlockPos pos, BlockState state) { if (!SLSCarpetSettings.legacyExploitMode || !SLSCarpetSettings.legacyFallingBlockReplace) { return original; } - String target = SLSCarpetSettings.legacyFallingBlockTarget; - if (target == null || target.isEmpty() || "#none".equals(target)) { - return original; + BlockState recent = LegacyBlockChangeRecorder.getRecentChange(pos); + if (recent != null && !recent.isAir()) { + return recent; } - Block block = BuiltInRegistries.BLOCK.getOptional(Identifier.withDefaultNamespace(target)).orElse(null); - if (block == null) { - return original; - } - return block.defaultBlockState(); + return original; } } \ No newline at end of file diff --git a/src/main/java/com/github/zly2006/carpetslsaddition/mixin/legacy/LevelChunkMixin.java b/src/main/java/com/github/zly2006/carpetslsaddition/mixin/legacy/LevelChunkMixin.java new file mode 100644 index 0000000..7143330 --- /dev/null +++ b/src/main/java/com/github/zly2006/carpetslsaddition/mixin/legacy/LevelChunkMixin.java @@ -0,0 +1,35 @@ +package com.github.zly2006.carpetslsaddition.mixin.legacy; + +import com.github.zly2006.carpetslsaddition.SLSCarpetSettings; +import com.github.zly2006.carpetslsaddition.util.LegacyBlockChangeRecorder; +import net.minecraft.core.BlockPos; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.chunk.LevelChunk; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +/** + * [旧版] 方块变更记录——为"替换窗口"提供数据源。 + * + *
hook {@link LevelChunk#setBlockState}:当规则开启时,记录每次方块变更(位置 + 新状态 + 当前 tick)。 + * 重力方块下落时({@code FallingBlockEntityMixin})会查询此记录, + * 若该位置"刚被改过",就用记录的新方块作为下落实体内容——重建 1.12 的窗口替换效果。 + * + *
仅在 {@code legacyExploitMode} + {@code legacyFallingBlockReplace} 同时开启时记录,避免性能损耗。
+ */
+@Mixin(LevelChunk.class)
+public abstract class LevelChunkMixin {
+
+ @Inject(method = "setBlockState", at = @At("RETURN"))
+ private void legacyRecordBlockChange(BlockPos pos, BlockState newState, int flags, CallbackInfoReturnable 1.12 的窗口依赖"异步线程在主线程 tick 中途改块",26.1.2 单线程模型下物理不存在。
+ * 本类改用"记录+回放":把最近发生的方块变更记录在此,
+ * 当重力方块触发下落时,若该位置"刚被改过",就用记录的新方块作为下落实体的内容——
+ * 等价于 1.12 的窗口替换效果(只是窗口由 mod 主动开启)。
+ *
+ * 记录以"服务器 tick 数"为界:仅保留最近 {@link #WINDOW_TICKS} 个 tick 内的变更,
+ * 超过即失效,避免记录无限增长或误伤无关方块。
+ */
+public final class LegacyBlockChangeRecorder {
+
+ private LegacyBlockChangeRecorder() {}
+
+ /** 窗口大小:变更与下落之间允许的最大 tick 间隔。 */
+ public static final long WINDOW_TICKS = 2L;
+
+ private record Change(BlockState state, long tick) {}
+
+ private static final Map