fix:回退无限存储元件修复

This commit is contained in:
C-H716 2025-09-16 22:02:13 +08:00
parent 3039add611
commit d316c5f039
3 changed files with 24 additions and 93 deletions

View File

@ -26,7 +26,6 @@ import java.math.RoundingMode;
import java.text.DecimalFormat; import java.text.DecimalFormat;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicBoolean;
/** /**
* InfinityBigIntegerCellInventory * InfinityBigIntegerCellInventory
@ -92,9 +91,9 @@ public class InfinityBigIntegerCellInventory implements StorageCell {
return null; return null;
} }
// 获取全局存储实例尽量安全地获取任意已注册的世界实例 // 获取全局存储实例
private static InfinityStorageManager getStorageInstance() { private static InfinityStorageManager getStorageInstance() {
return InfinityStorageManager.getAnyInstance(); return InfinityStorageManager.INSTANCE;
} }
// 服务器 tick 回调合并并执行待持久化项 // 服务器 tick 回调合并并执行待持久化项
@ -134,12 +133,10 @@ public class InfinityBigIntegerCellInventory implements StorageCell {
private InfinityDataStorage getCellStorage() { private InfinityDataStorage getCellStorage() {
if (this.getUUID() == null) { if (this.getUUID() == null) {
// 如果没有UUID返回空存储 // 如果没有UUID返回空存储
return InfinityDataStorage.empty(); return InfinityDataStorage.EMPTY;
} else { } else {
// 否则获取或创建对应UUID的存储若管理器不可用则返回空存储以避免 NPE // 否则获取或创建对应UUID的存储
InfinityStorageManager mgr = getStorageInstance(); return getStorageInstance().getOrCreateCell(getUUID());
if (mgr == null) return InfinityDataStorage.empty();
return mgr.getOrCreateCell(getUUID());
} }
} }
@ -190,11 +187,8 @@ public class InfinityBigIntegerCellInventory implements StorageCell {
private void loadCellStoredMap() { private void loadCellStoredMap() {
boolean corruptedTag = false; // 标记数据是否损坏 boolean corruptedTag = false; // 标记数据是否损坏
if (!stack.hasTag()) return; if (!stack.hasTag()) return;
// 在加载前重置缓存避免重复累计 ListTag keys = this.getCellStorage().keys;
totalStored = BigInteger.ZERO; ListTag amounts = this.getCellStorage().amounts;
InfinityDataStorage storage = this.getCellStorage();
ListTag keys = storage.getKeys();
ListTag amounts = storage.getAmounts();
int len = Math.min(keys.size(), amounts.size()); int len = Math.min(keys.size(), amounts.size());
for (int i = 0; i < len; i++) { for (int i = 0; i < len; i++) {
AEKey key = AEKey.fromTagGeneric(keys.getCompound(i)); AEKey key = AEKey.fromTagGeneric(keys.getCompound(i));
@ -229,8 +223,6 @@ public class InfinityBigIntegerCellInventory implements StorageCell {
} }
// 标记数据需要保存并通知容器或直接持久化 // 标记数据需要保存并通知容器或直接持久化
private final AtomicBoolean queued = new AtomicBoolean(false);
private void saveChanges() { private void saveChanges() {
// 标记为未持久化交由容器或延迟任务合并写入以减少 I/O // 标记为未持久化交由容器或延迟任务合并写入以减少 I/O
isPersisted = false; isPersisted = false;
@ -239,7 +231,7 @@ public class InfinityBigIntegerCellInventory implements StorageCell {
container.saveChanges(); container.saveChanges();
} else { } else {
// 如果没有容器入队等待服务器 tick 在主线程统一持久化避免频繁 I/O // 如果没有容器入队等待服务器 tick 在主线程统一持久化避免频繁 I/O
if (queued.compareAndSet(false, true)) { if (!PENDING_PERSIST.contains(this)) {
PENDING_PERSIST.offer(this); PENDING_PERSIST.offer(this);
} }
} }
@ -266,21 +258,13 @@ public class InfinityBigIntegerCellInventory implements StorageCell {
if (map.isEmpty()) { if (map.isEmpty()) {
// 如果存储为空移除UUID和全局存储中的数据 // 如果存储为空移除UUID和全局存储中的数据
if (this.hasUUID()) { if (this.hasUUID()) {
InfinityStorageManager mgr = getStorageInstance(); getStorageInstance().removeCell(getUUID());
if (mgr != null) mgr.removeCell(getUUID());
if (stack.getTag() != null) { if (stack.getTag() != null) {
stack.getTag().remove("uuid"); stack.getTag().remove("uuid");
// 移除缓存的 total 字段 // 移除缓存的 total 字段
stack.getTag().remove("total"); stack.getTag().remove("total");
} }
// 清理 queued 标志并标记已持久化
queued.set(false);
isPersisted = true;
return;
} }
// UUID 且为空不需要做额外操作但确保已标记为已持久化
queued.set(false);
isPersisted = true;
return; return;
} }
// 构建要保存的Key和数量列表混合表示long string // 构建要保存的Key和数量列表混合表示long string
@ -300,16 +284,11 @@ public class InfinityBigIntegerCellInventory implements StorageCell {
} }
} }
// 如果没有Key更新为空存储否则保存数据 // 如果没有Key更新为空存储否则保存数据
InfinityStorageManager mgr = getStorageInstance(); if (keys.isEmpty()) {
if (mgr == null) { getStorageInstance().updateCell(this.getUUID(), new InfinityDataStorage());
// 无可用存储管理器跳过持久化保留内存状态
} else { } else {
if (keys.isEmpty()) { // amounts 现在为 CompoundTag 列表
mgr.updateCell(this.getUUID(), new InfinityDataStorage()); getStorageInstance().modifyCell(this.getUUID(), keys, amountTags);
} else {
// amounts 现在为 CompoundTag 列表
mgr.modifyCell(this.getUUID(), keys, amountTags);
}
} }
// 将缓存的 totalStored 同步到 ItemStack NBT优先使用 long // 将缓存的 totalStored 同步到 ItemStack NBT优先使用 long
if (stack.getOrCreateTag() != null) { if (stack.getOrCreateTag() != null) {
@ -320,8 +299,6 @@ public class InfinityBigIntegerCellInventory implements StorageCell {
} }
} }
isPersisted = true; isPersisted = true;
// 持久化完成后清除 queued 标志
queued.set(false);
} }
// 插入物品到存储单元 // 插入物品到存储单元

View File

@ -18,17 +18,17 @@ import net.minecraft.nbt.Tag;
*/ */
public class InfinityDataStorage { public class InfinityDataStorage {
// 不再暴露可变的共享实例避免多个调用方修改同一 ListTag 导致交叉污染 /** 空实例(表示没有数据) */
private static final InfinityDataStorage TRUE_EMPTY = new InfinityDataStorage(new ListTag(), new ListTag()); public static final InfinityDataStorage EMPTY = new InfinityDataStorage();
/** 序列化的键列表NBT ListTag元素为 CompoundTag */ /** 序列化的键列表NBT ListTag元素为 CompoundTag */
private ListTag keys; public ListTag keys;
/** /**
* keys 对应的数量列表NBT ListTag元素为 CompoundTag * keys 对应的数量列表NBT ListTag元素为 CompoundTag
* - 若数量能放入 long CompoundTag 包含键 "l"(long) * - 若数量能放入 long CompoundTag 包含键 "l"(long)
* - 否则包含键 "s"(String) 存放 BigInteger 的字符串形式 * - 否则包含键 "s"(String) 存放 BigInteger 的字符串形式
*/ */
private ListTag amounts; public ListTag amounts;
public InfinityDataStorage() { public InfinityDataStorage() {
this(new ListTag(), new ListTag()); this(new ListTag(), new ListTag());
@ -39,14 +39,6 @@ public class InfinityDataStorage {
this.amounts = amounts; this.amounts = amounts;
} }
/**
* 返回一个空的不可共享实例调用方若需要可变副本请自行复制
*/
public static InfinityDataStorage empty() {
// 返回一个新的实例以避免共享可变对象被篡改
return new InfinityDataStorage(new ListTag(), new ListTag());
}
/** /**
* 将当前数据封装为 CompoundTag 以写入存档 * 将当前数据封装为 CompoundTag 以写入存档
*/ */
@ -66,20 +58,4 @@ public class InfinityDataStorage {
ListTag stackAmounts = nbt.getList("amounts", Tag.TAG_COMPOUND); ListTag stackAmounts = nbt.getList("amounts", Tag.TAG_COMPOUND);
return new InfinityDataStorage(stackKeys, stackAmounts); return new InfinityDataStorage(stackKeys, stackAmounts);
} }
public ListTag getKeys() {
return keys;
}
public ListTag getAmounts() {
return amounts;
}
public void setKeys(ListTag keys) {
this.keys = keys;
}
public void setAmounts(ListTag amounts) {
this.amounts = amounts;
}
} }

