版本更新 1.2.2 -> 1.2.3
修复拴绳配置同步问题
This commit is contained in:
parent
0b6412548f
commit
18d1d06d39
|
|
@ -123,6 +123,11 @@ legacyForge {
|
|||
server {
|
||||
server()
|
||||
}
|
||||
testServer {
|
||||
server()
|
||||
// 设置服务器运行目录
|
||||
gameDirectory = file('run/server')
|
||||
}
|
||||
data {
|
||||
data()
|
||||
programArguments.addAll '--mod', mod_id, '--all',
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ mod_name=Super Lead Rope
|
|||
# The license of the mod. Review your options at https://choosealicense.com/. All Rights Reserved is the default.
|
||||
mod_license=GPLv3
|
||||
# The mod version. See https://semver.org/
|
||||
mod_version=1.2.2
|
||||
mod_version=1.2.3
|
||||
# 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
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ import net.minecraftforge.fml.config.ModConfig;
|
|||
import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import top.r3944realms.superleadrope.compat.CurtainCompat;
|
||||
import top.r3944realms.superleadrope.compat.WayStoneCompat;
|
||||
import top.r3944realms.superleadrope.config.LeashCommonConfig;
|
||||
import top.r3944realms.superleadrope.core.register.*;
|
||||
|
|
@ -72,6 +73,7 @@ public class SuperLeadRope {
|
|||
ModLoadingContext modLoadingContext = ModLoadingContext.get();
|
||||
ConfigUtil.registerConfig(modLoadingContext, ModConfig.Type.COMMON, LeashCommonConfig.SPEC, c, "leash");
|
||||
WayStoneCompat.init();
|
||||
CurtainCompat.init();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -104,4 +104,11 @@ public interface IWorkSpaceHelper {
|
|||
* @return the leash state
|
||||
*/
|
||||
Optional<ILeashState> getLeashState(@NotNull Entity pEntity);
|
||||
|
||||
/**
|
||||
* Register fake player.
|
||||
*
|
||||
* @param classes the classes
|
||||
*/
|
||||
void registerFakePlayer(@NotNull Class<?>... classes);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,15 +29,11 @@ public class CurtainCompat{
|
|||
public final static boolean isModLoaded = ModList.get().isLoaded("curtain");
|
||||
|
||||
/**
|
||||
* Is not fake player boolean.
|
||||
*
|
||||
* @param player the player
|
||||
* @return the boolean
|
||||
* Init.
|
||||
*/
|
||||
public static boolean isNotFakePlayer(Player player) {
|
||||
public static void init() {
|
||||
if (isModLoaded) {
|
||||
return !(player instanceof EntityPlayerMPFake);
|
||||
FakePlayerJudge.registersFakePlayer(EntityPlayerMPFake.class);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
* Super Lead rope mod
|
||||
* Copyright (C) 2026 R3944Realms
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package top.r3944realms.superleadrope.compat;
|
||||
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* The type Fake player judge.
|
||||
*/
|
||||
public class FakePlayerJudge {
|
||||
private final static Set<Class<?>> fakePlayerClasses = new HashSet<>();
|
||||
|
||||
private static void check(final @NotNull Class<?> fakePlayerClass) throws IllegalArgumentException {
|
||||
if(fakePlayerClass.equals(Player.class) || fakePlayerClass.equals(ServerPlayer.class))
|
||||
throw new IllegalArgumentException("Player or ServerPlayer class cannot be used as FakePlayer");
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers fake player.
|
||||
*
|
||||
* @param fakePlayerClass the fake player class
|
||||
*/
|
||||
public static void registersFakePlayer(final Class<?> @NotNull ...fakePlayerClass) {
|
||||
for (Class<?> playerClass : fakePlayerClass) {
|
||||
check(playerClass);
|
||||
}
|
||||
fakePlayerClasses.addAll(List.of(fakePlayerClass));
|
||||
}
|
||||
|
||||
/**
|
||||
* Is fake player boolean.
|
||||
*
|
||||
* @param entity the entity
|
||||
* @return the boolean
|
||||
*/
|
||||
public static boolean isNotFakePlayer(@NotNull Entity entity) {
|
||||
if (!(entity instanceof Player)) return true;
|
||||
return !fakePlayerClasses.contains(entity.getClass());
|
||||
}
|
||||
}
|
||||
|
|
@ -228,7 +228,7 @@ public class LeashConfigManager {
|
|||
* @return the teleport whitelist
|
||||
*/
|
||||
// ================== 白名单 ==================
|
||||
public List<String> getTeleportWhitelist() { return Collections.unmodifiableList(teleportWhitelistCache); }
|
||||
public List<String> getTeleportWhitelist() { return new ArrayList<>(teleportWhitelistCache); }
|
||||
|
||||
/**
|
||||
* Is entity teleport allowed boolean.
|
||||
|
|
@ -358,7 +358,7 @@ public class LeashConfigManager {
|
|||
*
|
||||
* @return the axis elasticity
|
||||
*/
|
||||
public List<Double> getAxisElasticity() { return Collections.unmodifiableList(axisElasticity); }
|
||||
public List<Double> getAxisElasticity() { return new ArrayList<>(axisElasticity); }
|
||||
|
||||
/**
|
||||
* Gets x elasticity.
|
||||
|
|
@ -423,7 +423,7 @@ public class LeashConfigManager {
|
|||
maxForce = LeashCommonConfig.COMMON.maxForce.get();
|
||||
playerSpringFactor = LeashCommonConfig.COMMON.playerSpringFactor.get();
|
||||
mobSpringFactor = LeashCommonConfig.COMMON.mobSpringFactor.get();
|
||||
cacheHash = calculateConfigHash();
|
||||
cacheHash = -1;
|
||||
cacheTag = serializeToNBT();
|
||||
SuperLeadRope.logger.debug("Configs reloaded: {}", getStats());
|
||||
} catch (Exception e) {
|
||||
|
|
@ -476,7 +476,11 @@ public class LeashConfigManager {
|
|||
* @return the compound tag
|
||||
*/
|
||||
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();
|
||||
|
||||
// 序列化偏移映射
|
||||
|
|
@ -518,7 +522,7 @@ public class LeashConfigManager {
|
|||
tag.put("axis_elasticity", elasticityTag);
|
||||
|
||||
tag.putInt("max_leashes_per_entity", maxLeashesPerEntity);
|
||||
cacheHash = calculateConfigHash();
|
||||
cacheHash = currentHash;
|
||||
cacheTag = tag;
|
||||
|
||||
return tag;
|
||||
|
|
@ -554,7 +558,6 @@ public class LeashConfigManager {
|
|||
LeashCommonConfig.COMMON.maxForce.set(maxForce);
|
||||
LeashCommonConfig.COMMON.playerSpringFactor.set(playerSpringFactor);
|
||||
LeashCommonConfig.COMMON.mobSpringFactor.set(mobSpringFactor);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -617,6 +620,9 @@ public class LeashConfigManager {
|
|||
public void deserializeFromNBT(CompoundTag tag) {
|
||||
if (tag == null || tag.isEmpty()) return;
|
||||
|
||||
cacheHash = -1;
|
||||
cacheTag = null;
|
||||
|
||||
// 反序列化偏移映射
|
||||
if (tag.contains("offsets", Tag.TAG_COMPOUND)) {
|
||||
CompoundTag offsets = tag.getCompound("offsets");
|
||||
|
|
@ -635,7 +641,7 @@ public class LeashConfigManager {
|
|||
for (int i = 0; i < whitelistTag.size(); i++) {
|
||||
whitelist.add(whitelistTag.getString(i));
|
||||
}
|
||||
teleportWhitelistCache = Collections.unmodifiableList(whitelist);
|
||||
teleportWhitelistCache = new ArrayList<>(whitelist);
|
||||
}
|
||||
|
||||
if (tag.contains("command_prefix", Tag.TAG_STRING)) {
|
||||
|
|
@ -681,7 +687,7 @@ public class LeashConfigManager {
|
|||
for (int i = 0; i < elasticityTag.size(); i++) {
|
||||
elasticity.add(elasticityTag.getDouble(i));
|
||||
}
|
||||
axisElasticity = Collections.unmodifiableList(elasticity);
|
||||
axisElasticity = new ArrayList<>(elasticity);
|
||||
}
|
||||
|
||||
if (tag.contains("max_leashes_per_entity", Tag.TAG_INT)) {
|
||||
|
|
@ -707,10 +713,6 @@ public class LeashConfigManager {
|
|||
hash = fnv1aHashMap(hash, tagLeashMap);
|
||||
hash = fnv1aHashMap(hash, modLeashMap);
|
||||
|
||||
// 哈希白名单
|
||||
for (String entry : teleportWhitelistCache) {
|
||||
hash = fnv1aHashString(hash, entry);
|
||||
}
|
||||
|
||||
// 哈希字符串参数
|
||||
hash = fnv1aHashString(hash, commandPrefixCache);
|
||||
|
|
@ -727,8 +729,18 @@ public class LeashConfigManager {
|
|||
hash = fnv1aHashLong(hash, Double.doubleToLongBits(extremeSnapFactor));
|
||||
hash = fnv1aHashLong(hash, Double.doubleToLongBits(springDampening));
|
||||
|
||||
// 哈希轴弹性列表
|
||||
for (double value : axisElasticity) {
|
||||
|
||||
// 白名单排序后再哈希
|
||||
List<String> sortedWhitelist = new ArrayList<>(teleportWhitelistCache);
|
||||
Collections.sort(sortedWhitelist);
|
||||
for (String entry : sortedWhitelist) {
|
||||
hash = fnv1aHashString(hash, entry);
|
||||
}
|
||||
|
||||
// 轴弹性列表排序(或者保持原序但确保两端一致)
|
||||
List<Double> sortedElasticity = new ArrayList<>(axisElasticity);
|
||||
Collections.sort(sortedElasticity);
|
||||
for (double value : sortedElasticity) {
|
||||
hash = fnv1aHashLong(hash, Double.doubleToLongBits(value));
|
||||
}
|
||||
|
||||
|
|
@ -743,7 +755,7 @@ public class LeashConfigManager {
|
|||
private void serializeOffsetMap(CompoundTag parent, String key, @NotNull Map<String, double[]> map) {
|
||||
CompoundTag mapTag = new CompoundTag();
|
||||
for (Map.Entry<String, double[]> entry : map.entrySet()) {
|
||||
String entryKey = entry.getKey().replace(':', '_'); // 避免NBT键中的冒号问题
|
||||
String entryKey = entry.getKey();
|
||||
ListTag offsetList = new ListTag();
|
||||
for (double value : entry.getValue()) {
|
||||
offsetList.add(DoubleTag.valueOf(value));
|
||||
|
|
@ -764,12 +776,13 @@ public class LeashConfigManager {
|
|||
for (int i = 0; i < offsetList.size(); i++) {
|
||||
offset[i] = offsetList.getDouble(i);
|
||||
}
|
||||
map.put(entryKey.replace('_', ':'), offset); // 恢复原始键名
|
||||
map.put(entryKey, offset);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// FNV-1a哈希辅助方法
|
||||
private int fnv1aHashInt(int hash, int value) {
|
||||
hash ^= (value & 0xFF);
|
||||
|
|
@ -797,7 +810,8 @@ public class LeashConfigManager {
|
|||
}
|
||||
|
||||
private int fnv1aHashMap(int hash, @NotNull Map<String, double[]> map) {
|
||||
for (Map.Entry<String, double[]> entry : map.entrySet()) {
|
||||
Map<String, double[]> sortedMap = map instanceof TreeMap ? map : new TreeMap<>(map);
|
||||
for (Map.Entry<String, double[]> entry : sortedMap.entrySet()) {
|
||||
hash = fnv1aHashString(hash, entry.getKey());
|
||||
for (double value : entry.getValue()) {
|
||||
hash = fnv1aHashLong(hash, Double.doubleToLongBits(value));
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ import top.r3944realms.superleadrope.SuperLeadRope;
|
|||
import top.r3944realms.superleadrope.api.event.SuperLeadRopeEvent;
|
||||
import top.r3944realms.superleadrope.api.type.capabilty.ILeashData;
|
||||
import top.r3944realms.superleadrope.api.type.capabilty.LeashInfo;
|
||||
import top.r3944realms.superleadrope.compat.CurtainCompat;
|
||||
import top.r3944realms.superleadrope.compat.FakePlayerJudge;
|
||||
import top.r3944realms.superleadrope.compat.LuckPermsCompat;
|
||||
import top.r3944realms.superleadrope.config.LeashConfigManager;
|
||||
import top.r3944realms.superleadrope.content.entity.SuperLeashKnotEntity;
|
||||
|
|
@ -785,7 +785,7 @@ public class LeashDataImpl implements ILeashData {
|
|||
}
|
||||
@Override
|
||||
public void applyLeashForcesClientPlayer() {
|
||||
if (entity instanceof ServerPlayer player && CurtainCompat.isNotFakePlayer(player)) return;
|
||||
if (entity instanceof ServerPlayer player && FakePlayerJudge.isNotFakePlayer(player)) return;
|
||||
Vec3 combinedForce = Vec3.ZERO;
|
||||
Vec3 combinedDirection = Vec3.ZERO;
|
||||
Map<Integer, LeashInfo> result = leashHolders.entrySet().stream()
|
||||
|
|
@ -881,7 +881,7 @@ public class LeashDataImpl implements ILeashData {
|
|||
if (MinecraftForge.EVENT_BUS.post(hasFocus)) return;
|
||||
combinedForce = hasFocus.getCombinedForce();
|
||||
// 玩家与普通实体统一力应用
|
||||
if (targetEntity instanceof ServerPlayer player && CurtainCompat.isNotFakePlayer(player) ) {
|
||||
if (targetEntity instanceof ServerPlayer player && FakePlayerJudge.isNotFakePlayer(player)) {
|
||||
// 是真实玩家则交给客户端自行处理拴绳逻辑
|
||||
// DO NOTHING
|
||||
if(targetEntity == entity) {
|
||||
|
|
|
|||
|
|
@ -18,9 +18,12 @@ package top.r3944realms.superleadrope.network.toClient;
|
|||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraftforge.network.NetworkEvent;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import top.r3944realms.superleadrope.CommonEventHandler;
|
||||
import top.r3944realms.superleadrope.SuperLeadRope;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
|
|
@ -54,15 +57,61 @@ 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 = CommonEventHandler.leashConfigManager.serializeToNBT();
|
||||
// 1. 保存当前配置(强制重新序列化,不使用缓存)
|
||||
CompoundTag currentConfig = CommonEventHandler.leashConfigManager.serializeToNBT();
|
||||
int currentHash = CommonEventHandler.leashConfigManager.calculateConfigHash();
|
||||
|
||||
// 2. 应用新配置
|
||||
CommonEventHandler.leashConfigManager.deserializeFromNBT(msg.config);
|
||||
if (CommonEventHandler.leashConfigManager.calculateConfigHash() != msg.hash) { //BACK
|
||||
SuperLeadRope.logger.error("Hash mismatch! Except:{}, Actual:{}", msg.hash, CommonEventHandler.leashConfigManager.calculateConfigHash());
|
||||
CommonEventHandler.leashConfigManager.deserializeFromNBT(old);
|
||||
|
||||
// 3. 验证哈希
|
||||
int newHash = CommonEventHandler.leashConfigManager.calculateConfigHash();
|
||||
if (newHash != msg.hash) {
|
||||
SuperLeadRope.logger.error("Hash mismatch! Expected: {}, Actual: {}", msg.hash, newHash);
|
||||
SuperLeadRope.logger.error("Current hash before deserialization: {}", currentHash);
|
||||
|
||||
// 可选:打印差异详情
|
||||
if (currentConfig != null && msg.config != null) {
|
||||
compareConfigs(currentConfig, msg.config);
|
||||
}
|
||||
|
||||
// 4. 恢复旧配置
|
||||
CommonEventHandler.leashConfigManager.deserializeFromNBT(currentConfig);
|
||||
} else {
|
||||
SuperLeadRope.logger.debug("Config sync successful, hash: {}", msg.hash);
|
||||
}
|
||||
});
|
||||
ctx.get().setPacketHandled(true);
|
||||
}
|
||||
|
||||
// 辅助方法:比较配置差异
|
||||
private static void compareConfigs(CompoundTag oldConfig, CompoundTag newConfig) {
|
||||
Set<String> oldKeys = oldConfig.getAllKeys();
|
||||
Set<String> newKeys = newConfig.getAllKeys();
|
||||
|
||||
// 找出只存在于旧配置的键
|
||||
for (String key : oldKeys) {
|
||||
if (!newConfig.contains(key)) {
|
||||
SuperLeadRope.logger.warn("Key only in old config: {}", key);
|
||||
}
|
||||
}
|
||||
|
||||
// 找出只存在于新配置的键
|
||||
for (String key : newKeys) {
|
||||
if (!oldConfig.contains(key)) {
|
||||
SuperLeadRope.logger.warn("Key only in new config: {}", key);
|
||||
}
|
||||
}
|
||||
|
||||
// 比较共同键的值
|
||||
for (String key : oldKeys) {
|
||||
if (newConfig.contains(key) && !Objects.equals(oldConfig.get(key), newConfig.get(key))) {
|
||||
SuperLeadRope.logger.warn("Value mismatch for key: {}", key);
|
||||
SuperLeadRope.logger.warn(" Old: {}", oldConfig.get(key));
|
||||
SuperLeadRope.logger.warn(" New: {}", newConfig.get(key));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ import top.r3944realms.superleadrope.api.type.capabilty.ILeashData;
|
|||
import top.r3944realms.superleadrope.api.type.capabilty.ILeashState;
|
||||
import top.r3944realms.superleadrope.api.type.util.ILeashHelper;
|
||||
import top.r3944realms.superleadrope.api.workspace.IWorkSpaceHelper;
|
||||
import top.r3944realms.superleadrope.compat.FakePlayerJudge;
|
||||
import top.r3944realms.superleadrope.content.capability.impi.LeashDataImpl;
|
||||
import top.r3944realms.superleadrope.content.entity.SuperLeashKnotEntity;
|
||||
import top.r3944realms.superleadrope.util.capability.LeashDataInnerAPI;
|
||||
|
|
@ -84,4 +85,9 @@ public class WorkSpaceHelper implements IWorkSpaceHelper {
|
|||
return LeashStateInnerAPI.getLeashState(pEntity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerFakePlayer(@NotNull Class<?>... classes) {
|
||||
FakePlayerJudge.registersFakePlayer(classes);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user