fix: 修复 Infinity 存储:移除可变共享 EMPTY,封装 NBT 字段;按世界管理 StorageManager(移除全局 INSTANCE);加载时重置 totalStored 防止重复累计;用 AtomicBoolean 代替队列 contains,修正 persist 的空/无管理器处理。
This commit is contained in:
parent
9bdda0526f
commit
37d24334bb
|
|
@ -26,6 +26,7 @@ import java.math.RoundingMode;
|
|||
import java.text.DecimalFormat;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
/**
|
||||
* InfinityBigIntegerCellInventory
|
||||
|
|
@ -91,9 +92,9 @@ public class InfinityBigIntegerCellInventory implements StorageCell {
|
|||
return null;
|
||||
}
|
||||
|
||||
// 获取全局存储实例
|
||||
// 获取全局存储实例(尽量安全地获取任意已注册的世界实例)
|
||||
private static InfinityStorageManager getStorageInstance() {
|
||||
return InfinityStorageManager.INSTANCE;
|
||||
return InfinityStorageManager.getAnyInstance();
|
||||
}
|
||||
|
||||
// 服务器 tick 回调:合并并执行待持久化项
|
||||
|
|
@ -133,10 +134,12 @@ public class InfinityBigIntegerCellInventory implements StorageCell {
|
|||
private InfinityDataStorage getCellStorage() {
|
||||
if (this.getUUID() == null) {
|
||||
// 如果没有UUID,返回空存储
|
||||
return InfinityDataStorage.EMPTY;
|
||||
return InfinityDataStorage.empty();
|
||||
} else {
|
||||
// 否则获取或创建对应UUID的存储
|
||||
return getStorageInstance().getOrCreateCell(getUUID());
|
||||
// 否则获取或创建对应UUID的存储,若管理器不可用则返回空存储以避免 NPE
|
||||
InfinityStorageManager mgr = getStorageInstance();
|
||||
if (mgr == null) return InfinityDataStorage.empty();
|
||||
return mgr.getOrCreateCell(getUUID());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -187,8 +190,11 @@ public class InfinityBigIntegerCellInventory implements StorageCell {
|
|||
private void loadCellStoredMap() {
|
||||
boolean corruptedTag = false; // 标记数据是否损坏
|
||||
if (!stack.hasTag()) return;
|
||||
ListTag keys = this.getCellStorage().keys;
|
||||
ListTag amounts = this.getCellStorage().amounts;
|
||||
// 在加载前重置缓存,避免重复累计
|
||||
totalStored = BigInteger.ZERO;
|
||||
InfinityDataStorage storage = this.getCellStorage();
|
||||
ListTag keys = storage.getKeys();
|
||||
ListTag amounts = storage.getAmounts();
|
||||
int len = Math.min(keys.size(), amounts.size());
|
||||
for (int i = 0; i < len; i++) {
|
||||
AEKey key = AEKey.fromTagGeneric(keys.getCompound(i));
|
||||
|
|
@ -223,6 +229,8 @@ public class InfinityBigIntegerCellInventory implements StorageCell {
|
|||
}
|
||||
|
||||
// 标记数据需要保存,并通知容器或直接持久化
|
||||
private final AtomicBoolean queued = new AtomicBoolean(false);
|
||||
|
||||
private void saveChanges() {
|
||||
// 标记为未持久化,交由容器或延迟任务合并写入以减少 I/O
|
||||
isPersisted = false;
|
||||
|
|
@ -231,7 +239,7 @@ public class InfinityBigIntegerCellInventory implements StorageCell {
|
|||
container.saveChanges();
|
||||
} else {
|
||||
// 如果没有容器,入队等待服务器 tick 在主线程统一持久化,避免频繁 I/O
|
||||
if (!PENDING_PERSIST.contains(this)) {
|
||||
if (queued.compareAndSet(false, true)) {
|
||||
PENDING_PERSIST.offer(this);
|
||||
}
|
||||
}
|
||||
|
|
@ -258,13 +266,21 @@ public class InfinityBigIntegerCellInventory implements StorageCell {
|
|||
if (map.isEmpty()) {
|
||||
// 如果存储为空,移除UUID和全局存储中的数据
|
||||
if (this.hasUUID()) {
|
||||
getStorageInstance().removeCell(getUUID());
|
||||
InfinityStorageManager mgr = getStorageInstance();
|
||||
if (mgr != null) mgr.removeCell(getUUID());
|
||||
if (stack.getTag() != null) {
|
||||
stack.getTag().remove("uuid");
|
||||
// 移除缓存的 total 字段
|
||||
stack.getTag().remove("total");
|
||||
}
|
||||
// 清理 queued 标志并标记已持久化
|
||||
queued.set(false);
|
||||
isPersisted = true;
|
||||
return;
|
||||
}
|
||||
// 无 UUID 且为空,不需要做额外操作,但确保已标记为已持久化
|
||||
queued.set(false);
|
||||
isPersisted = true;
|
||||
return;
|
||||
}
|
||||
// 构建要保存的Key和数量列表(混合表示:long 或 string)
|
||||
|
|
@ -284,11 +300,16 @@ public class InfinityBigIntegerCellInventory implements StorageCell {
|
|||
}
|
||||
}
|
||||
// 如果没有Key,更新为空存储,否则保存数据
|
||||
if (keys.isEmpty()) {
|
||||
getStorageInstance().updateCell(this.getUUID(), new InfinityDataStorage());
|
||||
InfinityStorageManager mgr = getStorageInstance();
|
||||
if (mgr == null) {
|
||||
// 无可用存储管理器,跳过持久化(保留内存状态)
|
||||
} else {
|
||||
// amounts 现在为 CompoundTag 列表
|
||||
getStorageInstance().modifyCell(this.getUUID(), keys, amountTags);
|
||||
if (keys.isEmpty()) {
|
||||
mgr.updateCell(this.getUUID(), new InfinityDataStorage());
|
||||
} else {
|
||||
// amounts 现在为 CompoundTag 列表
|
||||
mgr.modifyCell(this.getUUID(), keys, amountTags);
|
||||
}
|
||||
}
|
||||
// 将缓存的 totalStored 同步到 ItemStack 的 NBT,优先使用 long
|
||||
if (stack.getOrCreateTag() != null) {
|
||||
|
|
@ -299,6 +320,8 @@ public class InfinityBigIntegerCellInventory implements StorageCell {
|
|||
}
|
||||
}
|
||||
isPersisted = true;
|
||||
// 持久化完成后,清除 queued 标志
|
||||
queued.set(false);
|
||||
}
|
||||
|
||||
// 插入物品到存储单元
|
||||
|
|
|
|||
|
|
@ -18,17 +18,17 @@ import net.minecraft.nbt.Tag;
|
|||
*/
|
||||
public class InfinityDataStorage {
|
||||
|
||||
/** 空实例(表示没有数据) */
|
||||
public static final InfinityDataStorage EMPTY = new InfinityDataStorage();
|
||||
// 不再暴露可变的共享实例,避免多个调用方修改同一 ListTag 导致交叉污染
|
||||
private static final InfinityDataStorage TRUE_EMPTY = new InfinityDataStorage(new ListTag(), new ListTag());
|
||||
|
||||
/** 序列化的键列表(NBT ListTag,元素为 CompoundTag) */
|
||||
public ListTag keys;
|
||||
private ListTag keys;
|
||||
/**
|
||||
* 与 keys 对应的数量列表(NBT ListTag,元素为 CompoundTag):
|
||||
* - 若数量能放入 long,则 CompoundTag 包含键 "l"(long)
|
||||
* - 否则包含键 "s"(String) 存放 BigInteger 的字符串形式
|
||||
*/
|
||||
public ListTag amounts;
|
||||
private ListTag amounts;
|
||||
|
||||
public InfinityDataStorage() {
|
||||
this(new ListTag(), new ListTag());
|
||||
|
|
@ -39,6 +39,14 @@ public class InfinityDataStorage {
|
|||
this.amounts = amounts;
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回一个空的不可共享实例(调用方若需要可变副本请自行复制)
|
||||
*/
|
||||
public static InfinityDataStorage empty() {
|
||||
// 返回一个新的实例以避免共享可变对象被篡改
|
||||
return new InfinityDataStorage(new ListTag(), new ListTag());
|
||||
}
|
||||
|
||||
/**
|
||||
* 将当前数据封装为 CompoundTag 以写入存档
|
||||
*/
|
||||
|
|
@ -58,4 +66,20 @@ public class InfinityDataStorage {
|
|||
ListTag stackAmounts = nbt.getList("amounts", Tag.TAG_COMPOUND);
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,13 +2,16 @@ package com.extendedae_plus.util.storage;
|
|||
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.nbt.ListTag;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.saveddata.SavedData;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* InfinityStorageManager
|
||||
|
|
@ -26,9 +29,9 @@ public class InfinityStorageManager extends SavedData {
|
|||
*/
|
||||
public static final String FILE_NAME = "eap_infinity_biginteger_cells";
|
||||
/**
|
||||
* 全局单例实例(在世界加载时由 InfiniteBigIntegerStorageCell.onLevelLoad 填充)
|
||||
* Per-world instances to avoid cross-world leakage. Keyed by world ResourceKey<Level>.
|
||||
*/
|
||||
public static InfinityStorageManager INSTANCE = null;
|
||||
private static final Map<ResourceKey<Level>, InfinityStorageManager> INSTANCES = new ConcurrentHashMap<>();
|
||||
/**
|
||||
* UUID -> 数据 的内存映射
|
||||
*/
|
||||
|
|
@ -54,10 +57,29 @@ public class InfinityStorageManager extends SavedData {
|
|||
* 根据给定的 ServerLevel 获取或创建该世界对应的 SavedData 实例并缓存到 INSTANCE
|
||||
*/
|
||||
public static InfinityStorageManager getForLevel(ServerLevel level) {
|
||||
if (INSTANCE == null && level != null) {
|
||||
INSTANCE = level.getDataStorage().computeIfAbsent(InfinityStorageManager::new, InfinityStorageManager::new, FILE_NAME);
|
||||
if (level == null) return null;
|
||||
ResourceKey<Level> key = level.dimension();
|
||||
InfinityStorageManager mgr = INSTANCES.get(key);
|
||||
if (mgr == null) {
|
||||
mgr = level.getDataStorage().computeIfAbsent(InfinityStorageManager::new, InfinityStorageManager::new, FILE_NAME);
|
||||
INSTANCES.put(key, mgr);
|
||||
}
|
||||
return INSTANCE;
|
||||
return mgr;
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回任何现有实例,作为无法访问 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
|
||||
|
|
@ -98,8 +120,8 @@ public class InfinityStorageManager extends SavedData {
|
|||
public void modifyCell(UUID cellID, ListTag stackKeys, ListTag stackAmounts) {
|
||||
InfinityDataStorage cellToModify = getOrCreateCell(cellID);
|
||||
if (stackKeys != null && stackAmounts != null) {
|
||||
cellToModify.keys = stackKeys;
|
||||
cellToModify.amounts = stackAmounts;
|
||||
cellToModify.setKeys(stackKeys);
|
||||
cellToModify.setAmounts(stackAmounts);
|
||||
}
|
||||
updateCell(cellID, cellToModify);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user