View File

@ -2,16 +2,13 @@ package com.extendedae_plus.util.storage;
import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.ListTag; import net.minecraft.nbt.ListTag;
import net.minecraft.resources.ResourceKey;
import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.saveddata.SavedData; import net.minecraft.world.level.saveddata.SavedData;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
/** /**
* InfinityStorageManager * InfinityStorageManager
@ -29,9 +26,9 @@ public class InfinityStorageManager extends SavedData {
*/ */
public static final String FILE_NAME = "eap_infinity_biginteger_cells"; public static final String FILE_NAME = "eap_infinity_biginteger_cells";
/** /**
* Per-world instances to avoid cross-world leakage. Keyed by world ResourceKey<Level>. * 全局单例实例在世界加载时由 InfiniteBigIntegerStorageCell.onLevelLoad 填充
*/ */
private static final Map<ResourceKey<Level>, InfinityStorageManager> INSTANCES = new ConcurrentHashMap<>(); public static InfinityStorageManager INSTANCE = null;
/** /**
* UUID -> 数据 的内存映射 * UUID -> 数据 的内存映射
*/ */
@ -57,29 +54,10 @@ public class InfinityStorageManager extends SavedData {
* 根据给定的 ServerLevel 获取或创建该世界对应的 SavedData 实例并缓存到 INSTANCE * 根据给定的 ServerLevel 获取或创建该世界对应的 SavedData 实例并缓存到 INSTANCE
*/ */
public static InfinityStorageManager getForLevel(ServerLevel level) { public static InfinityStorageManager getForLevel(ServerLevel level) {
if (level == null) return null; if (INSTANCE == null && level != null) {
ResourceKey<Level> key = level.dimension(); INSTANCE = level.getDataStorage().computeIfAbsent(InfinityStorageManager::new, InfinityStorageManager::new, FILE_NAME);
InfinityStorageManager mgr = INSTANCES.get(key);
if (mgr == null) {
mgr = level.getDataStorage().computeIfAbsent(InfinityStorageManager::new, InfinityStorageManager::new, FILE_NAME);
INSTANCES.put(key, mgr);
} }
return mgr; return INSTANCE;
}
/**
* 返回任何现有实例作为无法访问 ServerLevel 的代码路径的安全回退
*/
public static InfinityStorageManager getAnyInstance() {
return INSTANCES.values().stream().findFirst().orElse(null);
}
/**
* 删除世界的实例在世界卸载时调用
*/
public static void removeForLevel(ServerLevel level) {
if (level == null) return;
INSTANCES.remove(level.dimension());
} }
@Override @Override
@ -120,8 +98,8 @@ public class InfinityStorageManager extends SavedData {
public void modifyCell(UUID cellID, ListTag stackKeys, ListTag stackAmounts) { public void modifyCell(UUID cellID, ListTag stackKeys, ListTag stackAmounts) {
InfinityDataStorage cellToModify = getOrCreateCell(cellID); InfinityDataStorage cellToModify = getOrCreateCell(cellID);
if (stackKeys != null && stackAmounts != null) { if (stackKeys != null && stackAmounts != null) {
cellToModify.setKeys(stackKeys); cellToModify.keys = stackKeys;
cellToModify.setAmounts(stackAmounts); cellToModify.amounts = stackAmounts;
} }
updateCell(cellID, cellToModify); updateCell(cellID, cellToModify);
} }