From e5e46b3ee986b05e85f213d1d939dd1ba15303c6 Mon Sep 17 00:00:00 2001 From: 3944Realms Date: Wed, 1 Apr 2026 12:37:22 +0800 Subject: [PATCH] =?UTF-8?q?=E5=B0=9D=E8=AF=95=E4=BF=AE=E5=A4=8D=E9=85=8D?= =?UTF-8?q?=E7=BD=AE=E4=B8=8D=E5=90=8C=E6=AD=A5=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- forge-mod/build.gradle | 4 + forge-mod/gradle.properties | 2 +- .../crossmod/CrossTeleportMod.java | 2 +- .../client/overlay/CrossServerTipOverLay.java | 7 +- .../crossmod/command/GotoServerCommand.java | 3 +- .../config/CrossServerConfigManager.java | 24 +++++- .../crossmod/network/PingRequestManager.java | 5 ++ .../toClient/SyncCommonConfigPacket.java | 75 ++++++++++++++++--- 8 files changed, 99 insertions(+), 23 deletions(-) diff --git a/forge-mod/build.gradle b/forge-mod/build.gradle index 21fd4f4..0ebc126 100644 --- a/forge-mod/build.gradle +++ b/forge-mod/build.gradle @@ -71,6 +71,10 @@ legacyForge { server() systemProperty 'forge.enabledGameTestNamespaces', mod_id } + gameTestServer { + type = "gameTestServer" + systemProperty 'forge.enabledGameTestNamespaces', mod_id + } } mods { "${mod_id}" { diff --git a/forge-mod/gradle.properties b/forge-mod/gradle.properties index 1e20597..ebbdea3 100644 --- a/forge-mod/gradle.properties +++ b/forge-mod/gradle.properties @@ -49,7 +49,7 @@ mod_name=Leisure Time Dock Mod # The license of the mod. Review your options at https://choosealicense.com/. All Rights Reserved is the default. mod_license=MIT # The mod version. See https://semver.org/ -mod_version=0.0.1.3 +mod_version=1.0.0 # The group ID for the mod. It is only important when publishing as an artifact to a Maven repository. # This should match the base package used for the mod sources. # See https://maven.apache.org/guides/mini/guide-naming-conventions.html diff --git a/forge-mod/src/main/java/com/leisuretimedock/crossmod/CrossTeleportMod.java b/forge-mod/src/main/java/com/leisuretimedock/crossmod/CrossTeleportMod.java index 2413bd1..99a2014 100644 --- a/forge-mod/src/main/java/com/leisuretimedock/crossmod/CrossTeleportMod.java +++ b/forge-mod/src/main/java/com/leisuretimedock/crossmod/CrossTeleportMod.java @@ -6,7 +6,6 @@ import com.leisuretimedock.crossmod.config.CrossServerConfigManager; import com.leisuretimedock.crossmod.command.PingCommand; import com.leisuretimedock.crossmod.network.NetworkHandler; import com.leisuretimedock.crossmod.network.PingRequestManager; -import com.leisuretimedock.crossmod.network.toClient.GotoServerPayload; import com.leisuretimedock.crossmod.reset.ClientResetManager; import lombok.extern.slf4j.Slf4j; import net.minecraft.server.MinecraftServer; @@ -84,6 +83,7 @@ public class CrossTeleportMod { } @SubscribeEvent public static void onServerStop(ServerStoppedEvent event) { + PingRequestManager.close(); server = null; } public static ServerPlayer getPlayerByUUID(UUID uuid) { diff --git a/forge-mod/src/main/java/com/leisuretimedock/crossmod/client/overlay/CrossServerTipOverLay.java b/forge-mod/src/main/java/com/leisuretimedock/crossmod/client/overlay/CrossServerTipOverLay.java index fd0c08e..255194f 100644 --- a/forge-mod/src/main/java/com/leisuretimedock/crossmod/client/overlay/CrossServerTipOverLay.java +++ b/forge-mod/src/main/java/com/leisuretimedock/crossmod/client/overlay/CrossServerTipOverLay.java @@ -5,16 +5,12 @@ import com.mojang.blaze3d.vertex.PoseStack; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.Font; import net.minecraft.client.gui.GuiGraphics; -import net.minecraft.client.renderer.entity.ItemRenderer; -import net.minecraft.client.resources.language.I18n; import net.minecraft.network.chat.Component; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.Items; import net.minecraftforge.client.gui.overlay.ForgeGui; import net.minecraftforge.client.gui.overlay.IGuiOverlay; -import java.awt.*; - public class CrossServerTipOverLay implements IGuiOverlay { public static final CrossServerTipOverLay INSTANCE = new CrossServerTipOverLay(); private static boolean showOverlay = false; @@ -27,11 +23,10 @@ public class CrossServerTipOverLay implements IGuiOverlay { } @Override public void render(ForgeGui forgeGui, GuiGraphics guiGraphics, float v, int i, int i1) { - if ( !showOverlay || mc.player == null || mc.level == null) return; + if (isShowOverlay()) return; int x = 10; int y = 10; Font font = mc.font; - ItemRenderer itemRenderer = mc.getItemRenderer(); // 1. 原版钟物品 ItemStack clockStack = new ItemStack(Items.CLOCK); diff --git a/forge-mod/src/main/java/com/leisuretimedock/crossmod/command/GotoServerCommand.java b/forge-mod/src/main/java/com/leisuretimedock/crossmod/command/GotoServerCommand.java index 02ee27b..6439cbe 100644 --- a/forge-mod/src/main/java/com/leisuretimedock/crossmod/command/GotoServerCommand.java +++ b/forge-mod/src/main/java/com/leisuretimedock/crossmod/command/GotoServerCommand.java @@ -10,13 +10,12 @@ import net.minecraft.commands.Commands; import net.minecraft.commands.arguments.EntityArgument; import net.minecraft.network.chat.Component; import net.minecraft.server.level.ServerPlayer; -import net.minecraft.world.entity.player.Player; import java.util.Collection; public class GotoServerCommand { public static void register(CommandDispatcher dispatcher) { - LiteralArgumentBuilder main = Commands.literal("server") + LiteralArgumentBuilder main = Commands.literal("cross") .requires(cs -> cs.hasPermission(2)) .then(Commands.argument("players", EntityArgument.players()) .then(Commands.literal("goto") diff --git a/forge-mod/src/main/java/com/leisuretimedock/crossmod/config/CrossServerConfigManager.java b/forge-mod/src/main/java/com/leisuretimedock/crossmod/config/CrossServerConfigManager.java index 351780f..42d30e9 100644 --- a/forge-mod/src/main/java/com/leisuretimedock/crossmod/config/CrossServerConfigManager.java +++ b/forge-mod/src/main/java/com/leisuretimedock/crossmod/config/CrossServerConfigManager.java @@ -53,7 +53,9 @@ public class CrossServerConfigManager { try { clear(); servers.putAll(parseServer(CrossServerConfig.SERVER_LIST.get())); + cacheHash = -1; cacheTag = serializeToNBT(); + log.debug("Configs reloaded"); } catch (Exception e) { log.error("Failed to reload configs", e); cacheHash = -1; @@ -67,10 +69,15 @@ public class CrossServerConfigManager { */ public void clear() { servers.clear(); + cacheHash = -1; + cacheTag = null; } public synchronized CompoundTag serializeToNBT() { - if (cacheHash == calculateConfigHash() && cacheTag != null) return cacheTag; + int currentHash = calculateConfigHash(); + if (cacheTag != null && cacheHash == currentHash) { + return cacheTag; + } CompoundTag tag = new CompoundTag(); serializeMap(tag, "servers", this.servers); cacheHash = calculateConfigHash(); @@ -80,7 +87,8 @@ public class CrossServerConfigManager { private void serializeMap(CompoundTag parent, String key, @NotNull Map map) { CompoundTag mapTag = new CompoundTag(); - for (Map.Entry entry : map.entrySet()) { + TreeMap sortedMap = new TreeMap<>(map); + for (Map.Entry entry : sortedMap.entrySet()) { mapTag.putString(entry.getKey(), entry.getValue()); } parent.put(key, mapTag); @@ -93,16 +101,22 @@ public class CrossServerConfigManager { * @param tag the tag */ public void deserializeFromNBT(@NotNull CompoundTag tag) { + cacheHash = -1; + cacheTag = null; clear(); deserializeMap(tag, "servers", servers); + cacheTag = serializeToNBT(); } private void deserializeMap(@NotNull CompoundTag parent, String key, Map map) { if (parent.contains(key)) { CompoundTag mapTag = parent.getCompound(key); + TreeMap tempMap = new TreeMap<>(); for (String key_ : mapTag.getAllKeys()) { - map.put(key_, mapTag.getString(key_)); + tempMap.put(key_, mapTag.getString(key_)); } + map.clear(); + map.putAll(tempMap); } } @@ -136,7 +150,8 @@ public class CrossServerConfigManager { public int calculateConfigHash() { // 使用FNV-1a哈希算法 int hash = 0x811c9dc5; // FNV偏移基础值 - hash = fnv1aHashMap(hash, servers); + TreeMap sortedMap = new TreeMap<>(servers); + hash = fnv1aHashMap(hash, sortedMap); return hash; } @@ -155,6 +170,7 @@ public class CrossServerConfigManager { } return hash; } + /** * Broad hash packet. */ diff --git a/forge-mod/src/main/java/com/leisuretimedock/crossmod/network/PingRequestManager.java b/forge-mod/src/main/java/com/leisuretimedock/crossmod/network/PingRequestManager.java index d39cea6..4659589 100644 --- a/forge-mod/src/main/java/com/leisuretimedock/crossmod/network/PingRequestManager.java +++ b/forge-mod/src/main/java/com/leisuretimedock/crossmod/network/PingRequestManager.java @@ -376,6 +376,11 @@ public final class PingRequestManager { } } + public static void close() { + pingScheduler.shutdownNow(); + scheduler.shutdownNow(); + } + // ========== 数据结构 ========== private static class PlayerPingData { diff --git a/forge-mod/src/main/java/com/leisuretimedock/crossmod/network/toClient/SyncCommonConfigPacket.java b/forge-mod/src/main/java/com/leisuretimedock/crossmod/network/toClient/SyncCommonConfigPacket.java index 84c392c..4be1035 100644 --- a/forge-mod/src/main/java/com/leisuretimedock/crossmod/network/toClient/SyncCommonConfigPacket.java +++ b/forge-mod/src/main/java/com/leisuretimedock/crossmod/network/toClient/SyncCommonConfigPacket.java @@ -5,7 +5,11 @@ import lombok.extern.slf4j.Slf4j; import net.minecraft.nbt.CompoundTag; import net.minecraft.network.FriendlyByteBuf; import net.minecraftforge.network.NetworkEvent; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import java.util.Objects; +import java.util.Set; import java.util.function.Supplier; /** @@ -19,7 +23,7 @@ public record SyncCommonConfigPacket(CompoundTag config, int hash) { * @param msg the msg * @param buf the buf */ - public static void encode(SyncCommonConfigPacket msg, FriendlyByteBuf buf) { + public static void encode(@NotNull SyncCommonConfigPacket msg, @NotNull FriendlyByteBuf buf) { buf.writeNbt(msg.config); buf.writeInt(msg.hash); } @@ -30,7 +34,8 @@ public record SyncCommonConfigPacket(CompoundTag config, int hash) { * @param buf the buf * @return the packet eternal potato remove packet */ - public static SyncCommonConfigPacket decode(FriendlyByteBuf buf) { + @Contract("_ -> new") + public static @NotNull SyncCommonConfigPacket decode(@NotNull FriendlyByteBuf buf) { return new SyncCommonConfigPacket(buf.readNbt(), buf.readInt()); } @@ -40,16 +45,68 @@ public record SyncCommonConfigPacket(CompoundTag config, int hash) { * @param msg the msg * @param ctx the ctx */ - public static void handle(SyncCommonConfigPacket msg, Supplier ctx) { + public static void handle(SyncCommonConfigPacket msg, @NotNull Supplier ctx) { ctx.get().enqueueWork(() -> { - CompoundTag old = CrossServerConfigManager.INSTANCE.serializeToNBT(); - CrossServerConfigManager.INSTANCE.deserializeFromNBT(msg.config); - int newHashCode = CrossServerConfigManager.INSTANCE.calculateConfigHash(); - if (newHashCode != msg.hash) { //BACK - log.error("Hash mismatch! Except:{}, Actual:{}", msg.hash, newHashCode); - CrossServerConfigManager.INSTANCE.deserializeFromNBT(old); + CrossServerConfigManager manager = CrossServerConfigManager.INSTANCE; + + // 1. 保存当前配置(强制重新序列化,不使用缓存) + CompoundTag currentConfig = manager.serializeToNBT(); + int currentHash = manager.calculateConfigHash(); + + // 2. 应用新配置 + manager.deserializeFromNBT(msg.config); + + // 3. 验证哈希 + int newHash = manager.calculateConfigHash(); + if (newHash != msg.hash) { + log.error("Hash mismatch! Expected: {}, Actual: {}", msg.hash, newHash); + log.error("Current hash before deserialization: {}", currentHash); + + // 打印差异详情 + if (currentConfig != null) { + compareConfigs(currentConfig, msg.config); + manager.deserializeFromNBT(currentConfig); + } + + // 验证恢复是否成功 + int restoredHash = manager.calculateConfigHash(); + if (restoredHash != currentHash) { + log.error("Failed to restore config! Hash mismatch after rollback!"); + } else { + log.info("Successfully rolled back to previous config"); + } + } else { + log.debug("Config sync successful, hash: {}", msg.hash); } }); ctx.get().setPacketHandled(true); } + + private static void compareConfigs(@NotNull CompoundTag oldConfig, @NotNull CompoundTag newConfig) { + Set oldKeys = oldConfig.getAllKeys(); + Set newKeys = newConfig.getAllKeys(); + + // 找出只存在于旧配置的键 + for (String key : oldKeys) { + if (!newConfig.contains(key)) { + log.warn("Key only in old config: {}", key); + } + } + + // 找出只存在于新配置的键 + for (String key : newKeys) { + if (!oldConfig.contains(key)) { + log.warn("Key only in new config: {}", key); + } + } + + // 比较共同键的值 + for (String key : oldKeys) { + if (newConfig.contains(key) && !Objects.equals(oldConfig.get(key), newConfig.get(key))) { + log.warn("Value mismatch for key: {}", key); + log.warn(" Old: {}", oldConfig.get(key)); + log.warn(" New: {}", newConfig.get(key)); + } + } + } }