From ac6cd5c1b55c95b39ef7e53fa241db4f9e19598f Mon Sep 17 00:00:00 2001
From: Ap_Tx <3045929398@qq.com>
Date: Tue, 6 Oct 2026 19:34:48 +0800
Subject: [PATCH] =?UTF-8?q?feat:=20=E6=A6=9C=E5=8D=95=E5=AE=A2=E6=88=B7?=
=?UTF-8?q?=E7=AB=AF=EF=BC=88=E6=8E=A5=E6=94=B6=E5=8C=85+/slsboard?=
=?UTF-8?q?=E5=91=BD=E4=BB=A4+=E5=8F=B3=E4=BE=A7HUD=EF=BC=89?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
BoardClient注册客户端入口:接收BoardSyncPayload、/slsboard deaths/mined/off切换显示、HudElementRegistry注册右侧HUD。各玩家本地状态独立,不同步。
---
.../carpetslsaddition/client/BoardClient.java | 69 ++++++++++++++++++
.../client/BoardClientState.java | 58 +++++++++++++++
.../client/BoardHudElement.java | 70 +++++++++++++++++++
src/main/resources/fabric.mod.json | 3 +
4 files changed, 200 insertions(+)
create mode 100644 src/main/java/com/github/zly2006/carpetslsaddition/client/BoardClient.java
create mode 100644 src/main/java/com/github/zly2006/carpetslsaddition/client/BoardClientState.java
create mode 100644 src/main/java/com/github/zly2006/carpetslsaddition/client/BoardHudElement.java
diff --git a/src/main/java/com/github/zly2006/carpetslsaddition/client/BoardClient.java b/src/main/java/com/github/zly2006/carpetslsaddition/client/BoardClient.java
new file mode 100644
index 0000000..12a70bb
--- /dev/null
+++ b/src/main/java/com/github/zly2006/carpetslsaddition/client/BoardClient.java
@@ -0,0 +1,69 @@
+package com.github.zly2006.carpetslsaddition.client;
+
+import com.github.zly2006.carpetslsaddition.net.BoardSyncPayload;
+import com.mojang.brigadier.arguments.StringArgumentType;
+import net.fabricmc.api.ClientModInitializer;
+import net.fabricmc.api.EnvType;
+import net.fabricmc.api.Environment;
+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.network.chat.Component;
+import net.minecraft.resources.Identifier;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 客户端入口:接收榜单数据、注册 /slsboard 命令、注册 HUD 渲染。
+ *
+ *
纯客户端功能:显示哪个榜由本地状态决定,各玩家可不同步。
+ */
+@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()));
+ }
+ context.client().execute(() -> BoardClientState.setBoard(payload.board(), entries));
+ });
+
+ // 2) /slsboard 命令(纯客户端,非 OP 也能用)
+ ClientCommandRegistrationCallback.EVENT.register((dispatcher, registryAccess) -> {
+ dispatcher.register(
+ net.minecraft.commands.Commands.literal("slsboard")
+ .then(net.minecraft.commands.Commands.literal("deaths")
+ .executes(c -> { BoardClientState.toggle("deaths"); feedback(c.getSource()); return 1; }))
+ .then(net.minecraft.commands.Commands.literal("mined")
+ .executes(c -> { BoardClientState.toggle("mined"); feedback(c.getSource()); return 1; }))
+ .then(net.minecraft.commands.Commands.literal("off")
+ .executes(c -> { BoardClientState.set(""); feedback(c.getSource()); return 1; }))
+ .then(net.minecraft.commands.Commands.argument("board", StringArgumentType.word())
+ .suggests((c, b) -> net.minecraft.commands.SharedSuggestionProvider.suggest(
+ new String[]{"deaths", "mined", "off"}, b))
+ .executes(c -> {
+ String v = StringArgumentType.getString(c, "board");
+ BoardClientState.set("off".equals(v) ? "" : v);
+ feedback(c.getSource());
+ return 1;
+ }))
+ );
+ });
+
+ // 3) HUD 渲染
+ HudElementRegistry.addLast(HUD_ID, new BoardHudElement());
+ }
+
+ private static void feedback(FabricClientCommandSource source) {
+ String d = BoardClientState.displaying;
+ source.sendFeedback(Component.literal("[SLSA] 榜单显示:" + (d.isEmpty() ? "关闭" : d)));
+ }
+}
\ 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
new file mode 100644
index 0000000..9c11532
--- /dev/null
+++ b/src/main/java/com/github/zly2006/carpetslsaddition/client/BoardClientState.java
@@ -0,0 +1,58 @@
+package com.github.zly2006.carpetslsaddition.client;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * 客户端榜单状态(纯客户端,各玩家独立,不同步)。
+ *
+ *
+ * - {@link #displaying}:当前显示哪个榜("" = 不显示)。
+ * - {@code deaths}/{@code mined}:从服务端收到的排行数据缓存。
+ *
+ */
+public final class BoardClientState {
+
+ private BoardClientState() {}
+
+ /** 当前显示的榜:"" / "deaths" / "mined"。 */
+ public static volatile String displaying = "";
+
+ 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
new file mode 100644
index 0000000..eb586c4
--- /dev/null
+++ b/src/main/java/com/github/zly2006/carpetslsaddition/client/BoardHudElement.java
@@ -0,0 +1,70 @@
+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();
+ Font font = mc.font;
+
+ 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 x = screenW - maxW - PAD * 2 - 2;
+ int y = 4;
+
+ // 背景
+ int h = PAD * 2 + LINE_H * (list.size() + 1);
+ 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/resources/fabric.mod.json b/src/main/resources/fabric.mod.json
index 4101bd8..f23a221 100644
--- a/src/main/resources/fabric.mod.json
+++ b/src/main/resources/fabric.mod.json
@@ -19,6 +19,9 @@
"entrypoints": {
"main": [
"com.github.zly2006.carpetslsaddition.ServerMain"
+ ],
+ "client": [
+ "com.github.zly2006.carpetslsaddition.client.BoardClient"
]
},
"accessWidener": "slsa.accesswidener",