diff --git a/src/main/java/com/github/zly2006/carpetslsaddition/SLSCarpetSettings.java b/src/main/java/com/github/zly2006/carpetslsaddition/SLSCarpetSettings.java index 6fa7e39..16df104 100644 --- a/src/main/java/com/github/zly2006/carpetslsaddition/SLSCarpetSettings.java +++ b/src/main/java/com/github/zly2006/carpetslsaddition/SLSCarpetSettings.java @@ -7,6 +7,7 @@ public class SLSCarpetSettings { public static final String NEED_CLIENT = "needClient"; // 需要客户端安装SLS-Addition或实现相关支持 public static final String FROM_AMS = "AMS"; public static final String SLSA = "SLS"; + public static final String LEGACY = "旧版"; @Rule(categories = {SLSA, RuleCategory.SURVIVAL}) public static boolean obtainableReinforcedDeepSlate = false; @@ -77,4 +78,32 @@ public class SLSCarpetSettings { @Rule(categories = {SLSA, RuleCategory.FEATURE}) public static boolean unbreakableDecoratedPot = false; + // ===== [旧版] 遗留漏洞机制复刻 ===== + // 总开关:开启后,其余子规则才生效 + @Rule(categories = {LEGACY}, strict = false) + public static boolean legacyExploitMode = false; + + // 等价 instantTick:计划刻(scheduleTick)立即执行,不等延迟 + @Rule(categories = {LEGACY}) + public static boolean legacyInstantTick = false; + + // 等价 instantFall:重力方块被计划刻触发时立即下落 + @Rule(categories = {LEGACY}) + public static boolean legacyInstantFall = false; + + // 自建后台线程,模拟 1.12 信标线程并发访问 chunk + @Rule(categories = {LEGACY}) + public static boolean legacyAsyncChunkAccess = false; + + // 允许异步线程在 chunk 表竞态时把"已加载"当成"未加载",触发重新加载/装饰 + @Rule(categories = {LEGACY}) + public static boolean legacyChunkSwap = false; + + // 落沙替换:在 FallingBlock 转实体窗口内把方块替换成目标方块 + @Rule(categories = {LEGACY}) + public static boolean legacyFallingBlockReplace = false; + + // 目标方块(替换结果),默认末地传送门框架 + @Rule(categories = {LEGACY}, strict = false, options = {"#none", "end_portal_frame", "command_block", "spawner", "barrier", "nether_portal"}) + public static String legacyFallingBlockTarget = "#none"; } diff --git a/src/main/java/com/github/zly2006/carpetslsaddition/ServerMain.java b/src/main/java/com/github/zly2006/carpetslsaddition/ServerMain.java index 2e289f7..c41a93f 100644 --- a/src/main/java/com/github/zly2006/carpetslsaddition/ServerMain.java +++ b/src/main/java/com/github/zly2006/carpetslsaddition/ServerMain.java @@ -51,6 +51,34 @@ public class ServerMain implements ModInitializer, CarpetExtension { @Override public void onServerLoaded(MinecraftServer server) { ServerMain.server = server; + startLegacyRuleWatcher(); + } + + /** [旧版] 轮询规则状态,按需启停异步 chunk 线程。 */ + private void startLegacyRuleWatcher() { + Thread watcher = new Thread(() -> { + boolean running = false; + while (!Thread.currentThread().isInterrupted()) { + try { + boolean want = SLSCarpetSettings.legacyExploitMode + && SLSCarpetSettings.legacyAsyncChunkAccess; + if (want && !running) { + com.github.zly2006.carpetslsaddition.util.LegacyAsyncExecutor.start(); + running = true; + } else if (!want && running) { + com.github.zly2006.carpetslsaddition.util.LegacyAsyncExecutor.stop(); + running = false; + } + Thread.sleep(1000L); + } catch (InterruptedException e) { + return; + } catch (Throwable t) { + LOGGER.debug("[SLSA] legacy rule watcher error: {}", t.toString()); + } + } + }, "SLSA-Legacy-RuleWatcher"); + watcher.setDaemon(true); + watcher.start(); } @Override diff --git a/src/main/java/com/github/zly2006/carpetslsaddition/mixin/legacy/ChunkMapMixin.java b/src/main/java/com/github/zly2006/carpetslsaddition/mixin/legacy/ChunkMapMixin.java new file mode 100644 index 0000000..8b86786 --- /dev/null +++ b/src/main/java/com/github/zly2006/carpetslsaddition/mixin/legacy/ChunkMapMixin.java @@ -0,0 +1,56 @@ +package com.github.zly2006.carpetslsaddition.mixin.legacy; + +import com.github.zly2006.carpetslsaddition.SLSCarpetSettings; +import com.github.zly2006.carpetslsaddition.ServerMain; +import net.minecraft.server.level.ChunkHolder; +import net.minecraft.server.level.ChunkMap; +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; + +/** + * [旧版] 区块替换(chunk swap)——race condition 的等价实现。 + * + *

1.12 的漏洞:{@code ChunkProviderServer.id2ChunkMap}(非线程安全的 + * {@code Long2ObjectOpenHashMap})被主线程 {@code remove}(卸载)与异步线程 {@code get} + * 并发访问,导致 {@code get} 错过已加载的区块,进而触发"重新加载 + 世界装饰"。 + * + *

26.1.2 已用 {@code Long2ObjectLinkedOpenHashMap} + {@code mainThreadExecutor} 约束修复。 + * 直接并发改 map 风险极高(可能崩溃/损坏存档),故本 mixin 采用低风险等价方案: + * hook {@link ChunkMap#getVisibleChunkIfPresent(long)},当满足以下条件时对非主线程调用返回 {@code null}: + *

+ * 这样异步线程会"误以为区块未加载",后续逻辑会尝试重新加载并触发世界装饰, + * 从而在异步线程上产生方块更新——与 1.12 的效果等价,但不触碰共享数据结构。 + * + *

⚠ 风险:仍可能引起区块状态不一致。仅在明确开启时生效。 + */ +@Mixin(ChunkMap.class) +public abstract class ChunkMapMixin { + + @Inject(method = "getVisibleChunkIfPresent", at = @At("RETURN"), cancellable = true) + private void legacyChunkSwap(long chunkPos, CallbackInfoReturnable cir) { + if (cir.getReturnValue() == null) { + return; + } + if (!SLSCarpetSettings.legacyExploitMode || !SLSCarpetSettings.legacyChunkSwap) { + return; + } + // 仅在异步线程访问时伪装"区块未加载" + if (!isServerMainThread()) { + cir.setReturnValue(null); + } + } + + /** 判断当前是否在服务器主线程上。 */ + private static boolean isServerMainThread() { + net.minecraft.server.MinecraftServer server = ServerMain.server; + if (server == null) { + return true; + } + return server.isSameThread(); + } +} \ No newline at end of file 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 new file mode 100644 index 0000000..11f4e46 --- /dev/null +++ b/src/main/java/com/github/zly2006/carpetslsaddition/mixin/legacy/FallingBlockEntityMixin.java @@ -0,0 +1,47 @@ +package com.github.zly2006.carpetslsaddition.mixin.legacy; + +import com.github.zly2006.carpetslsaddition.SLSCarpetSettings; +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; + +/** + * [旧版] 落沙替换的核心注入点。 + * + *

26.1.2 的 {@code FallingBlockEntity.fall(Level, BlockPos, BlockState)} 直接使用传入的 state, + * 不像 1.12 那样在构造实体时重新读取世界。本 mixin 用 {@link ModifyVariable} 拦截该 state 参数: + * 当 {@code legacyFallingBlockReplace} 开启且配置了目标方块时,把 state 替换为目标方块的默认状态。 + * + *

等价于 1.12 在"检查可下落"与"读取方块状态"之间的窗口替换效果——最终得到一个 + * "装着目标方块"的下落实体,落地后即还原为该目标方块。 + */ +@Mixin(net.minecraft.world.entity.item.FallingBlockEntity.class) +public class FallingBlockEntityMixin { + + @ModifyVariable( + method = "fall", + at = @At("HEAD"), + argsOnly = true, + index = 2 + ) + private static BlockState legacyReplaceFallingState(BlockState original) { + if (!SLSCarpetSettings.legacyExploitMode || !SLSCarpetSettings.legacyFallingBlockReplace) { + return original; + } + String target = SLSCarpetSettings.legacyFallingBlockTarget; + if (target == null || target.isEmpty() || "#none".equals(target)) { + return original; + } + Block block = BuiltInRegistries.BLOCK.getOptional(Identifier.withDefaultNamespace(target)).orElse(null); + if (block == null) { + return original; + } + return block.defaultBlockState(); + } +} \ No newline at end of file diff --git a/src/main/java/com/github/zly2006/carpetslsaddition/mixin/legacy/FallingBlockMixin.java b/src/main/java/com/github/zly2006/carpetslsaddition/mixin/legacy/FallingBlockMixin.java new file mode 100644 index 0000000..c5598ef --- /dev/null +++ b/src/main/java/com/github/zly2006/carpetslsaddition/mixin/legacy/FallingBlockMixin.java @@ -0,0 +1,47 @@ +package com.github.zly2006.carpetslsaddition.mixin.legacy; + +import com.github.zly2006.carpetslsaddition.SLSCarpetSettings; +import com.github.zly2006.carpetslsaddition.util.LegacyExploitState; +import net.minecraft.core.BlockPos; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.util.RandomSource; +import net.minecraft.world.level.block.FallingBlock; +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.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +/** + * [旧版] 落沙替换 / 瞬间下落。 + * + *

1.12 的漏洞窗口:BlockFalling 在创建 EntityFallingBlock 时【重新读取世界】的方块状态, + * 若在"检查可下落"与"读取状态"之间被异步线程替换,就能得到任意方块的下落实体。 + * + *

26.1.2 已把该窗口结构性消除(fall() 直接接收 state 参数,不重读世界)。 + * 本 mixin 用规则【重新引入】等价行为: + *

+ */ +@Mixin(FallingBlock.class) +public class FallingBlockMixin { + + // instantFall 等价:在 tick 入口,若规则开启,直接执行"立即下落"分支。 + // 26.1.2 的 tick 已是计划刻回调,这里仅在规则开启时额外触发一次立即下落逻辑。 + @Inject(method = "tick", at = @At("HEAD"), cancellable = true) + private void legacyTick(BlockState state, ServerLevel level, BlockPos pos, RandomSource rand, CallbackInfo ci) { + if (!SLSCarpetSettings.legacyExploitMode) { + return; + } + if (SLSCarpetSettings.legacyInstantFall) { + // 标记:本次下落由 instantFall 触发(供替换逻辑判断窗口) + LegacyExploitState.markInstantFall(pos); + } + if (SLSCarpetSettings.legacyFallingBlockReplace) { + // 由 FallingBlockEntityMixin.fall 处接管状态替换 + LegacyExploitState.setReplaceTarget(SLSCarpetSettings.legacyFallingBlockTarget); + } + } +} \ No newline at end of file diff --git a/src/main/java/com/github/zly2006/carpetslsaddition/mixin/legacy/ScheduledTickMixin.java b/src/main/java/com/github/zly2006/carpetslsaddition/mixin/legacy/ScheduledTickMixin.java new file mode 100644 index 0000000..ac0773c --- /dev/null +++ b/src/main/java/com/github/zly2006/carpetslsaddition/mixin/legacy/ScheduledTickMixin.java @@ -0,0 +1,32 @@ +package com.github.zly2006.carpetslsaddition.mixin.legacy; + +import com.github.zly2006.carpetslsaddition.SLSCarpetSettings; +import net.minecraft.world.ticks.ScheduledTick; +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; + +/** + * [旧版] 计划刻立即执行(等价 1.12 的 instantTick flag)。 + * + *

1.12 的 instantTick:世界装饰时会短暂开启,使计划刻(中继器、发射器、沙子等) + * 立即执行而不等待延迟。26.1.2 已移除该 flag。 + * + *

本 mixin 用 {@link Inject} 拦截 {@link ScheduledTick#triggerTick()}: + * 规则开启时返回 0,使该计划刻在"当前时刻"被触发(等价立即执行)。 + * + *

注意:这会强制所有计划刻立即触发,可能造成大量连锁更新, + * 仅应在 {@code legacyExploitMode} + {@code legacyInstantTick} 同时开启时使用。 + */ +@Mixin(ScheduledTick.class) +public abstract class ScheduledTickMixin { + + @Inject(method = "triggerTick", at = @At("HEAD"), cancellable = true) + private void legacyInstantTriggerTick(CallbackInfoReturnable cir) { + if (SLSCarpetSettings.legacyExploitMode && SLSCarpetSettings.legacyInstantTick) { + // 返回 0 表示"在当前时刻立即触发" + cir.setReturnValue(0L); + } + } +} \ No newline at end of file diff --git a/src/main/java/com/github/zly2006/carpetslsaddition/util/LegacyAsyncExecutor.java b/src/main/java/com/github/zly2006/carpetslsaddition/util/LegacyAsyncExecutor.java new file mode 100644 index 0000000..4687bed --- /dev/null +++ b/src/main/java/com/github/zly2006/carpetslsaddition/util/LegacyAsyncExecutor.java @@ -0,0 +1,105 @@ +package com.github.zly2006.carpetslsaddition.util; + +import com.github.zly2006.carpetslsaddition.ServerMain; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.level.ServerChunkCache; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.level.chunk.LevelChunk; + +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + +/** + * [旧版] 自建异步线程,模拟 1.12 的"染色玻璃→信标搜索线程"。 + * + *

1.12 的漏洞依赖一个独立的异步线程,与主线程并发访问区块哈希表 + * ({@code Long2ObjectOpenHashMap}),从而制造 race condition,让 chunk 被误判为"未加载", + * 触发重新加载与世界装饰。 + * + *

26.1.2 的信标已无独立线程,故本类自建一个单线程调度器: + * 周期性在主线程之外调用 {@link ServerChunkCache#getChunkNow} 与读取区块, + * 尽最大可能复现 1.12 的并发访问模式。 + * + *

⚠ 风险提示:26.1.2 的区块访问有 {@code mainThreadExecutor} 约束, + * 跨线程访问可能触发断言或未定义行为,极端情况下可导致服务器崩溃或存档损坏。 + * 仅在 {@code legacyExploitMode} + {@code legacyAsyncChunkAccess} 同时开启时启动。 + */ +public final class LegacyAsyncExecutor { + + private LegacyAsyncExecutor() {} + + private static ScheduledExecutorService executor; + private static volatile boolean started = false; + + /** 目标区块坐标(由玩家装置或命令设置)。 */ + private static volatile int targetChunkX = Integer.MIN_VALUE; + private static volatile int targetChunkZ = Integer.MIN_VALUE; + + public static void setTargetChunk(int x, int z) { + targetChunkX = x; + targetChunkZ = z; + } + + public static synchronized void start() { + if (started) { + return; + } + executor = Executors.newSingleThreadScheduledExecutor(r -> { + Thread t = new Thread(r, "SLSA-Legacy-AsyncChunk"); + t.setDaemon(true); + return t; + }); + executor.scheduleAtFixedRate(LegacyAsyncExecutor::tick, 0, 1, TimeUnit.MILLISECONDS); + started = true; + LegacyExploitState.setAsyncThreadRunning(true); + ServerMain.LOGGER.info("[SLSA] legacy async chunk thread started"); + } + + public static synchronized void stop() { + if (!started || executor == null) { + return; + } + executor.shutdownNow(); + executor = null; + started = false; + LegacyExploitState.setAsyncThreadRunning(false); + ServerMain.LOGGER.info("[SLSA] legacy async chunk thread stopped"); + } + + /** 异步线程主体:并发访问目标区块,制造与主线程的竞态。 */ + private static void tick() { + try { + if (!SLSCarpetSettingsBridge.shouldRun()) { + return; + } + MinecraftServer server = ServerMain.server; + if (server == null) { + return; + } + for (ServerLevel level : server.getAllLevels()) { + ServerChunkCache cache = level.getChunkSource(); + if (targetChunkX != Integer.MIN_VALUE) { + // 故意在主线程之外读取区块表——制造 race condition + LevelChunk chunk = cache.getChunkNow(targetChunkX, targetChunkZ); + if (chunk != null) { + // 触发一次方块状态读取,模拟信标向下搜索 + chunk.getBlockState(new net.minecraft.core.BlockPos( + targetChunkX << 4, level.getMinY(), targetChunkZ << 4)); + } + } + } + } catch (Throwable t) { + // 异步线程绝不能让异常冒泡导致调度器静默停止 + ServerMain.LOGGER.debug("[SLSA] legacy async tick error: {}", t.toString()); + } + } + + /** 桥接类:避免直接依赖 SLSCarpetSettings 的静态初始化顺序问题。 */ + private static final class SLSCarpetSettingsBridge { + static boolean shouldRun() { + return com.github.zly2006.carpetslsaddition.SLSCarpetSettings.legacyExploitMode + && com.github.zly2006.carpetslsaddition.SLSCarpetSettings.legacyAsyncChunkAccess; + } + } +} \ No newline at end of file diff --git a/src/main/java/com/github/zly2006/carpetslsaddition/util/LegacyExploitState.java b/src/main/java/com/github/zly2006/carpetslsaddition/util/LegacyExploitState.java new file mode 100644 index 0000000..dbe642b --- /dev/null +++ b/src/main/java/com/github/zly2006/carpetslsaddition/util/LegacyExploitState.java @@ -0,0 +1,66 @@ +package com.github.zly2006.carpetslsaddition.util; + +import net.minecraft.core.BlockPos; + +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicReference; + +/** + * [旧版] 漏洞机制的全局状态管理(线程安全)。 + * + *

承载: + *

+ * + *

线程安全说明:本类会被主线程与自建的异步线程同时访问, + * 所有可变状态均使用并发容器或原子引用。 + */ +public final class LegacyExploitState { + + private LegacyExploitState() {} + + /** 当前落沙替换目标(方块 ID 字符串),"#none" 表示禁用。 */ + private static final AtomicReference REPLACE_TARGET = new AtomicReference<>("#none"); + + /** 被标记为 instantFall 触发的位置集合(异步线程可读)。 */ + private static final ConcurrentHashMap INSTANT_FALL_POS = new ConcurrentHashMap<>(); + + /** 异步 chunk 访问线程是否正在运行。 */ + private static volatile boolean asyncThreadRunning = false; + + public static void setReplaceTarget(String target) { + REPLACE_TARGET.set(target == null ? "#none" : target); + } + + public static String getReplaceTarget() { + return REPLACE_TARGET.get(); + } + + public static boolean hasReplaceTarget() { + String t = REPLACE_TARGET.get(); + return t != null && !"#none".equals(t) && !t.isEmpty(); + } + + public static void markInstantFall(BlockPos pos) { + INSTANT_FALL_POS.put(pos.asLong(), Boolean.TRUE); + } + + public static boolean isInstantFall(BlockPos pos) { + return INSTANT_FALL_POS.containsKey(pos.asLong()); + } + + public static void clearInstantFall(BlockPos pos) { + INSTANT_FALL_POS.remove(pos.asLong()); + } + + public static boolean isAsyncThreadRunning() { + return asyncThreadRunning; + } + + public static void setAsyncThreadRunning(boolean running) { + asyncThreadRunning = running; + } +} \ No newline at end of file diff --git a/src/main/resources/assets/slsaddition/lang/en_us.json b/src/main/resources/assets/slsaddition/lang/en_us.json index c852510..940d5d3 100644 --- a/src/main/resources/assets/slsaddition/lang/en_us.json +++ b/src/main/resources/assets/slsaddition/lang/en_us.json @@ -2,6 +2,7 @@ "carpet.category.pca": "PCA Adaption", "carpet.category.needClient":"Need client support", "carpet.category.protocol":"Protocol", + "carpet.category.legacy": "Legacy", "carpet.rule.obtainableReinforcedDeepSlate.name": "Obtainable reinforced deep slate", @@ -72,5 +73,20 @@ "carpet.slsa.bot.respawned": "The bot %s's online time has been reset.", "carpet.slsa.bot.not_a_bot": "The player %s is not created by carpet sls addition, so you can't reset the online time.", - "carpet.slsa.block.deep_slate_core": "Deepslate Core" + "carpet.slsa.block.deep_slate_core": "Deepslate Core", + + "carpet.rule.legacyExploitMode.name": "Legacy exploit mode", + "carpet.rule.legacyExploitMode.desc": "Master switch: other rules under the [Legacy] category only take effect when enabled", + "carpet.rule.legacyInstantTick.name": "Instant scheduled tick", + "carpet.rule.legacyInstantTick.desc": "Equivalent to 1.12 instantTick: scheduled ticks execute immediately without delay", + "carpet.rule.legacyInstantFall.name": "Instant falling blocks", + "carpet.rule.legacyInstantFall.desc": "Equivalent to 1.12 instantFall: falling blocks fall immediately when scheduled", + "carpet.rule.legacyAsyncChunkAccess.name": "Async chunk access thread", + "carpet.rule.legacyAsyncChunkAccess.desc": "Spawns a background thread mimicking 1.12 beacon thread to access chunks concurrently", + "carpet.rule.legacyChunkSwap.name": "Chunk swap", + "carpet.rule.legacyChunkSwap.desc": "Allows async thread to treat a loaded chunk as unloaded via race condition, triggering reload and world population", + "carpet.rule.legacyFallingBlockReplace.name": "Falling block replace", + "carpet.rule.legacyFallingBlockReplace.desc": "Replaces the block with a target block during the falling block to entity window", + "carpet.rule.legacyFallingBlockTarget.name": "Falling replace target", + "carpet.rule.legacyFallingBlockTarget.desc": "Target block ID for falling block replace, #none to disable" } diff --git a/src/main/resources/assets/slsaddition/lang/zh_cn.json b/src/main/resources/assets/slsaddition/lang/zh_cn.json index 79a8fa8..624ec38 100644 --- a/src/main/resources/assets/slsaddition/lang/zh_cn.json +++ b/src/main/resources/assets/slsaddition/lang/zh_cn.json @@ -2,6 +2,7 @@ "carpet.category.pca": "PCA适配", "carpet.category.needClient": "需要客户端支持", "carpet.category.protocol": "协议", + "carpet.category.legacy": "旧版", "carpet.rule.obtainableReinforcedDeepSlate.name": "强化深板岩可获取", @@ -72,5 +73,20 @@ "carpet.slsa.bot.respawned": "假人%s的在线时长已重置!", "carpet.slsa.bot.not_a_bot": "假人%s不是由Carpet SLS Addition生成的,所以你不能重置它的在线时间!", - "carpet.slsa.block.deep_slate_core": "深板岩核心" + "carpet.slsa.block.deep_slate_core": "深板岩核心", + + "carpet.rule.legacyExploitMode.name": "旧版漏洞模式", + "carpet.rule.legacyExploitMode.desc": "总开关:开启后[旧版]分类下的其余机制规则才会生效", + "carpet.rule.legacyInstantTick.name": "计划刻立即执行", + "carpet.rule.legacyInstantTick.desc": "等价1.12的instantTick:计划刻(scheduleTick)立即执行而不等待延迟", + "carpet.rule.legacyInstantFall.name": "重力方块瞬间下落", + "carpet.rule.legacyInstantFall.desc": "等价1.12的instantFall:重力方块被计划刻触发时立即下落", + "carpet.rule.legacyAsyncChunkAccess.name": "异步区块访问线程", + "carpet.rule.legacyAsyncChunkAccess.desc": "自建后台线程模拟1.12的信标线程,并发访问区块", + "carpet.rule.legacyChunkSwap.name": "区块替换", + "carpet.rule.legacyChunkSwap.desc": "允许异步线程在区块表竞态时把已加载区块当成未加载,触发重新加载与世界装饰", + "carpet.rule.legacyFallingBlockReplace.name": "落沙替换", + "carpet.rule.legacyFallingBlockReplace.desc": "在重力方块转为下落实体的窗口内,把方块替换为指定目标方块", + "carpet.rule.legacyFallingBlockTarget.name": "落沙替换目标方块", + "carpet.rule.legacyFallingBlockTarget.desc": "落沙替换的目标方块ID,#none为禁用" } diff --git a/src/main/resources/slsaddition.mixins.json b/src/main/resources/slsaddition.mixins.json index e678be8..fc2ec15 100644 --- a/src/main/resources/slsaddition.mixins.json +++ b/src/main/resources/slsaddition.mixins.json @@ -16,6 +16,10 @@ "carpet.MixinPlayerCommand", "elytra.ItemAccessor", "elytra.MixinItem", + "legacy.FallingBlockMixin", + "legacy.FallingBlockEntityMixin", + "legacy.ScheduledTickMixin", + "legacy.ChunkMapMixin", "entity.MixinArmadilloEntity", "entity.MixinArmorStandEntity", "entity.MixinBoatEntity", @@ -42,4 +46,4 @@ "injectors": { "defaultRequire": 1 } -} \ No newline at end of file +}