package dev.modzuozhi.command; import com.mojang.brigadier.CommandDispatcher; import com.mojang.brigadier.context.CommandContext; import dev.modzuozhi.ModZuozhi; import dev.modzuozhi.core.compat.SableMainThreadBridge; import dev.modzuozhi.core.dimthread.DimThreadCore; import dev.modzuozhi.core.dimthread.EntityTickParallel; import dev.modzuozhi.core.dimthread.FaultGuard; import dev.modzuozhi.core.dimthread.FineGrainScheduler; import dev.modzuozhi.core.dimthread.ShardGate; import dev.modzuozhi.core.filter.SerDesFilter; import net.minecraft.commands.CommandSourceStack; import net.minecraft.commands.arguments.ResourceLocationArgument; import net.minecraft.network.chat.Component; import net.minecraft.resources.ResourceKey; import net.minecraft.server.level.ServerLevel; import net.minecraft.world.level.Level; import static net.minecraft.commands.Commands.literal; /** * modzuozhi 管理命令。 *

* 全局多线程优化默认启用,不提供全局开关(运行时切换全局并行风险高,容易引发 * 状态不一致问题)。若触发自动降级(见 {@link FaultGuard}),默认退回原版串行,重启游戏 * 即恢复启用,也可执行 {@code /dimensionalripper fine on} 手动复位。 *

* /dimensionalripper fine on|off - 单独开关"细粒度并行"(维度内 TE/区块/实体并行,默认开启)。 * /dimensionalripper nocollide on|off - 单独开关"禁用实体间碰撞"(Mob Collision Off 模式,默认开启)。 * /dimensionalripper status - 查看当前运行状态、降级情况与故障原因。 */ public final class MasterCommand { private MasterCommand() { } public static void register(CommandDispatcher dispatcher) { dispatcher.register( literal("dimensionalripper") .then(literal("flowstatus").executes(MasterCommand::flowStatus)) .then(literal("status").executes(MasterCommand::status)) .then(literal("fine") .then(literal("on").executes(ctx -> setFine(ctx, true))) .then(literal("off").executes(ctx -> setFine(ctx, false))) .then(literal("sharded") .then(literal("on").executes(ctx -> setSharded(ctx, true))) .then(literal("off").executes(ctx -> setSharded(ctx, false))))) .then(literal("nocollide") .then(literal("on").executes(ctx -> setNoCollide(ctx, true))) .then(literal("off").executes(ctx -> setNoCollide(ctx, false)))) .then(literal("tewhitelist") .then(net.minecraft.commands.Commands.argument("type", net.minecraft.commands.arguments.ResourceLocationArgument.id()) .executes(MasterCommand::teWhitelist))) .then(literal("teunwhitelist") .then(net.minecraft.commands.Commands.argument("type", net.minecraft.commands.arguments.ResourceLocationArgument.id()) .executes(MasterCommand::teUnwhitelist))) .then(literal("status").executes(MasterCommand::status))); } private static int setFine(CommandContext ctx, boolean enabled) { if (enabled && !ModZuozhi.isEnabled()) { // 全局关闭时单独开细粒度没有意义(维度 loop 已停、并行入口被 isActive 拦截), // 还会留下"全局关但细粒度开"的不一致状态。这里自动恢复全局开启,下一 tick // 主线程会重新拉起维度 loop(killAllLoops 已清空 loops map,computeIfAbsent 重建)。 ModZuozhi.ENABLED = true; ctx.getSource().sendSuccess(() -> Component.literal("全局此前已关闭,已随细粒度一并恢复为开启"), false); ModZuozhi.LOGGER.info("[ModZuozhi] 全局随 /dimensionalripper fine on 自动恢复为开启"); } FineGrainScheduler.setEnabled(enabled); // Barriers drain each current phase; keep the server event loop available // for any in-flight task that depends on a server callback. if (enabled) { FaultGuard.reset(); } ctx.getSource().sendSuccess(() -> Component.literal("细粒度并行已" + (enabled ? "开启" : "关闭")), false); return 1; } private static int setSharded(CommandContext ctx, boolean enabled) { EntityTickParallel.setSharded(enabled); ctx.getSource().sendSuccess(() -> Component.literal("分片并行(实体/TE/区块环境)已" + (enabled ? "开启" : "关闭")), false); return 1; } private static int setNoCollide(CommandContext ctx, boolean enabled) { ModZuozhi.MOB_COLLISION_OFF = enabled; ctx.getSource().sendSuccess(() -> Component.literal("实体间碰撞已" + (enabled ? "禁用" : "恢复")), false); ModZuozhi.LOGGER.info("[ModZuozhi] 实体间碰撞已切换为 {}", enabled ? "禁用" : "恢复"); return 1; } /** 将指定 TE 注册名加入 modded 并行白名单(仅影响该 TE,不改全局 lockModded 保守策略)。 */ private static int teWhitelist(CommandContext ctx) { String type = ResourceLocationArgument.getId(ctx, "type").toString(); SerDesFilter.whitelistModded(type); ctx.getSource().sendSuccess(() -> Component.literal("TE 白名单已添加: " + type + "(清单: " + SerDesFilter.moddedWhitelist() + ")"), false); ModZuozhi.LOGGER.info("[ModZuozhi] TE 白名单新增 {}", type); return 1; } /** 将指定 TE 注册名移出 modded 并行白名单(恢复该 modded TE 默认的强制串行)。 */ private static int teUnwhitelist(CommandContext ctx) { String type = ResourceLocationArgument.getId(ctx, "type").toString(); boolean removed = SerDesFilter.unwhitelistModded(type); ctx.getSource().sendSuccess(() -> Component.literal("TE 白名单已" + (removed ? "移除" : "移除(原本不在白名单中)") + ": " + type + "(清单: " + SerDesFilter.moddedWhitelist() + ")"), false); ModZuozhi.LOGGER.info("[ModZuozhi] TE 白名单移除 {}", type); return 1; } private static int flowStatus(CommandContext ctx) { var player = ctx.getSource().getPlayer(); if (player == null) { ctx.getSource().sendFailure(Component.literal("请由玩家站在不流动的水附近执行。")); return 0; } dev.modzuozhi.core.compat.SableAssemblyDispatch.dispatch(player, () -> { var level = player.serverLevel(); var hit = player.pick(8.0, 0.0F, true); var pos = hit instanceof net.minecraft.world.phys.BlockHitResult blockHit && hit.getType() == net.minecraft.world.phys.HitResult.Type.BLOCK ? blockHit.getBlockPos() : player.blockPosition(); var targetChunk = new net.minecraft.world.level.ChunkPos(pos); long chunk = targetChunk.toLong(); String message = "水流诊断:维度=" + level.dimension().location() + ",目标=" + pos.toShortString() + ",区块=" + targetChunk + ",世界时间=" + level.getGameTime() + ",正常刻=" + level.tickRateManager().runsNormally() + ",调试世界=" + level.isDebug() + ",区块可更新=" + level.getChunkSource().isPositionTicking(chunk) + ",实体已加载=" + level.areEntitiesLoaded(chunk) + ",目标流体=" + level.getFluidState(pos).getType() + ",待处理流体刻=" + level.getFluidTicks().count() + ",待处理方块刻=" + level.getBlockTicks().count(); ctx.getSource().sendSuccess(() -> Component.literal(message), false); String detail = ((dev.modzuozhi.core.compat.ScheduledTickDiagnostics) level.getFluidTicks()) .dimensionalripper$describeTicks(pos, level.getGameTime()); ctx.getSource().sendSuccess(() -> Component.literal("流体调度明细:" + detail), false); }); return 1; } private static int status(CommandContext ctx) { String fine = FineGrainScheduler.isEnabled() ? "开启" : "关闭"; String noCollide = ModZuozhi.MOB_COLLISION_OFF ? "开启" : "关闭"; String fineMark = FaultGuard.isFineDegraded() ? "(已自动降级)" : ""; // 全局始终默认启用,不提供开关;FaultGuard 若触发全局降级,其信息已写入 reason。 String reason = FaultGuard.getReason(); ServerLevel level = ctx.getSource().getLevel(); ResourceKey dimension = level.dimension(); String msg = String.format( "modzuozhi 状态:细粒度=%s%s,分片并行=%s,禁碰撞=%s,故障原因:%s\n" + "兼容状态:%s\n" + "当前维度:%s,掉拍分片累计:实体 %d / TE %d / 区块环境 %d", fine, fineMark, EntityTickParallel.isSharded() ? "开启" : "关闭", noCollide, reason, SableMainThreadBridge.statusReason(), dimensionName(dimension), EntityTickParallel.missedShards(), ShardGate.teMissed(), ShardGate.chunkMissed()); ctx.getSource().sendSuccess(() -> Component.literal(msg), false); ctx.getSource().sendSuccess(() -> Component.literal("线程交接:" + dev.modzuozhi.core.compat.ThreadOwnershipDiagnostics.timing()), false); ctx.getSource().sendSuccess(() -> Component.literal( dev.modzuozhi.core.compat.PayloadDiagnostics.snapshot(ctx.getSource().getServer())), false); var loops = DimThreadCore.MANAGER.loopsSnapshot(); if (loops.isEmpty()) { ctx.getSource().sendSuccess(() -> Component.literal("维度循环:无数据(尚未启动或已停止),不按 20 TPS 显示。"), false); } else { ctx.getSource().sendSuccess(() -> Component.literal( "维度实测:完成TPS按最近10秒计数(启动不足10秒用已观测时间,含暂停时间);" + "工作MSPT为最近100次均值/最近一次,包含交接等待,不含末尾区块队列轮询。"), false); long now = System.nanoTime(); loops.entrySet().stream().sorted(java.util.Comparator.comparing(e -> e.getKey().dimension().location().toString())) .forEach(entry -> { var loop = entry.getValue(); var p = loop.progress(now); String state = !loop.isRunning() ? "已停止" : loop.isPaused() ? (loop.isTicking() ? "暂停收口中" : "已暂停") : !loop.isScheduled() ? "未启动" : p.windowSeconds() == 0 ? "无采样" : p.runningMs() >= 1000 ? "当前Tick超过1秒" : p.sinceCompletedMs() >= 1000 ? "超过1秒无完成" : Double.isNaN(p.sinceCompletedMs()) ? "尚无成功完成" : "运行中"; String line = String.format(java.util.Locale.ROOT, "%s [%s] 完成TPS=%s (窗口%.1fs),工作MSPT(均/末)=%s/%s ms,当前Tick=%s ms,距上次完成=%s ms,失败Tick=%d", entry.getKey().dimension().location(), state, loop.isPaused() || !loop.isRunning() ? "—" : metric(p.tps()), p.windowSeconds(), metric(p.averageWorkMs()), metric(p.lastWorkMs()), Double.isNaN(p.runningMs()) ? "未执行" : metric(p.runningMs()), Double.isNaN(p.sinceCompletedMs()) ? "无记录" : metric(p.sinceCompletedMs()), p.failures()); ctx.getSource().sendSuccess(() -> Component.literal(line), false); }); } return 1; } private static String metric(double value) { return Double.isNaN(value) ? "无数据" : String.format(java.util.Locale.ROOT, "%.1f", value); } /** 把维度 key 映射成玩家可读的中文名。 */ private static String dimensionName(ResourceKey dimension) { return switch (dimension.location().getPath()) { case "overworld" -> "主世界"; case "the_nether" -> "下界"; case "the_end" -> "末地"; default -> dimension.location().toString(); }; } }