fix: 保证停止时强制持久化无限存储元件数据

This commit is contained in:
C-H716 2025-09-18 14:32:38 +08:00
parent ef83089dc3
commit 1490f73b5f
3 changed files with 29 additions and 51 deletions

View File

@ -3,6 +3,7 @@ package com.extendedae_plus;
import appeng.api.storage.StorageCells;
import appeng.menu.locator.MenuLocators;
import com.extendedae_plus.ae.api.storage.InfinityBigIntegerCellHandler;
import com.extendedae_plus.ae.api.storage.InfinityBigIntegerCellInventory;
import com.extendedae_plus.client.ClientRegistrar;
import com.extendedae_plus.config.ModConfig;
import com.extendedae_plus.init.*;
@ -54,6 +55,9 @@ public class ExtendedAEPlus {
MinecraftForge.EVENT_BUS.addListener(ExtendedAEPlus::onLevelLoad);
// 注册通用配置
ModConfig.init();
// 注册 InfinityBigIntegerCellInventory 的事件监听tick flush 与停止时 flush
MinecraftForge.EVENT_BUS.addListener(InfinityBigIntegerCellInventory::onServerTick);
MinecraftForge.EVENT_BUS.addListener(InfinityBigIntegerCellInventory::onServerStopping);
// ModLoadingContext.get().registerConfig(ModConfig.Type.COMMON, ModConfigs.COMMON_SPEC);
}

View File

@ -17,8 +17,8 @@ import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.ListTag;
import net.minecraft.network.chat.Component;
import net.minecraft.world.item.ItemStack;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.event.TickEvent;
import net.minecraftforge.event.server.ServerStoppingEvent;
import java.math.BigDecimal;
import java.math.BigInteger;
@ -27,6 +27,7 @@ import java.text.DecimalFormat;
import java.util.UUID;
import java.util.concurrent.ConcurrentLinkedQueue;
import static com.extendedae_plus.util.ExtendedAELogger.LOGGER;
/**
* InfinityBigIntegerCellInventory
* <p>
@ -50,15 +51,6 @@ public class InfinityBigIntegerCellInventory implements StorageCell {
// 数字格式化对象保留两位小数复用以减少对象分配
private static final DecimalFormat DF = new DecimalFormat("#.##");
static {
// 在类加载时注册服务器 tick 监听器用于在主线程合并写入
try {
MinecraftForge.EVENT_BUS.addListener(InfinityBigIntegerCellInventory::onServerTick);
} catch (Throwable ignored) {
// 保守降级若注册失败不阻塞实例化
}
}
// 关联的 ItemStack含可能的 uuid NBT
private final ItemStack stack;
// AE2 提供的保存提供者用于在容器中批量保存时触发回调
@ -97,7 +89,7 @@ public class InfinityBigIntegerCellInventory implements StorageCell {
}
// 服务器 tick 回调合并并执行待持久化项
private static void onServerTick(TickEvent.ServerTickEvent event) {
public static void onServerTick(TickEvent.ServerTickEvent event) {
if (event.phase != TickEvent.Phase.END) return;
InfinityBigIntegerCellInventory inv;
// 处理本次 tick 中的全部待持久化项
@ -107,11 +99,32 @@ public class InfinityBigIntegerCellInventory implements StorageCell {
inv.persist();
}
} catch (Throwable ignored) {
// 忽略单项错误继续处理其余队列
LOGGER.info("InfinityBigIntegerCellInventory onServerTick error: {}", ignored.getMessage());
}
}
}
// 在服务器停止时被调用立即强制持久化队列中的所有实例
public static void onServerStopping(ServerStoppingEvent event) {
InfinityBigIntegerCellInventory inv;
while ((inv = PENDING_PERSIST.poll()) != null) {
try {
if (!inv.isPersisted) {
inv.persist();
}
} catch (Throwable ignored) {
LOGGER.info("InfinityBigIntegerCellInventory onServerStopping error1: {}", ignored.getMessage());
}
}
// 额外尝试将全局存储管理器标记为脏以确保 SavedData 被写回在单人模式下可能直接由系统触发
try {
var stor = getStorageInstance();
if (stor != null) stor.setDirty();
} catch (Throwable ignored) {
LOGGER.info("InfinityBigIntegerCellInventory onServerStopping error2: {}", ignored.getMessage());
}
}
// BigInteger 格式化为带单位的字符串保留两位小数
public static String formatBigInteger(BigInteger number) {
// 使用局部 DF非线程安全 Minecraft 通常在主线程运行
@ -240,12 +253,6 @@ public class InfinityBigIntegerCellInventory implements StorageCell {
// 获取所有可用的物品堆栈及其数量
@Override
public void getAvailableStacks(KeyCounter out) {
// 使用饱和saturating加法将 BigInteger 值转换为 long 并安全地累加到 KeyCounter
// 问题背景当同一物品存在于多个 cell AE2 KeyCounter 使用 long 来记录数量
// 若简单将单个 cell 的超长值截断为 Long.MAX_VALUE 并直接 add多个 cell 的合并会导致
// 原本代表 "大于 long" 的值被重复添加而导致读取异常解决方案每次向 KeyCounter 添加前
// 先读取当前计数器中的已有值long并使用 BigInteger 做饱和加法后再写回为 long避免中间溢出
BigInteger maxLong = BigInteger.valueOf(Long.MAX_VALUE);
Object2ObjectMap<AEKey, BigInteger> map = getCellStoredMap();
for (Object2ObjectMap.Entry<AEKey, BigInteger> entry : map.object2ObjectEntrySet()) {

View File

@ -1,33 +0,0 @@
package com.extendedae_plus.ae.client.gui;
import appeng.client.gui.style.Blitter;
import com.extendedae_plus.ExtendedAEPlus;
import net.minecraft.resources.ResourceLocation;
public enum Icon {
REDSTONE_LOW(0, 0),
REDSTONE_HIGH(16, 0);
public final int x;
public final int y;
public final int width;
public final int height;
public static final ResourceLocation TEXTURE = new ResourceLocation(ExtendedAEPlus.MODID, "textures/guis/states.png");
public static final int TEXTURE_WIDTH = 256;
public static final int TEXTURE_HEIGHT = 256;
private Icon(int x, int y) {
this(x, y, 16, 16);
}
private Icon(int x, int y, int width, int height) {
this.x = x;
this.y = y;
this.width = width;
this.height = height;
}
public Blitter getBlitter() {
return Blitter.texture(TEXTURE, TEXTURE_WIDTH, TEXTURE_HEIGHT).src(this.x, this.y, this.width, this.height);
}
}