尝试修复配置不同步问题
This commit is contained in:
parent
790c55c4c0
commit
e5e46b3ee9
|
|
@ -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}" {
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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) {
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -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")
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -376,6 +376,11 @@ public final class PingRequestManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void close() {
|
||||||
|
pingScheduler.shutdownNow();
|
||||||
|
scheduler.shutdownNow();
|
||||||
|
}
|
||||||
|
|
||||||
// ========== 数据结构 ==========
|
// ========== 数据结构 ==========
|
||||||
|
|
||||||
private static class PlayerPingData {
|
private static class PlayerPingData {
|
||||||
|
|
|
||||||
|
|
@ -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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user