Remove Carpet features patches and protocols
Delete Carpet integration: remove luminol and minecraft Carpet feature patch files (1todos/server/luminol-patches/features/0006-Carpet-features.patch and 1todos/server/minecraft-patches/features/0044-Carpet-features.patch) and remove related protocol implementations (1todos/server/src/lophine/protocol/CarpetLoggerProtocol.java and 1todos/server/src/lophine/protocol/tiscm/TISCMProtocol.java). These files contained the Carpet feature backport/compat patches and protocol helpers; removing them cleans up the obsolete Carpet-specific changes.
This commit is contained in:
@@ -1,24 +0,0 @@
|
|||||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
||||||
From: Bacteriawa <A3167717663@hotmail.com>
|
|
||||||
Date: Sun, 22 Mar 2026 01:39:17 +0800
|
|
||||||
Subject: [PATCH] Carpet features
|
|
||||||
|
|
||||||
|
|
||||||
diff --git a/src/main/java/me/earthme/luminol/config/ConfigManager.java b/src/main/java/me/earthme/luminol/config/ConfigManager.java
|
|
||||||
index f5fb4e210c07f32e52eb8c1ba166d1571a6778c7..e990c147e1b632ef5a33e4f3c37b5cd608040ac7 100644
|
|
||||||
--- a/src/main/java/me/earthme/luminol/config/ConfigManager.java
|
|
||||||
+++ b/src/main/java/me/earthme/luminol/config/ConfigManager.java
|
|
||||||
@@ -25,6 +25,13 @@ public class ConfigManager {
|
|
||||||
public static void initConfigs() {
|
|
||||||
configfiles.put("luminol", ConfigsInstance.of("luminol", "me.earthme.luminol.config.modules"));
|
|
||||||
configfiles.put("lophine", ConfigsInstance.of("lophine", "fun.bm.lophine.config.modules")); // add lophine global config
|
|
||||||
+ configfiles.put("lophine_carpet", ConfigsInstance.of(
|
|
||||||
+ new java.io.File("lophine_config"),
|
|
||||||
+ "lophine_carpet",
|
|
||||||
+ "lophine_carpet_config.toml",
|
|
||||||
+ "lophinecarpetconfig",
|
|
||||||
+ "fun.bm.lophine.carpet.config.modules"
|
|
||||||
+ )); // add lophine carpet config
|
|
||||||
preLoad();
|
|
||||||
}
|
|
||||||
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,302 +0,0 @@
|
|||||||
package fun.bm.lophine.protocol;
|
|
||||||
|
|
||||||
import fun.bm.lophine.carpet.config.modules.GeneralCompatConfig;
|
|
||||||
import io.papermc.paper.adventure.PaperAdventure;
|
|
||||||
import io.papermc.paper.threadedregions.RegionizedWorldData;
|
|
||||||
import io.papermc.paper.threadedregions.TickRegionScheduler;
|
|
||||||
import net.kyori.adventure.text.Component;
|
|
||||||
import net.minecraft.ChatFormatting;
|
|
||||||
import net.minecraft.network.chat.MutableComponent;
|
|
||||||
import net.minecraft.network.protocol.game.ClientboundTabListPacket;
|
|
||||||
import net.minecraft.server.MinecraftServer;
|
|
||||||
import net.minecraft.server.ServerTickRateManager;
|
|
||||||
import net.minecraft.server.level.ServerLevel;
|
|
||||||
import net.minecraft.server.level.ServerPlayer;
|
|
||||||
import net.minecraft.world.entity.LivingEntity;
|
|
||||||
import net.minecraft.world.entity.MobCategory;
|
|
||||||
import net.minecraft.world.item.DyeColor;
|
|
||||||
import net.minecraft.world.level.Level;
|
|
||||||
import net.minecraft.world.level.NaturalSpawner;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import org.leavesmc.leaves.protocol.core.LeavesProtocol;
|
|
||||||
import org.leavesmc.leaves.protocol.core.ProtocolHandler;
|
|
||||||
import org.leavesmc.leaves.util.HopperCounter;
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import java.util.*;
|
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
|
||||||
|
|
||||||
@LeavesProtocol.Register(namespace = "carpet")
|
|
||||||
public class CarpetLoggerProtocol implements LeavesProtocol {
|
|
||||||
private static final Logger LOGGER = LoggerFactory.getLogger("CarpetLoggerProtocol");
|
|
||||||
private static final Map<String, Map<String, String>> PLAYER_SUBSCRIPTIONS = new ConcurrentHashMap<>();
|
|
||||||
private static volatile Map<String, String> configuredSubscriptions = null;
|
|
||||||
|
|
||||||
public static void refreshConfiguredDefaults(boolean initial) {
|
|
||||||
Map<String, String> defaults = parseConfiguredDefaults(GeneralCompatConfig.defaultLoggers);
|
|
||||||
configuredSubscriptions = defaults;
|
|
||||||
if (defaults == null || defaults.isEmpty()) {
|
|
||||||
PLAYER_SUBSCRIPTIONS.clear();
|
|
||||||
if (initial) return;
|
|
||||||
for (ServerPlayer player : MinecraftServer.getServer().getPlayerList().getPlayers()) {
|
|
||||||
clearHud(player);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
PLAYER_SUBSCRIPTIONS.replaceAll((playerName, ignored) -> defaults);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String serializeConfiguredDefaults(List<String> configuredLoggers) {
|
|
||||||
List<String> serialized = new ArrayList<>();
|
|
||||||
if (configuredLoggers != null) {
|
|
||||||
for (String entry : configuredLoggers) {
|
|
||||||
if (entry == null) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
String trimmed = entry.trim();
|
|
||||||
if (!trimmed.isEmpty()) {
|
|
||||||
serialized.add(trimmed);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return serialized.isEmpty() ? "none" : String.join(",", serialized);
|
|
||||||
}
|
|
||||||
|
|
||||||
@ProtocolHandler.PlayerJoin
|
|
||||||
public static void onPlayerJoin(ServerPlayer player) {
|
|
||||||
if (configuredSubscriptions != null && !configuredSubscriptions.isEmpty()) {
|
|
||||||
PLAYER_SUBSCRIPTIONS.putIfAbsent(player.getScoreboardName(), configuredSubscriptions);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@ProtocolHandler.PlayerLeave
|
|
||||||
public static void onPlayerLeave(ServerPlayer player) {
|
|
||||||
clearHud(player);
|
|
||||||
}
|
|
||||||
|
|
||||||
@ProtocolHandler.Ticker(tickerId = "hud")
|
|
||||||
public static void onHudTick() {
|
|
||||||
if (PLAYER_SUBSCRIPTIONS.isEmpty()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
MinecraftServer server = MinecraftServer.getServer();
|
|
||||||
for (ServerPlayer player : server.getPlayerList().getPlayers()) {
|
|
||||||
Map<String, String> subscriptions = PLAYER_SUBSCRIPTIONS.get(player.getScoreboardName());
|
|
||||||
if (subscriptions == null || subscriptions.isEmpty()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
player.getBukkitEntity().taskScheduler.schedule((LivingEntity livingEntity) -> {
|
|
||||||
if (livingEntity instanceof ServerPlayer scheduledPlayer) {
|
|
||||||
sendHud(server, scheduledPlayer, subscriptions);
|
|
||||||
}
|
|
||||||
}, null, 1L);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isActive() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int tickerInterval(String tickerID) {
|
|
||||||
return "hud".equals(tickerID) ? 20 : 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void sendHud(MinecraftServer server, ServerPlayer player, Map<String, String> subscriptions) {
|
|
||||||
List<net.minecraft.network.chat.Component> lines = new ArrayList<>();
|
|
||||||
subscriptions.forEach((loggerName, option) -> {
|
|
||||||
switch (loggerName) {
|
|
||||||
case "tps" -> lines.add(buildTpsLine(server));
|
|
||||||
case "mobcaps" -> {
|
|
||||||
net.minecraft.network.chat.Component line = buildMobcapsLine(player, option);
|
|
||||||
if (line != null) {
|
|
||||||
lines.add(line);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case "counter" -> lines.addAll(buildCounterLines(server, option));
|
|
||||||
default -> {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
MutableComponent footer = net.minecraft.network.chat.Component.empty();
|
|
||||||
for (int i = 0; i < lines.size(); i++) {
|
|
||||||
if (i > 0) {
|
|
||||||
footer.append(net.minecraft.network.chat.Component.literal("\n"));
|
|
||||||
}
|
|
||||||
footer.append(lines.get(i));
|
|
||||||
}
|
|
||||||
player.connection.send(new ClientboundTabListPacket(net.minecraft.network.chat.Component.empty(), footer));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void clearHud(ServerPlayer player) {
|
|
||||||
player.connection.send(new ClientboundTabListPacket(net.minecraft.network.chat.Component.empty(), net.minecraft.network.chat.Component.empty()));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static net.minecraft.network.chat.Component buildTpsLine(MinecraftServer server) {
|
|
||||||
ServerTickRateManager tickManager = server.tickRateManager();
|
|
||||||
ca.spottedleaf.moonrise.common.time.TickData.TickReportData tickData = TickRegionScheduler.getCurrentRegion().getData().getRegionSchedulingHandle().getTickReport5s(System.nanoTime());
|
|
||||||
final double tps = tickData.tpsData().segmentAll().average();
|
|
||||||
final double mspt = tickData.timePerTickData().segmentAll().average() / 1.0E6;
|
|
||||||
|
|
||||||
ChatFormatting color = heatmapColor(mspt, tickManager.millisecondsPerTick());
|
|
||||||
return net.minecraft.network.chat.Component.empty()
|
|
||||||
.append(net.minecraft.network.chat.Component.literal("TPS: ").withStyle(ChatFormatting.GRAY))
|
|
||||||
.append(net.minecraft.network.chat.Component.literal(String.format(Locale.US, "%.1f", tps)).withStyle(color))
|
|
||||||
.append(net.minecraft.network.chat.Component.literal(" MSPT: ").withStyle(ChatFormatting.GRAY))
|
|
||||||
.append(net.minecraft.network.chat.Component.literal(String.format(Locale.US, "%.1f", mspt)).withStyle(color));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static net.minecraft.network.chat.Component buildMobcapsLine(ServerPlayer player, String option) {
|
|
||||||
final ServerLevel level = resolveMobcapsLevel(player, option);
|
|
||||||
if (level == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
final RegionizedWorldData data = level.getCurrentWorldData();
|
|
||||||
if (data == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
final NaturalSpawner.SpawnState spawnState = data.lastSpawnState;
|
|
||||||
if (spawnState == null) {
|
|
||||||
return net.minecraft.network.chat.Component.literal("Mobcaps: unavailable").withStyle(ChatFormatting.DARK_GRAY);
|
|
||||||
}
|
|
||||||
|
|
||||||
int chunks = spawnState.getSpawnableChunkCount();
|
|
||||||
var counts = spawnState.getMobCategoryCounts();
|
|
||||||
MutableComponent line = net.minecraft.network.chat.Component.literal("Mobcaps").withStyle(ChatFormatting.GRAY);
|
|
||||||
for (MobCategory category : MobCategory.values()) {
|
|
||||||
if (category == MobCategory.MISC) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
int current = counts.getOrDefault(category, 0);
|
|
||||||
int limit = NaturalSpawner.globalLimitForCategory(level, category, chunks);
|
|
||||||
line.append(net.minecraft.network.chat.Component.literal(" " + shortName(category) + " ").withStyle(ChatFormatting.DARK_GRAY));
|
|
||||||
line.append(net.minecraft.network.chat.Component.literal(current + "/" + limit).withStyle(categoryColor(current, limit)));
|
|
||||||
}
|
|
||||||
return line;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static List<net.minecraft.network.chat.Component> buildCounterLines(MinecraftServer server, String option) {
|
|
||||||
List<net.minecraft.network.chat.Component> lines = new ArrayList<>();
|
|
||||||
String colors = option == null || option.isBlank() ? "white" : option;
|
|
||||||
for (String rawColor : colors.split(",")) {
|
|
||||||
String colorName = rawColor.trim();
|
|
||||||
if (colorName.isEmpty()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
DyeColor color = DyeColor.byName(colorName, null);
|
|
||||||
if (color == null) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
HopperCounter counter = HopperCounter.getCounter(color);
|
|
||||||
if (counter == null) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
for (Component component : counter.format(server, false)) {
|
|
||||||
lines.add(PaperAdventure.asVanilla(component));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return lines;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static ServerLevel resolveMobcapsLevel(ServerPlayer player, String option) {
|
|
||||||
if (option == null || option.isBlank() || option.equalsIgnoreCase("dynamic")) {
|
|
||||||
return player.level();
|
|
||||||
}
|
|
||||||
return switch (option.toLowerCase(Locale.ROOT)) {
|
|
||||||
case "overworld" -> player.level().dimension() == Level.OVERWORLD ? player.level() : null;
|
|
||||||
case "nether" -> player.level().dimension() == Level.NETHER ? player.level() : null;
|
|
||||||
case "end" -> player.level().dimension() == Level.END ? player.level() : null;
|
|
||||||
default -> player.level();
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private static ChatFormatting heatmapColor(double actual, double reference) {
|
|
||||||
if (actual > reference) {
|
|
||||||
return ChatFormatting.LIGHT_PURPLE;
|
|
||||||
}
|
|
||||||
if (actual > 0.8D * reference) {
|
|
||||||
return ChatFormatting.RED;
|
|
||||||
}
|
|
||||||
if (actual > 0.5D * reference) {
|
|
||||||
return ChatFormatting.YELLOW;
|
|
||||||
}
|
|
||||||
if (actual >= 0.0D) {
|
|
||||||
return ChatFormatting.DARK_GREEN;
|
|
||||||
}
|
|
||||||
return ChatFormatting.GRAY;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static ChatFormatting categoryColor(int current, int limit) {
|
|
||||||
if (limit <= 0) {
|
|
||||||
return ChatFormatting.DARK_GRAY;
|
|
||||||
}
|
|
||||||
double ratio = current / (double) limit;
|
|
||||||
if (ratio >= 1.0D) {
|
|
||||||
return ChatFormatting.RED;
|
|
||||||
}
|
|
||||||
if (ratio >= 0.8D) {
|
|
||||||
return ChatFormatting.YELLOW;
|
|
||||||
}
|
|
||||||
return ChatFormatting.GREEN;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String shortName(MobCategory category) {
|
|
||||||
return switch (category) {
|
|
||||||
case MONSTER -> "M";
|
|
||||||
case CREATURE -> "C";
|
|
||||||
case AMBIENT -> "A";
|
|
||||||
case AXOLOTLS -> "Ax";
|
|
||||||
case UNDERGROUND_WATER_CREATURE -> "UWC";
|
|
||||||
case WATER_CREATURE -> "WC";
|
|
||||||
case WATER_AMBIENT -> "WA";
|
|
||||||
case MISC -> "X";
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Map<String, String> parseConfiguredDefaults(List<String> configuredLoggers) {
|
|
||||||
LinkedHashMap<String, String> subscriptions = new LinkedHashMap<>();
|
|
||||||
if (configuredLoggers == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
for (String entry : configuredLoggers) {
|
|
||||||
if (entry == null) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
for (String chunk : entry.split(",")) {
|
|
||||||
String token = chunk.trim();
|
|
||||||
if (token.isEmpty() || token.equalsIgnoreCase("none")) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
String[] parts = token.split("\\s+", 2);
|
|
||||||
String loggerName = parts[0].toLowerCase(Locale.ROOT);
|
|
||||||
if (!isSupported(loggerName)) {
|
|
||||||
LOGGER.debug("Ignoring unsupported Carpet default logger '{}'", loggerName);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
String option = parts.length == 1 ? defaultOption(loggerName) : parts[1].trim();
|
|
||||||
if (option.isEmpty()) {
|
|
||||||
option = defaultOption(loggerName);
|
|
||||||
}
|
|
||||||
subscriptions.put(loggerName, option);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return subscriptions.isEmpty() ? null : Map.copyOf(subscriptions);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean isSupported(String loggerName) {
|
|
||||||
return Arrays.asList("tps", "mobcaps", "counter").contains(loggerName);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static @NotNull String defaultOption(String loggerName) {
|
|
||||||
return switch (loggerName) {
|
|
||||||
case "mobcaps" -> "dynamic";
|
|
||||||
case "counter" -> "white";
|
|
||||||
default -> "";
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,201 +0,0 @@
|
|||||||
package fun.bm.lophine.protocol.tiscm;
|
|
||||||
|
|
||||||
import com.mojang.logging.LogUtils;
|
|
||||||
import fun.bm.lophine.carpet.config.modules.GeneralCompatConfig;
|
|
||||||
import io.papermc.paper.ServerBuildInfo;
|
|
||||||
import net.minecraft.nbt.CompoundTag;
|
|
||||||
import net.minecraft.nbt.ListTag;
|
|
||||||
import net.minecraft.nbt.StringTag;
|
|
||||||
import net.minecraft.network.RegistryFriendlyByteBuf;
|
|
||||||
import net.minecraft.network.codec.ByteBufCodecs;
|
|
||||||
import net.minecraft.network.codec.StreamCodec;
|
|
||||||
import net.minecraft.resources.Identifier;
|
|
||||||
import net.minecraft.server.MinecraftServer;
|
|
||||||
import net.minecraft.server.level.ServerPlayer;
|
|
||||||
import org.jetbrains.annotations.Contract;
|
|
||||||
import org.leavesmc.leaves.protocol.core.LeavesCustomPayload;
|
|
||||||
import org.leavesmc.leaves.protocol.core.LeavesProtocol;
|
|
||||||
import org.leavesmc.leaves.protocol.core.ProtocolHandler;
|
|
||||||
import org.leavesmc.leaves.protocol.core.ProtocolUtils;
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
|
|
||||||
import java.util.*;
|
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
@LeavesProtocol.Register(namespace = TISCMProtocol.PROTOCOL_ID)
|
|
||||||
public class TISCMProtocol implements LeavesProtocol {
|
|
||||||
private static final Logger LOGGER = LogUtils.getLogger();
|
|
||||||
|
|
||||||
public static final String PROTOCOL_ID = "tiscm";
|
|
||||||
private static final String PLATFORM_NAME = "Lophine";
|
|
||||||
private static final String PLATFORM_VERSION = ServerBuildInfo.buildInfo().asString(ServerBuildInfo.StringRepresentation.VERSION_SIMPLE);
|
|
||||||
private static final Map<String, EnumSet<S2CPacket>> CLIENT_SUPPORTED_PACKETS = new ConcurrentHashMap<>();
|
|
||||||
|
|
||||||
@Contract("_ -> new")
|
|
||||||
public static Identifier id(String path) {
|
|
||||||
return Identifier.fromNamespaceAndPath(PROTOCOL_ID, path);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void broadcastMsptSample(long tickCounter, long nanosecond) {
|
|
||||||
if (!GeneralCompatConfig.tiscmNetworkProtocol || !GeneralCompatConfig.syncServerMsptMetricsData) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
CompoundTag nbt = new CompoundTag();
|
|
||||||
nbt.putInt("version", 2);
|
|
||||||
nbt.putLong("millisecond", nanosecond / 1_000_000L);
|
|
||||||
nbt.putLong("nanosecond", nanosecond);
|
|
||||||
nbt.putString("type", "tick_server_method");
|
|
||||||
|
|
||||||
TISCMPayload payload = new TISCMPayload(S2CPacket.MSPT_METRICS_SAMPLE.id, nbt);
|
|
||||||
for (ServerPlayer player : MinecraftServer.getServer().getPlayerList().getPlayers()) {
|
|
||||||
if (supports(player, S2CPacket.MSPT_METRICS_SAMPLE)) {
|
|
||||||
ProtocolUtils.sendPayloadPacket(player, payload);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@ProtocolHandler.PayloadReceiver(payload = TISCMPayload.class)
|
|
||||||
public static void handlePayload(ServerPlayer player, TISCMPayload payload) {
|
|
||||||
if (!GeneralCompatConfig.tiscmNetworkProtocol) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Optional<C2SPacket> packetType = C2SPacket.fromId(payload.packetId());
|
|
||||||
if (packetType.isEmpty()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (packetType.get()) {
|
|
||||||
case HI -> handleHi(player, payload.nbt());
|
|
||||||
case SUPPORTED_S2C_PACKETS -> handleSupportedS2CPackets(player, payload.nbt());
|
|
||||||
default -> {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@ProtocolHandler.PlayerLeave
|
|
||||||
public static void onPlayerLeave(ServerPlayer player) {
|
|
||||||
CLIENT_SUPPORTED_PACKETS.remove(player.getStringUUID());
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void handleHi(ServerPlayer player, CompoundTag payload) {
|
|
||||||
String platformName = payload.getString("platform_name").orElse("Unknown");
|
|
||||||
String platformVersion = payload.getString("platform_version").orElse("Unknown");
|
|
||||||
LOGGER.info("Player {} connected with TISCM protocol support ({} @ {})", player.getScoreboardName(), platformName, platformVersion);
|
|
||||||
|
|
||||||
send(player, S2CPacket.HELLO, nbt -> {
|
|
||||||
nbt.putString("platform_name", PLATFORM_NAME);
|
|
||||||
nbt.putString("platform_version", PLATFORM_VERSION);
|
|
||||||
});
|
|
||||||
send(player, S2CPacket.SUPPORTED_C2S_PACKETS, nbt -> nbt.put("supported_c2s_packets", stringList(List.of(
|
|
||||||
C2SPacket.HI.id,
|
|
||||||
C2SPacket.SUPPORTED_S2C_PACKETS.id
|
|
||||||
))));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void handleSupportedS2CPackets(ServerPlayer player, CompoundTag payload) {
|
|
||||||
EnumSet<S2CPacket> packets = EnumSet.noneOf(S2CPacket.class);
|
|
||||||
ListTag listTag = payload.getListOrEmpty("supported_s2c_packets");
|
|
||||||
for (int i = 0; i < listTag.size(); i++) {
|
|
||||||
S2CPacket.fromId(listTag.getString(i).orElse("")).ifPresent(packets::add);
|
|
||||||
}
|
|
||||||
CLIENT_SUPPORTED_PACKETS.put(player.getStringUUID(), packets);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void send(ServerPlayer player, S2CPacket packet, PayloadBuilder builder) {
|
|
||||||
if (!supports(player, packet)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
CompoundTag nbt = new CompoundTag();
|
|
||||||
builder.accept(nbt);
|
|
||||||
ProtocolUtils.sendPayloadPacket(player, new TISCMPayload(packet.id, nbt));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean supports(ServerPlayer player, S2CPacket packet) {
|
|
||||||
if (packet.handshake) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
EnumSet<S2CPacket> packets = CLIENT_SUPPORTED_PACKETS.get(player.getStringUUID());
|
|
||||||
return packets != null && packets.contains(packet);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static ListTag stringList(List<String> values) {
|
|
||||||
ListTag list = new ListTag();
|
|
||||||
for (String value : values) {
|
|
||||||
list.add(StringTag.valueOf(value));
|
|
||||||
}
|
|
||||||
return list;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isActive() {
|
|
||||||
return GeneralCompatConfig.tiscmNetworkProtocol;
|
|
||||||
}
|
|
||||||
|
|
||||||
private interface PayloadBuilder {
|
|
||||||
void accept(CompoundTag nbt);
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum C2SPacket {
|
|
||||||
HI("hi", true),
|
|
||||||
SUPPORTED_S2C_PACKETS("supported_s2c_packets", true),
|
|
||||||
SPEED_TEST_UPLOAD_PAYLOAD("speed_test_upload_payload", false),
|
|
||||||
SPEED_TEST_PING("speed_test_ping", false);
|
|
||||||
|
|
||||||
private static final Map<String, C2SPacket> BY_ID = Arrays.stream(values()).collect(Collectors.toMap(packet -> packet.id, packet -> packet));
|
|
||||||
|
|
||||||
private final String id;
|
|
||||||
private final boolean handshake;
|
|
||||||
|
|
||||||
C2SPacket(String id, boolean handshake) {
|
|
||||||
this.id = id;
|
|
||||||
this.handshake = handshake;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Optional<C2SPacket> fromId(String id) {
|
|
||||||
return Optional.ofNullable(BY_ID.get(id));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum S2CPacket {
|
|
||||||
HELLO("hello", true),
|
|
||||||
SUPPORTED_C2S_PACKETS("supported_c2s_packets", true),
|
|
||||||
MSPT_METRICS_SAMPLE("mspt_metrics_sample", false),
|
|
||||||
SPEED_TEST_DOWNLOAD_PAYLOAD("speed_test_download_payload", false),
|
|
||||||
SPEED_TEST_UPLOAD_REQUEST("speed_test_upload_request", false),
|
|
||||||
SPEED_TEST_PING("speed_test_ping", false),
|
|
||||||
SPEED_TEST_ABORT("speed_test_abort", false);
|
|
||||||
|
|
||||||
private static final Map<String, S2CPacket> BY_ID = Arrays.stream(values()).collect(Collectors.toMap(packet -> packet.id, packet -> packet));
|
|
||||||
|
|
||||||
private final String id;
|
|
||||||
private final boolean handshake;
|
|
||||||
|
|
||||||
S2CPacket(String id, boolean handshake) {
|
|
||||||
this.id = id;
|
|
||||||
this.handshake = handshake;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Optional<S2CPacket> fromId(String id) {
|
|
||||||
return Optional.ofNullable(BY_ID.get(id));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public record TISCMPayload(String packetId, CompoundTag nbt) implements LeavesCustomPayload {
|
|
||||||
@ID
|
|
||||||
private static final Identifier NETWORK_ID = TISCMProtocol.id("network/v1");
|
|
||||||
|
|
||||||
@Codec
|
|
||||||
private static final StreamCodec<RegistryFriendlyByteBuf, TISCMPayload> CODEC = StreamCodec.composite(
|
|
||||||
ByteBufCodecs.STRING_UTF8,
|
|
||||||
TISCMPayload::packetId,
|
|
||||||
ByteBufCodecs.COMPOUND_TAG,
|
|
||||||
TISCMPayload::nbt,
|
|
||||||
TISCMPayload::new
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user