feat: /slsboard改发请求包,服务端给该玩家单独发display包(per-player显示)
客户端本地setDisplayObjective会被服务端覆盖,改用C2S请求包,服务端单独发ClientboundSetDisplayObjectivePacket给请求者。
This commit is contained in:
1 parent
d3bd73f7c5
commit
5001b2f5a2
4 files changed
+69
-31
No files matched your search
@@ -43,6 +43,13 @@ public class ServerMain implements ModInitializer, CarpetExtension {
|
||||
LOGGER.info("[SLSA] onInitialize: managing Carpet extension");
|
||||
INSTANCE = this;
|
||||
CarpetServer.manageExtension(this);
|
||||
com.github.zly2006.carpetslsaddition.net.BoardRequestPayload.register();
|
||||
// 客户端请求显示榜单 → 给该玩家单独发 display 包
|
||||
net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking.registerGlobalReceiver(
|
||||
com.github.zly2006.carpetslsaddition.net.BoardRequestPayload.TYPE,
|
||||
(payload, context) -> context.server().execute(() ->
|
||||
com.github.zly2006.carpetslsaddition.util.BoardManager.onClientRequest(
|
||||
context.player(), payload.objectiveName())));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.github.zly2006.carpetslsaddition.client;
|
||||
|
||||
import com.github.zly2006.carpetslsaddition.net.BoardRequestPayload;
|
||||
import com.mojang.brigadier.arguments.StringArgumentType;
|
||||
import net.fabricmc.api.ClientModInitializer;
|
||||
import net.fabricmc.api.EnvType;
|
||||
@@ -7,15 +8,14 @@ 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.minecraft.commands.SharedSuggestionProvider;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.world.scores.DisplaySlot;
|
||||
import net.minecraft.world.scores.Objective;
|
||||
|
||||
/**
|
||||
* 客户端入口:/slsboard 命令,控制本地计分板侧边栏显示哪个榜。
|
||||
* 客户端入口:/slsboard 命令,请求服务端给自己显示某个榜单。
|
||||
*
|
||||
* <p>利用原版计分板侧边栏渲染(可靠),各玩家本地切换、互不影响。
|
||||
* <p>服务端收到请求后,给该玩家单独发 display 包,实现各玩家各自显示、互不影响。
|
||||
* 数据由服务端从 stats 同步到 scoreboard objective(见 BoardManager)。
|
||||
*/
|
||||
@Environment(EnvType.CLIENT)
|
||||
@@ -26,42 +26,24 @@ public class BoardClient implements ClientModInitializer {
|
||||
ClientCommandRegistrationCallback.EVENT.register((dispatcher, registryAccess) -> {
|
||||
dispatcher.register(
|
||||
ClientCommands.literal("slsboard")
|
||||
.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.literal("deaths").executes(c -> request(c.getSource(), "sls_deaths")))
|
||||
.then(ClientCommands.literal("mined").executes(c -> request(c.getSource(), "sls_mined")))
|
||||
.then(ClientCommands.literal("off").executes(c -> request(c.getSource(), "")))
|
||||
.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");
|
||||
if ("off".equals(v)) { clear(c.getSource()); return 1; }
|
||||
return show(c.getSource(), "sls_" + v);
|
||||
return request(c.getSource(), "off".equals(v) ? "" : "sls_" + v);
|
||||
}))
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
private static int show(FabricClientCommandSource source, String objectiveName) {
|
||||
var mc = source.getClient();
|
||||
var level = mc.level;
|
||||
if (level == null) {
|
||||
return 0;
|
||||
}
|
||||
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));
|
||||
private static int request(FabricClientCommandSource source, String objectiveName) {
|
||||
ClientPlayNetworking.send(new BoardRequestPayload(objectiveName));
|
||||
source.sendFeedback(Component.literal(
|
||||
objectiveName.isEmpty() ? "[SLSA] 榜单已关闭" : "[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] 榜单已关闭"));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
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;
|
||||
import net.minecraft.resources.Identifier;
|
||||
|
||||
/**
|
||||
* 客户端 → 服务端:请求显示某个榜单(或关闭)。
|
||||
*
|
||||
* <p>服务端收到后,给该玩家单独发送 {@code ClientboundSetDisplayObjectivePacket},
|
||||
* 实现"各玩家各自显示、互不影响"。
|
||||
*
|
||||
* <p>{@code objectiveName} 为空字符串表示关闭。
|
||||
*/
|
||||
public record BoardRequestPayload(String objectiveName) implements CustomPacketPayload {
|
||||
|
||||
public static final CustomPacketPayload.Type<BoardRequestPayload> TYPE =
|
||||
new CustomPacketPayload.Type<>(Identifier.fromNamespaceAndPath("slsa", "board_request"));
|
||||
|
||||
public static final StreamCodec<RegistryFriendlyByteBuf, BoardRequestPayload> CODEC = StreamCodec.composite(
|
||||
ByteBufCodecs.STRING_UTF8, BoardRequestPayload::objectiveName,
|
||||
BoardRequestPayload::new
|
||||
);
|
||||
|
||||
@Override
|
||||
public CustomPacketPayload.Type<? extends CustomPacketPayload> type() {
|
||||
return TYPE;
|
||||
}
|
||||
|
||||
public static void register() {
|
||||
net.fabricmc.fabric.api.networking.v1.PayloadTypeRegistry.serverboundPlay().register(TYPE, CODEC);
|
||||
}
|
||||
}
|
||||
@@ -95,4 +95,18 @@ public final class BoardManager {
|
||||
setScore(sb, mined, player, totalMined(counter));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** 客户端请求显示某榜单 → 给该玩家单独发 display 包(不影响其他玩家)。 */
|
||||
public static void onClientRequest(ServerPlayer player, String objectiveName) {
|
||||
MinecraftServer server = ServerMain.server;
|
||||
if (server == null) {
|
||||
return;
|
||||
}
|
||||
Objective obj = objectiveName == null || objectiveName.isEmpty()
|
||||
? null
|
||||
: server.getScoreboard().getObjective(objectiveName);
|
||||
// 直接给该玩家发 display 包(slot=SIDEBAR,objective 可为 null 表示清除)
|
||||
player.connection.send(new net.minecraft.network.protocol.game.ClientboundSetDisplayObjectivePacket(
|
||||
net.minecraft.world.scores.DisplaySlot.SIDEBAR, obj));
|
||||
}
|
||||
}\n
|
||||
Reference in new issue
Block a user