尝试修复配置不同步问题

This commit is contained in:
叁玖领域 2026-04-01 12:37:22 +08:00
parent 790c55c4c0
commit e5e46b3ee9
8 changed files with 99 additions and 23 deletions

View File

@ -71,6 +71,10 @@ legacyForge {
server() server()
systemProperty 'forge.enabledGameTestNamespaces', mod_id systemProperty 'forge.enabledGameTestNamespaces', mod_id
} }
gameTestServer {
type = "gameTestServer"
systemProperty 'forge.enabledGameTestNamespaces', mod_id
}
} }
mods { mods {
"${mod_id}" { "${mod_id}" {

View File

@ -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. # The license of the mod. Review your options at https://choosealicense.com/. All Rights Reserved is the default.
mod_license=MIT mod_license=MIT
# The mod version. See https://semver.org/ # 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. # 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. # This should match the base package used for the mod sources.
# See https://maven.apache.org/guides/mini/guide-naming-conventions.html # See https://maven.apache.org/guides/mini/guide-naming-conventions.html

View File

@ -6,7 +6,6 @@ import com.leisuretimedock.crossmod.config.CrossServerConfigManager;
import com.leisuretimedock.crossmod.command.PingCommand; import com.leisuretimedock.crossmod.command.PingCommand;
import com.leisuretimedock.crossmod.network.NetworkHandler; import com.leisuretimedock.crossmod.network.NetworkHandler;
import com.leisuretimedock.crossmod.network.PingRequestManager; import com.leisuretimedock.crossmod.network.PingRequestManager;
import com.leisuretimedock.crossmod.network.toClient.GotoServerPayload;
import com.leisuretimedock.crossmod.reset.ClientResetManager; import com.leisuretimedock.crossmod.reset.ClientResetManager;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import net.minecraft.server.MinecraftServer; import net.minecraft.server.MinecraftServer;
@ -84,6 +83,7 @@ public class CrossTeleportMod {
} }
@SubscribeEvent @SubscribeEvent
public static void onServerStop(ServerStoppedEvent event) { public static void onServerStop(ServerStoppedEvent event) {
PingRequestManager.close();
server = null; server = null;
} }
public static ServerPlayer getPlayerByUUID(UUID uuid) { public static ServerPlayer getPlayerByUUID(UUID uuid) {

View File

@ -5,16 +5,12 @@ import com.mojang.blaze3d.vertex.PoseStack;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.Font; import net.minecraft.client.gui.Font;
import net.minecraft.client.gui.GuiGraphics; 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.network.chat.Component;
import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Items; import net.minecraft.world.item.Items;
import net.minecraftforge.client.gui.overlay.ForgeGui; import net.minecraftforge.client.gui.overlay.ForgeGui;
import net.minecraftforge.client.gui.overlay.IGuiOverlay; import net.minecraftforge.client.gui.overlay.IGuiOverlay;
import java.awt.*;
public class CrossServerTipOverLay implements IGuiOverlay { public class CrossServerTipOverLay implements IGuiOverlay {
public static final CrossServerTipOverLay INSTANCE = new CrossServerTipOverLay(); public static final CrossServerTipOverLay INSTANCE = new CrossServerTipOverLay();
private static boolean showOverlay = false; private static boolean showOverlay = false;
@ -27,11 +23,10 @@ public class CrossServerTipOverLay implements IGuiOverlay {
} }
@Override @Override
public void render(ForgeGui forgeGui, GuiGraphics guiGraphics, float v, int i, int i1) { 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 x = 10;
int y = 10; int y = 10;
Font font = mc.font; Font font = mc.font;
ItemRenderer itemRenderer = mc.getItemRenderer();
// 1. 原版钟物品 // 1. 原版钟物品
ItemStack clockStack = new ItemStack(Items.CLOCK); ItemStack clockStack = new ItemStack(Items.CLOCK);

View File

@ -10,13 +10,12 @@ import net.minecraft.commands.Commands;
import net.minecraft.commands.arguments.EntityArgument; import net.minecraft.commands.arguments.EntityArgument;
import net.minecraft.network.chat.Component; import net.minecraft.network.chat.Component;
import net.minecraft.server.level.ServerPlayer; import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.entity.player.Player;
import java.util.Collection; import java.util.Collection;
public class GotoServerCommand { public class GotoServerCommand {
public static void register(CommandDispatcher<CommandSourceStack> dispatcher) { public static void register(CommandDispatcher<CommandSourceStack> dispatcher) {
LiteralArgumentBuilder<CommandSourceStack> main = Commands.literal("server") LiteralArgumentBuilder<CommandSourceStack> main = Commands.literal("cross")
.requires(cs -> cs.hasPermission(2)) .requires(cs -> cs.hasPermission(2))
.then(Commands.argument("players", EntityArgument.players()) .then(Commands.argument("players", EntityArgument.players())
.then(Commands.literal("goto") .then(Commands.literal("goto")

View File

@ -53,7 +53,9 @@ public class CrossServerConfigManager {
try { try {
clear(); clear();
servers.putAll(parseServer(CrossServerConfig.SERVER_LIST.get())); servers.putAll(parseServer(CrossServerConfig.SERVER_LIST.get()));
cacheHash = -1;
cacheTag = serializeToNBT(); cacheTag = serializeToNBT();
log.debug("Configs reloaded");
} catch (Exception e) { } catch (Exception e) {
log.error("Failed to reload configs", e); log.error("Failed to reload configs", e);
cacheHash = -1; cacheHash = -1;
@ -67,10 +69,15 @@ public class CrossServerConfigManager {
*/ */
public void clear() { public void clear() {
servers.clear(); servers.clear();
cacheHash = -1;
cacheTag = null;
} }
public synchronized CompoundTag serializeToNBT() { 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(); CompoundTag tag = new CompoundTag();
serializeMap(tag, "servers", this.servers); serializeMap(tag, "servers", this.servers);
cacheHash = calculateConfigHash(); cacheHash = calculateConfigHash();
@ -80,7 +87,8 @@ public class CrossServerConfigManager {
private void serializeMap(CompoundTag parent, String key, @NotNull Map<String, String> map) { private void serializeMap(CompoundTag parent, String key, @NotNull Map<String, String> map) {
CompoundTag mapTag = new CompoundTag(); CompoundTag mapTag = new CompoundTag();
for (Map.Entry<String,String> entry : map.entrySet()) { TreeMap<String, String> sortedMap = new TreeMap<>(map);
for (Map.Entry<String, String> entry : sortedMap.entrySet()) {
mapTag.putString(entry.getKey(), entry.getValue()); mapTag.putString(entry.getKey(), entry.getValue());
} }
parent.put(key, mapTag); parent.put(key, mapTag);
@ -93,16 +101,22 @@ public class CrossServerConfigManager {
* @param tag the tag * @param tag the tag
*/ */
public void deserializeFromNBT(@NotNull CompoundTag tag) { public void deserializeFromNBT(@NotNull CompoundTag tag) {
cacheHash = -1;
cacheTag = null;
clear(); clear();
deserializeMap(tag, "servers", servers); deserializeMap(tag, "servers", servers);
cacheTag = serializeToNBT();
} }
private void deserializeMap(@NotNull CompoundTag parent, String key, Map<String, String> map) { private void deserializeMap(@NotNull CompoundTag parent, String key, Map<String, String> map) {
if (parent.contains(key)) { if (parent.contains(key)) {
CompoundTag mapTag = parent.getCompound(key); CompoundTag mapTag = parent.getCompound(key);
TreeMap<String, String> tempMap = new TreeMap<>();
for (String key_ : mapTag.getAllKeys()) { 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() { public int calculateConfigHash() {
// 使用FNV-1a哈希算法 // 使用FNV-1a哈希算法
int hash = 0x811c9dc5; // FNV偏移基础值 int hash = 0x811c9dc5; // FNV偏移基础值
hash = fnv1aHashMap(hash, servers); TreeMap<String, String> sortedMap = new TreeMap<>(servers);
hash = fnv1aHashMap(hash, sortedMap);
return hash; return hash;
} }
@ -155,6 +170,7 @@ public class CrossServerConfigManager {
} }
return hash; return hash;
} }
/** /**
* Broad hash packet. * Broad hash packet.
*/ */

View File

@ -376,6 +376,11 @@ public final class PingRequestManager {
} }
} }
public static void close() {
pingScheduler.shutdownNow();
scheduler.shutdownNow();
}
// ========== 数据结构 ========== // ========== 数据结构 ==========
private static class PlayerPingData { private static class PlayerPingData {

View File

@ -5,7 +5,11 @@ import lombok.extern.slf4j.Slf4j;
import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.FriendlyByteBuf; import net.minecraft.network.FriendlyByteBuf;
import net.minecraftforge.network.NetworkEvent; 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; import java.util.function.Supplier;
/** /**
@ -19,7 +23,7 @@ public record SyncCommonConfigPacket(CompoundTag config, int hash) {
* @param msg the msg * @param msg the msg
* @param buf the buf * @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.writeNbt(msg.config);
buf.writeInt(msg.hash); buf.writeInt(msg.hash);
} }
@ -30,7 +34,8 @@ public record SyncCommonConfigPacket(CompoundTag config, int hash) {
* @param buf the buf * @param buf the buf
* @return the packet eternal potato remove packet * @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()); return new SyncCommonConfigPacket(buf.readNbt(), buf.readInt());
} }
@ -40,16 +45,68 @@ public record SyncCommonConfigPacket(CompoundTag config, int hash) {
* @param msg the msg * @param msg the msg
* @param ctx the ctx * @param ctx the ctx
*/ */
public static void handle(SyncCommonConfigPacket msg, Supplier<NetworkEvent.Context> ctx) { public static void handle(SyncCommonConfigPacket msg, @NotNull Supplier<NetworkEvent.Context> ctx) {
ctx.get().enqueueWork(() -> { ctx.get().enqueueWork(() -> {
CompoundTag old = CrossServerConfigManager.INSTANCE.serializeToNBT(); CrossServerConfigManager manager = CrossServerConfigManager.INSTANCE;
CrossServerConfigManager.INSTANCE.deserializeFromNBT(msg.config);
int newHashCode = CrossServerConfigManager.INSTANCE.calculateConfigHash(); // 1. 保存当前配置强制重新序列化不使用缓存
if (newHashCode != msg.hash) { //BACK CompoundTag currentConfig = manager.serializeToNBT();
log.error("Hash mismatch! Except:{}, Actual:{}", msg.hash, newHashCode); int currentHash = manager.calculateConfigHash();
CrossServerConfigManager.INSTANCE.deserializeFromNBT(old);
// 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); ctx.get().setPacketHandled(true);
} }
private static void compareConfigs(@NotNull CompoundTag oldConfig, @NotNull CompoundTag newConfig) {
Set<String> oldKeys = oldConfig.getAllKeys();
Set<String> 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));
}
}
}
} }