diff --git a/src/main/java/com/github/zly2006/carpetslsaddition/ServerMain.java b/src/main/java/com/github/zly2006/carpetslsaddition/ServerMain.java index a92c863..31da6ca 100644 --- a/src/main/java/com/github/zly2006/carpetslsaddition/ServerMain.java +++ b/src/main/java/com/github/zly2006/carpetslsaddition/ServerMain.java @@ -43,12 +43,6 @@ public class ServerMain implements ModInitializer, CarpetExtension { LOGGER.info("[SLSA] onInitialize: managing Carpet extension"); INSTANCE = this; CarpetServer.manageExtension(this); - com.github.zly2006.carpetslsaddition.net.BoardSyncPayload.register(); - com.github.zly2006.carpetslsaddition.net.BoardTogglePayload.register(); - // 玩家加入时同步榜单 - net.fabricmc.fabric.api.networking.v1.ServerPlayConnectionEvents.JOIN.register( - (handler, sender, srv) -> - com.github.zly2006.carpetslsaddition.util.BoardSyncManager.syncTo(handler.getPlayer())); } @Override @@ -60,7 +54,7 @@ public class ServerMain implements ModInitializer, CarpetExtension { public void onServerLoaded(MinecraftServer server) { ServerMain.server = server; com.github.zly2006.carpetslsaddition.util.BotListManager.load(); - com.github.zly2006.carpetslsaddition.util.BoardSyncManager.syncToAll(server); + com.github.zly2006.carpetslsaddition.util.BoardManager.ensureObjectives(server); startBoardWatcher(server); } @@ -69,7 +63,7 @@ public class ServerMain implements ModInitializer, CarpetExtension { Thread t = new Thread(() -> { while (!Thread.currentThread().isInterrupted()) { try { - com.github.zly2006.carpetslsaddition.util.BoardSyncManager.tick(server); + com.github.zly2006.carpetslsaddition.util.BoardManager.syncScores(server); Thread.sleep(1000L); } catch (InterruptedException e) { return; diff --git a/src/main/java/com/github/zly2006/carpetslsaddition/client/BoardClient.java b/src/main/java/com/github/zly2006/carpetslsaddition/client/BoardClient.java index 38199ac..0f945bd 100644 --- a/src/main/java/com/github/zly2006/carpetslsaddition/client/BoardClient.java +++ b/src/main/java/com/github/zly2006/carpetslsaddition/client/BoardClient.java @@ -1,7 +1,5 @@ package com.github.zly2006.carpetslsaddition.client; -import com.github.zly2006.carpetslsaddition.net.BoardSyncPayload; -import com.github.zly2006.carpetslsaddition.net.BoardTogglePayload; import com.mojang.brigadier.arguments.StringArgumentType; import net.fabricmc.api.ClientModInitializer; import net.fabricmc.api.EnvType; @@ -9,75 +7,61 @@ import net.fabricmc.api.Environment; import net.fabricmc.fabric.api.client.command.v2.ClientCommands; import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback; import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; -import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking; -import net.fabricmc.fabric.api.client.rendering.v1.hud.HudElementRegistry; import net.minecraft.commands.SharedSuggestionProvider; import net.minecraft.network.chat.Component; -import net.minecraft.resources.Identifier; - -import java.util.ArrayList; -import java.util.List; +import net.minecraft.world.scores.DisplaySlot; +import net.minecraft.world.scores.Objective; /** - * 客户端入口:接收榜单数据、注册 /slsboard 命令、注册 HUD 渲染。 + * 客户端入口:/slsboard 命令,控制本地计分板侧边栏显示哪个榜。 * - *

纯客户端功能:显示哪个榜由本地状态决定,各玩家可不同步。 + *

利用原版计分板侧边栏渲染(可靠),各玩家本地切换、互不影响。 + * 数据由服务端从 stats 同步到 scoreboard objective(见 BoardManager)。 */ @Environment(EnvType.CLIENT) public class BoardClient implements ClientModInitializer { - public static final Identifier HUD_ID = Identifier.fromNamespaceAndPath("slsa", "board_hud"); - @Override public void onInitializeClient() { - // 1) 接收服务端下发的榜单数据 - ClientPlayNetworking.registerGlobalReceiver(BoardSyncPayload.TYPE, (payload, context) -> { - List entries = new ArrayList<>(); - for (BoardSyncPayload.Entry e : payload.entries()) { - entries.add(new BoardClientState.Entry(e.name(), e.score())); - } - com.github.zly2006.carpetslsaddition.ServerMain.LOGGER.info("[SLSA-BOARD-C] recv board={} entries={}", payload.board(), entries.size()); - context.client().execute(() -> BoardClientState.setBoard(payload.board(), entries)); - }); - - // 接收开关状态 - ClientPlayNetworking.registerGlobalReceiver(BoardTogglePayload.TYPE, (payload, context) -> { - context.client().execute(() -> BoardClientState.enabled = payload.enabled()); - }); - - // 2) /slsboard 命令(纯客户端,非 OP 也能用) ClientCommandRegistrationCallback.EVENT.register((dispatcher, registryAccess) -> { dispatcher.register( ClientCommands.literal("slsboard") - .then(ClientCommands.literal("deaths") - .executes(c -> run(c.getSource(), () -> BoardClientState.toggle("deaths")))) - .then(ClientCommands.literal("mined") - .executes(c -> run(c.getSource(), () -> BoardClientState.toggle("mined")))) - .then(ClientCommands.literal("off") - .executes(c -> run(c.getSource(), () -> BoardClientState.set("")))) + .then(ClientCommands.literal("deaths").executes(c -> show(c.getSource(), "sls_deaths"))) + .then(ClientCommands.literal("mined").executes(c -> show(c.getSource(), "sls_mined"))) + .then(ClientCommands.literal("off").executes(c -> { clear(c.getSource()); return 1; })) .then(ClientCommands.argument("board", StringArgumentType.word()) .suggests((c, b) -> SharedSuggestionProvider.suggest( new String[]{"deaths", "mined", "off"}, b)) .executes(c -> { String v = StringArgumentType.getString(c, "board"); - return run(c.getSource(), () -> BoardClientState.set("off".equals(v) ? "" : v)); + if ("off".equals(v)) { clear(c.getSource()); return 1; } + return show(c.getSource(), "sls_" + v); })) ); }); - - // 3) HUD 渲染 - HudElementRegistry.addLast(HUD_ID, new BoardHudElement()); } - /** 统一的命令入口:先检查服务端开关,再执行动作。 */ - private static int run(FabricClientCommandSource source, Runnable action) { - if (!BoardClientState.enabled) { - source.sendError(Component.literal("[SLSA] 榜单功能未开启(服务端 /carpet slsBoardEnabled true)")); + private static int show(FabricClientCommandSource source, String objectiveName) { + var mc = source.getClient(); + var level = mc.level; + if (level == null) { return 0; } - action.run(); - String d = BoardClientState.displaying; - source.sendFeedback(Component.literal("[SLSA] 榜单显示:" + (d.isEmpty() ? "关闭" : d))); + Objective obj = level.getScoreboard().getObjective(objectiveName); + if (obj == null) { + source.sendError(Component.literal("[SLSA] 找不到计分板 " + objectiveName + "(服务端规则未开启?)")); + return 0; + } + level.getScoreboard().setDisplayObjective(DisplaySlot.SIDEBAR, obj); + source.sendFeedback(Component.literal("[SLSA] 榜单显示:" + objectiveName)); return 1; } + + private static void clear(FabricClientCommandSource source) { + var level = source.getClient().level; + if (level != null) { + level.getScoreboard().setDisplayObjective(DisplaySlot.SIDEBAR, null); + } + source.sendFeedback(Component.literal("[SLSA] 榜单已关闭")); + } } \ No newline at end of file diff --git a/src/main/java/com/github/zly2006/carpetslsaddition/client/BoardClientState.java b/src/main/java/com/github/zly2006/carpetslsaddition/client/BoardClientState.java deleted file mode 100644 index 30b6610..0000000 --- a/src/main/java/com/github/zly2006/carpetslsaddition/client/BoardClientState.java +++ /dev/null @@ -1,61 +0,0 @@ -package com.github.zly2006.carpetslsaddition.client; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -/** - * 客户端榜单状态(纯客户端,各玩家独立,不同步)。 - * - *

- */ -public final class BoardClientState { - - private BoardClientState() {} - - /** 当前显示的榜:"" / "deaths" / "mined"。 */ - public static volatile String displaying = ""; - - /** 服务端 Carpet 规则 slsBoardEnabled 的状态(未开启则禁止使用)。 */ - public static volatile boolean enabled = false; - - private static volatile List deaths = Collections.emptyList(); - private static volatile List mined = Collections.emptyList(); - - public record Entry(String name, int score) {} - - public static void setBoard(String board, List entries) { - if ("deaths".equals(board)) { - deaths = entries; - } else if ("mined".equals(board)) { - mined = entries; - } - } - - public static List current() { - return switch (displaying) { - case "deaths" -> deaths; - case "mined" -> mined; - default -> Collections.emptyList(); - }; - } - - public static void toggle(String board) { - displaying = displaying.equals(board) ? "" : board; - } - - public static void set(String board) { - displaying = board; - } - - public static String currentTitle() { - return switch (displaying) { - case "deaths" -> "死亡榜"; - case "mined" -> "挖掘榜"; - default -> ""; - }; - } -} \ No newline at end of file diff --git a/src/main/java/com/github/zly2006/carpetslsaddition/client/BoardHudElement.java b/src/main/java/com/github/zly2006/carpetslsaddition/client/BoardHudElement.java deleted file mode 100644 index 87e4a17..0000000 --- a/src/main/java/com/github/zly2006/carpetslsaddition/client/BoardHudElement.java +++ /dev/null @@ -1,76 +0,0 @@ -package com.github.zly2006.carpetslsaddition.client; - -import net.fabricmc.api.EnvType; -import net.fabricmc.api.Environment; -import net.fabricmc.fabric.api.client.rendering.v1.hud.HudElement; -import net.minecraft.client.DeltaTracker; -import net.minecraft.client.Minecraft; -import net.minecraft.client.gui.Font; -import net.minecraft.client.gui.GuiGraphicsExtractor; -import net.minecraft.network.chat.Component; - -import java.util.List; - -/** - * 右侧排行榜 HUD:显示死亡榜/挖掘榜的 Top N。 - * - *

纯客户端渲染,内容来自 {@link BoardClientState}(服务端同步而来)。 - */ -@Environment(EnvType.CLIENT) -public class BoardHudElement implements HudElement { - - private static final int LINE_H = 10; - private static final int PAD = 4; - private static final int BG = 0x80000000; // 半透明黑 - private static final int TITLE_COLOR = 0xFFFF55; // 黄 - private static final int TEXT_COLOR = 0xFFFFFF; // 白 - - @Override - public void extractRenderState(GuiGraphicsExtractor g, DeltaTracker delta) { - String board = BoardClientState.displaying; - if (board == null || board.isEmpty()) { - return; - } - Minecraft mc = Minecraft.getInstance(); - if (mc.player == null || mc.options.hideGui) { - return; - } - List list = BoardClientState.current(); - if (list.isEmpty()) { - return; - } - Font font = mc.font; - - // 关键:进入新的渲染层,否则画的内容不显示 - g.nextStratum(); - - String title = BoardClientState.currentTitle(); - int maxW = font.width(title); - for (BoardClientState.Entry e : list) { - maxW = Math.max(maxW, font.width(line(e))); - } - - int screenW = g.guiWidth(); - int screenH = g.guiHeight(); - int x = screenW - maxW - PAD * 2 - 2; - // 背景(右侧垂直居中) - int h = PAD * 2 + LINE_H * (list.size() + 1); - int y = screenH / 2 - h / 2; - g.fill(x, y, x + maxW + PAD * 2, y + h, BG); - - int ty = y + PAD; - g.text(font, Component.literal(title), x + PAD, ty, TITLE_COLOR); - ty += LINE_H; - for (int i = 0; i < list.size(); i++) { - BoardClientState.Entry e = list.get(i); - g.text(font, Component.literal(line(e)), x + PAD, ty, TEXT_COLOR); - ty += LINE_H; - } - } - - private static String line(BoardClientState.Entry e) { - String name = e.name(); - if (name.length() > 12) name = name.substring(0, 12); - return name + " " + e.score(); - } -} diff --git a/src/main/java/com/github/zly2006/carpetslsaddition/net/BoardSyncPayload.java b/src/main/java/com/github/zly2006/carpetslsaddition/net/BoardSyncPayload.java deleted file mode 100644 index d032769..0000000 --- a/src/main/java/com/github/zly2006/carpetslsaddition/net/BoardSyncPayload.java +++ /dev/null @@ -1,47 +0,0 @@ -package com.github.zly2006.carpetslsaddition.net; - -import com.github.zly2006.carpetslsaddition.ServerMain; -import net.minecraft.network.RegistryFriendlyByteBuf; -import net.minecraft.network.codec.ByteBufCodecs; -import net.minecraft.network.codec.StreamCodec; -import net.minecraft.network.protocol.common.custom.CustomPacketPayload; - -import java.util.List; - -/** - * 服务端 → 客户端:同步排行榜数据。 - * - *

携带某一种榜单(死亡/挖掘)的完整排行:条目为 (玩家名, 分数),已按分数降序。 - * 客户端收到后缓存,供 HUD 渲染。 - */ -public record BoardSyncPayload(String board, List entries) implements CustomPacketPayload { - - /** 单条排行数据。 */ - public record Entry(String name, int score) { - public static final StreamCodec CODEC = StreamCodec.composite( - ByteBufCodecs.STRING_UTF8, Entry::name, - ByteBufCodecs.VAR_INT, Entry::score, - Entry::new - ); - } - - public static final CustomPacketPayload.Type TYPE = - new CustomPacketPayload.Type<>(net.minecraft.resources.Identifier.fromNamespaceAndPath("slsa", "board_sync")); - - public static final StreamCodec CODEC = StreamCodec.composite( - ByteBufCodecs.STRING_UTF8, BoardSyncPayload::board, - Entry.CODEC.apply(ByteBufCodecs.list()), BoardSyncPayload::entries, - BoardSyncPayload::new - ); - - @Override - public CustomPacketPayload.Type type() { - return TYPE; - } - - /** 在 onInitialize 里调用,注册 payload 类型。 */ - public static void register() { - net.fabricmc.fabric.api.networking.v1.PayloadTypeRegistry.clientboundPlay().register(TYPE, CODEC); - ServerMain.LOGGER.info("[SLSA] registered BoardSyncPayload"); - } -} diff --git a/src/main/java/com/github/zly2006/carpetslsaddition/net/BoardTogglePayload.java b/src/main/java/com/github/zly2006/carpetslsaddition/net/BoardTogglePayload.java deleted file mode 100644 index be40d41..0000000 --- a/src/main/java/com/github/zly2006/carpetslsaddition/net/BoardTogglePayload.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.github.zly2006.carpetslsaddition.net; - -import net.minecraft.network.RegistryFriendlyByteBuf; -import net.minecraft.network.codec.ByteBufCodecs; -import net.minecraft.network.codec.StreamCodec; -import net.minecraft.network.protocol.common.custom.CustomPacketPayload; - -/** - * 服务端 → 客户端:榜单功能总开关状态(对应 Carpet 规则 {@code slsBoardEnabled})。 - * - *

规则变化时下发;客户端收到后决定 {@code /slsboard} 是否可用。 - */ -public record BoardTogglePayload(boolean enabled) implements CustomPacketPayload { - - public static final CustomPacketPayload.Type TYPE = - new CustomPacketPayload.Type<>(net.minecraft.resources.Identifier.fromNamespaceAndPath("slsa", "board_toggle")); - - public static final StreamCodec CODEC = StreamCodec.composite( - ByteBufCodecs.BOOL, BoardTogglePayload::enabled, - BoardTogglePayload::new - ); - - @Override - public CustomPacketPayload.Type type() { - return TYPE; - } - - public static void register() { - net.fabricmc.fabric.api.networking.v1.PayloadTypeRegistry.clientboundPlay().register(TYPE, CODEC); - } -} \ No newline at end of file diff --git a/src/main/java/com/github/zly2006/carpetslsaddition/util/BoardManager.java b/src/main/java/com/github/zly2006/carpetslsaddition/util/BoardManager.java index f8ad4f6..cef23d6 100644 --- a/src/main/java/com/github/zly2006/carpetslsaddition/util/BoardManager.java +++ b/src/main/java/com/github/zly2006/carpetslsaddition/util/BoardManager.java @@ -4,6 +4,9 @@ import com.github.zly2006.carpetslsaddition.ServerMain; import net.minecraft.network.chat.Component; import net.minecraft.server.MinecraftServer; import net.minecraft.server.level.ServerPlayer; +import net.minecraft.stats.ServerStatsCounter; +import net.minecraft.stats.Stat; +import net.minecraft.stats.Stats; import net.minecraft.world.scores.Objective; import net.minecraft.world.scores.ScoreAccess; import net.minecraft.world.scores.ScoreHolder; @@ -13,12 +16,14 @@ import net.minecraft.world.scores.criteria.ObjectiveCriteria; /** * 死亡榜 / 挖掘榜的 scoreboard 管理器(服务端)。 * - *

    - *
  • {@code sls_deaths}:使用原版 {@link ObjectiveCriteria#DEATH_COUNT} 判据,自动统计死亡次数。
  • - *
  • {@code sls_mined}:使用 {@link ObjectiveCriteria#DUMMY} 判据,由 {@code BoardEvents} 在破坏方块时写入。
  • - *
+ *

数据源是 vanilla stats,但 stats 不会自动同步给其他客户端。 + * 因此本类定期把 stats 的聚合值写入 scoreboard objective, + * 客户端再用原版计分板侧边栏显示(scoreboard 会自动同步给所有客户端)。 * - *

objective 在服务器启动时自动创建;客户端会同步到这两个 objective,用于 HUD 显示。 + *

    + *
  • {@code sls_deaths}:死亡次数(DUMMY 判据,从 stats 的 {@link Stats#DEATHS} 写入)。
  • + *
  • {@code sls_mined}:挖掘总数(DUMMY 判据,从 stats 的 {@link Stats#BLOCK_MINED} 聚合)。
  • + *
*/ public final class BoardManager { @@ -31,41 +36,54 @@ public final class BoardManager { public static void ensureObjectives(MinecraftServer server) { Scoreboard sb = server.getScoreboard(); if (sb.getObjective(OBJ_DEATHS) == null) { - sb.addObjective( - OBJ_DEATHS, - ObjectiveCriteria.DEATH_COUNT, - Component.literal("死亡榜"), - ObjectiveCriteria.RenderType.INTEGER, - true, - null - ); + sb.addObjective(OBJ_DEATHS, ObjectiveCriteria.DUMMY, + Component.literal("死亡榜"), ObjectiveCriteria.RenderType.INTEGER, true, null); ServerMain.LOGGER.info("[SLSA] created scoreboard objective {}", OBJ_DEATHS); } if (sb.getObjective(OBJ_MINED) == null) { - sb.addObjective( - OBJ_MINED, - ObjectiveCriteria.DUMMY, - Component.literal("挖掘榜"), - ObjectiveCriteria.RenderType.INTEGER, - true, - null - ); + sb.addObjective(OBJ_MINED, ObjectiveCriteria.DUMMY, + Component.literal("挖掘榜"), ObjectiveCriteria.RenderType.INTEGER, true, null); ServerMain.LOGGER.info("[SLSA] created scoreboard objective {}", OBJ_MINED); } } - /** 玩家破坏一个方块 → 挖掘榜 +1。 */ - public static void onBlockMined(ServerPlayer player, int count) { - MinecraftServer server = ServerMain.server; - if (server == null) { - return; + /** 读取某玩家的挖掘总数(遍历 stats map 中 BLOCK_MINED 类型条目)。 */ + private static int totalMined(ServerStatsCounter counter) { + int sum = 0; + for (Stat stat : counter.stats.keySet()) { + if (stat.getType() == Stats.BLOCK_MINED) { + sum += counter.getValue(stat); + } } - Scoreboard sb = server.getScoreboard(); - Objective obj = sb.getObjective(OBJ_MINED); + return sum; + } + + /** 读取某玩家的死亡次数。 */ + private static int totalDeaths(ServerStatsCounter counter) { + return counter.getValue(Stats.CUSTOM, Stats.DEATHS); + } + + /** 把一个分数写入 objective(若不存在则创建/更新)。 */ + private static void setScore(Scoreboard sb, Objective obj, ServerPlayer player, int value) { if (obj == null) { return; } ScoreAccess access = sb.getOrCreatePlayerScore((ScoreHolder) player, obj); - access.add(count); + access.set(value); + } + + /** 从 stats 同步所有在线玩家的分数到 scoreboard(定期调用)。 */ + public static void syncScores(MinecraftServer server) { + Scoreboard sb = server.getScoreboard(); + Objective deaths = sb.getObjective(OBJ_DEATHS); + Objective mined = sb.getObjective(OBJ_MINED); + if (deaths == null && mined == null) { + return; + } + for (ServerPlayer player : server.getPlayerList().getPlayers()) { + ServerStatsCounter counter = player.getStats(); + setScore(sb, deaths, player, totalDeaths(counter)); + setScore(sb, mined, player, totalMined(counter)); + } } } \ No newline at end of file diff --git a/src/main/java/com/github/zly2006/carpetslsaddition/util/BoardSyncManager.java b/src/main/java/com/github/zly2006/carpetslsaddition/util/BoardSyncManager.java deleted file mode 100644 index 284bd91..0000000 --- a/src/main/java/com/github/zly2006/carpetslsaddition/util/BoardSyncManager.java +++ /dev/null @@ -1,114 +0,0 @@ -package com.github.zly2006.carpetslsaddition.util; - -import com.github.zly2006.carpetslsaddition.ServerMain; -import com.github.zly2006.carpetslsaddition.net.BoardSyncPayload; -import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking; -import net.minecraft.core.registries.BuiltInRegistries; -import net.minecraft.server.MinecraftServer; -import net.minecraft.server.level.ServerPlayer; -import net.minecraft.stats.ServerStatsCounter; -import net.minecraft.stats.Stat; -import net.minecraft.stats.Stats; -import net.minecraft.world.level.block.Block; - -import java.util.ArrayList; -import java.util.Comparator; -import java.util.List; - -/** - * 服务端:读取玩家原版统计(挖掘/死亡),打包排行榜发给客户端。 - * - *

数据源是 vanilla stats({@code stats/.json}),非 scoreboard。 - * 客户端原生拿不到别人的 stats,故由服务端读取后通过网络包 {@link BoardSyncPayload} 下发。 - */ -public final class BoardSyncManager { - - private BoardSyncManager() {} - - public static final String BOARD_DEATHS = "deaths"; - public static final String BOARD_MINED = "mined"; - - /** 排行榜最多显示条目数。 */ - public static final int TOP_N = 10; - - /** 读取某玩家的挖掘总数(遍历该玩家的 stats,累加 BLOCK_MINED 类型条目)。 */ - private static int totalMined(ServerStatsCounter counter) { - int sum = 0; - int cnt = 0; - for (Stat stat : counter.stats.keySet()) { - if (stat.getType() == Stats.BLOCK_MINED) { - cnt++; - sum += counter.getValue(stat); - } - } - com.github.zly2006.carpetslsaddition.ServerMain.LOGGER.info("[SLSA-BOARD] totalMined: mapSize={} minedTypes={} sum={}", counter.stats.size(), cnt, sum); - return sum; - } - /** 读取某玩家的死亡次数。 */ - private static int deaths(ServerStatsCounter counter) { - return counter.getValue(Stats.CUSTOM, Stats.DEATHS); - } - - /** 构建某榜单的排行数据(降序,取前 TOP_N)。 */ - public static List buildBoard(MinecraftServer server, String board) { - List list = new ArrayList<>(); - for (ServerPlayer player : server.getPlayerList().getPlayers()) { - ServerStatsCounter counter = player.getStats(); - int score = BOARD_MINED.equals(board) ? totalMined(counter) : deaths(counter); - if (score > 0) { - list.add(new BoardSyncPayload.Entry(player.getScoreboardName(), score)); - } - } - list.sort(Comparator.comparingInt(BoardSyncPayload.Entry::score).reversed()); - if (list.size() > TOP_N) { - return new ArrayList<>(list.subList(0, TOP_N)); - } - return list; - } - - /** 把两个榜单 + 开关状态同步给某玩家。 */ - public static void syncTo(ServerPlayer player) { - MinecraftServer server = ServerMain.server; - if (server == null) { - return; - } - boolean enabled = com.github.zly2006.carpetslsaddition.SLSCarpetSettings.slsBoardEnabled; - // 先发开关状态 - ServerPlayNetworking.send(player, new com.github.zly2006.carpetslsaddition.net.BoardTogglePayload(enabled)); - if (!enabled) { - return; - } - List d = buildBoard(server, BOARD_DEATHS); - List m = buildBoard(server, BOARD_MINED); - com.github.zly2006.carpetslsaddition.ServerMain.LOGGER.info("[SLSA-BOARD] syncTo {} deaths={} mined={}", player.getScoreboardName(), d.size(), m.size()); - ServerPlayNetworking.send(player, new BoardSyncPayload(BOARD_DEATHS, d)); - ServerPlayNetworking.send(player, new BoardSyncPayload(BOARD_MINED, m)); - } - - /** 同步给所有在线玩家。 */ - public static void syncToAll(MinecraftServer server) { - for (ServerPlayer player : server.getPlayerList().getPlayers()) { - syncTo(player); - } - } - - private static volatile Boolean lastEnabled = null; - private static long lastSyncTick = 0; - - /** 由轮询线程每秒调用:规则值变化时立即同步;否则每 5 秒定期同步一次数据。 */ - public static void tick(MinecraftServer server) { - boolean now = com.github.zly2006.carpetslsaddition.SLSCarpetSettings.slsBoardEnabled; - if (lastEnabled == null || lastEnabled != now) { - lastEnabled = now; - syncToAll(server); - return; - } - if (now) { - long tick = server.getTickCount(); - if (tick - lastSyncTick >= 100) { // 每 100 tick(5秒)同步一次数据 - lastSyncTick = tick; - syncToAll(server); - } - } - } -}