尝试修复配置不同步问题

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()
systemProperty 'forge.enabledGameTestNamespaces', mod_id
}
gameTestServer {
type = "gameTestServer"
systemProperty 'forge.enabledGameTestNamespaces', mod_id
}
}
mods {
"${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.
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

View File

@ -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) {

View File

@ -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);

View File

@ -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<CommandSourceStack> dispatcher) {
LiteralArgumentBuilder<CommandSourceStack> main = Commands.literal("server")
LiteralArgumentBuilder<CommandSourceStack> main = Commands.literal("cross")
.requires(cs -> cs.hasPermission(2))
.then(Commands.argument("players", EntityArgument.players())
.then(Commands.literal("goto")

View File

@ -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<String, String> map) {
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());
}
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<String, String> map) {
if (parent.contains(key)) {
CompoundTag mapTag = parent.getCompound(key);
TreeMap<String, String> 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<String, String> sortedMap = new TreeMap<>(servers);
hash = fnv1aHashMap(hash, sortedMap);
return hash;
}
@ -155,6 +170,7 @@ public class CrossServerConfigManager {
}
return hash;
}
/**
* 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 {

View File

@ -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<NetworkEvent.Context> ctx) {
public static void handle(SyncCommonConfigPacket msg, @NotNull Supplier<NetworkEvent.Context> 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<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));
}
}
}
}