diff --git a/gradle.properties b/gradle.properties index 21dcae0..32815b9 100644 --- a/gradle.properties +++ b/gradle.properties @@ -3,7 +3,7 @@ org.gradle.jvmargs=-Xmx1G loom.platform = forge # Mod properties -mod_version = 1.4.2 +mod_version = 1.4.2-fix maven_group = com.extendedae_plus archives_name = extendedae_plus diff --git a/src/main/java/com/extendedae_plus/ExtendedAEPlus.java b/src/main/java/com/extendedae_plus/ExtendedAEPlus.java index 2688340..23b2770 100644 --- a/src/main/java/com/extendedae_plus/ExtendedAEPlus.java +++ b/src/main/java/com/extendedae_plus/ExtendedAEPlus.java @@ -3,7 +3,6 @@ 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.*; @@ -55,10 +54,6 @@ 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); } /** @@ -128,7 +123,8 @@ public class ExtendedAEPlus { // 在世界加载时注册/加载 SavedData private static void onLevelLoad(LevelEvent.Load event) { if (event.getLevel() instanceof ServerLevel serverLevel) { - InfinityStorageManager.getForLevel(serverLevel); + // 初始化自定义的文件 I/O 存储管理器 + InfinityStorageManager.INSTANCE.initFromWorld(serverLevel); } } } diff --git a/src/main/java/com/extendedae_plus/ae/api/storage/InfinityBigIntegerCellInventory.java b/src/main/java/com/extendedae_plus/ae/api/storage/InfinityBigIntegerCellInventory.java index 2cbb417..ce89043 100644 --- a/src/main/java/com/extendedae_plus/ae/api/storage/InfinityBigIntegerCellInventory.java +++ b/src/main/java/com/extendedae_plus/ae/api/storage/InfinityBigIntegerCellInventory.java @@ -17,8 +17,6 @@ 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.event.TickEvent; -import net.minecraftforge.event.server.ServerStoppingEvent; import java.math.BigDecimal; import java.math.BigInteger; @@ -27,7 +25,6 @@ import java.text.DecimalFormat; import java.util.UUID; import java.util.concurrent.ConcurrentLinkedQueue; -import static com.extendedae_plus.util.ExtendedAELogger.LOGGER; /** * InfinityBigIntegerCellInventory *

@@ -59,8 +56,7 @@ public class InfinityBigIntegerCellInventory implements StorageCell { private Object2ObjectMap storedMap = null; // 标记是否已持久化到 SavedData private boolean isPersisted = true; - // 缓存的总存储量,避免每次调用进行全表扫描 - private BigInteger totalStored = BigInteger.ZERO; + /** * 私有构造器:通过 createInventory 工厂方法调用 @@ -70,59 +66,9 @@ public class InfinityBigIntegerCellInventory implements StorageCell { */ private InfinityBigIntegerCellInventory(ItemStack stack, ISaveProvider saveProvider) { this.stack = stack; - container = saveProvider; - // 不在构造时创建 storedMap,推迟到实际访问或首次写入时初始化 + this.container = saveProvider; this.storedMap = null; - } - - // 创建存储单元库存实例的静态方法 - static InfinityBigIntegerCellInventory createInventory(ItemStack stack, ISaveProvider saveProvider) { - if (stack.getItem() instanceof InfinityBigIntegerCellItem) { - return new InfinityBigIntegerCellInventory(stack, saveProvider); - } - return null; - } - - // 获取全局存储实例 - private static InfinityStorageManager getStorageInstance() { - return InfinityStorageManager.INSTANCE; - } - - // 服务器 tick 回调:合并并执行待持久化项 - public static void onServerTick(TickEvent.ServerTickEvent event) { - if (event.phase != TickEvent.Phase.END) return; - InfinityBigIntegerCellInventory inv; - // 处理本次 tick 中的全部待持久化项 - while ((inv = PENDING_PERSIST.poll()) != null) { - try { - if (!inv.isPersisted) { - 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()); - } + initData(); } // 将 BigInteger 格式化为带单位的字符串,保留两位小数 @@ -142,14 +88,24 @@ public class InfinityBigIntegerCellInventory implements StorageCell { return DF.format(bd.doubleValue()) + units[idx]; } + // 创建存储单元库存实例的静态方法 + static InfinityBigIntegerCellInventory createInventory(ItemStack stack, ISaveProvider saveProvider) { + return new InfinityBigIntegerCellInventory(stack, saveProvider); + } + + private void initData() { + if (!hasUUID()) { + getCellStoredMap(); + } + } + // 获取当前存储单元的数据存储对象 private InfinityDataStorage getCellStorage() { if (this.getUUID() == null) { // 如果没有UUID,返回空存储 return InfinityDataStorage.EMPTY; } else { - // 否则获取或创建对应UUID的存储 - return getStorageInstance().getOrCreateCell(getUUID()); + return InfinityStorageManager.INSTANCE.getOrCreateCell(this.getUUID()); } } @@ -181,10 +137,11 @@ public class InfinityBigIntegerCellInventory implements StorageCell { // 获取物品堆栈的UUID public UUID getUUID() { - if (this.hasUUID()) + if (this.hasUUID()) { return stack.getOrCreateTag().getUUID("uuid"); - else + } else { return null; + } } // 获取或初始化存储映射 @@ -199,11 +156,13 @@ public class InfinityBigIntegerCellInventory implements StorageCell { // 从存储中加载物品映射 private void loadCellStoredMap() { boolean corruptedTag = false; // 标记数据是否损坏 - if (!stack.hasTag()) return; + if (!stack.hasTag()) + return; ListTag keys = this.getCellStorage().keys; ListTag amounts = this.getCellStorage().amounts; - int len = Math.min(keys.size(), amounts.size()); - for (int i = 0; i < len; i++) { + + + for (int i = 0; i < amounts.size(); i++) { AEKey key = AEKey.fromTagGeneric(keys.getCompound(i)); CompoundTag amtTag = amounts.getCompound(i); try { @@ -221,15 +180,13 @@ public class InfinityBigIntegerCellInventory implements StorageCell { corruptedTag = true; } else { // storedMap 已在 getCellStoredMap() 中初始化,直接使用字段以避免额外方法开销 - storedMap.put(key, amount); - // 更新缓存的总数 - totalStored = totalStored.add(amount); + getCellStoredMap().put(key, amount); } } catch (NumberFormatException ex) { corruptedTag = true; } } - // 如果有损坏,保存修正后的数据 + // 如果有损坏,尝试保存修正后的数据;若全局管理器尚未就绪则保守处理 if (corruptedTag) { this.saveChanges(); } @@ -243,10 +200,7 @@ public class InfinityBigIntegerCellInventory implements StorageCell { // 当存在容器时,优先让容器统一处理持久化 container.saveChanges(); } else { - // 如果没有容器,入队等待服务器 tick 在主线程统一持久化,避免频繁 I/O - if (!PENDING_PERSIST.contains(this)) { - PENDING_PERSIST.offer(this); - } + persist(); } } @@ -287,20 +241,25 @@ public class InfinityBigIntegerCellInventory implements StorageCell { // 持久化存储单元数据到全局存储 @Override public void persist() { - if (this.isPersisted) + if (this.isPersisted) { return; + } Object2ObjectMap map = this.getCellStoredMap(); if (map.isEmpty()) { - // 如果存储为空,移除UUID和全局存储中的数据 + // 如果存储为空,保守处理:写回空的 persisted 数据但不要从 ItemStack 上移除 uuid if (this.hasUUID()) { - getStorageInstance().removeCell(getUUID()); - if (stack.getTag() != null) { - stack.getTag().remove("uuid"); - // 移除缓存的 total 字段 - stack.getTag().remove("total"); + // 如果存储为空,移除UUID和全局存储中的数据,并清理缓存的 types/total + if (hasUUID()) { + InfinityStorageManager.INSTANCE.removeCell(getUUID()); + if (stack.getTag() != null) { + stack.getTag().remove("uuid"); + stack.getTag().remove("types"); + stack.getTag().remove("total"); + } + initData(); } + return; } - return; } // 构建要保存的Key和数量列表(混合表示:long 或 string) ListTag amountTags = new ListTag(); @@ -318,23 +277,35 @@ public class InfinityBigIntegerCellInventory implements StorageCell { amountTags.add(amt); } } - // 如果没有Key,更新为空存储,否则保存数据 if (keys.isEmpty()) { - getStorageInstance().updateCell(this.getUUID(), new InfinityDataStorage()); + InfinityStorageManager.INSTANCE.updateCell(this.getUUID(), new InfinityDataStorage()); + // 清理缓存 + if (stack.getTag() != null) { + stack.getTag().remove("types"); + stack.getTag().remove("total"); + } } else { // amounts 现在为 CompoundTag 列表 - getStorageInstance().modifyCell(this.getUUID(), keys, amountTags); - } - // 将缓存的 totalStored 同步到 ItemStack 的 NBT,优先使用 long - if (stack.getOrCreateTag() != null) { - if (totalStored.compareTo(BigInteger.valueOf(Long.MAX_VALUE)) <= 0) { - stack.getOrCreateTag().putLong("total", totalStored.longValue()); - } else { - stack.getOrCreateTag().putString("total", totalStored.toString()); + InfinityStorageManager.INSTANCE.modifyCell(this.getUUID(), keys, amountTags); + // 缓存类型数量与总量到 ItemStack 的 NBT,避免每次 tooltip 或展示时重新统计 + try { + if (stack.getTag() == null) stack.setTag(new CompoundTag()); + int typesCount = keys.size(); + stack.getOrCreateTag().putInt("types", typesCount); + java.math.BigInteger total = java.math.BigInteger.ZERO; + for (java.util.Map.Entry e : map.object2ObjectEntrySet()) { + java.math.BigInteger v = e.getValue(); + if (v.compareTo(java.math.BigInteger.ZERO) > 0) { + total = total.add(v); + } + } + if (total.compareTo(java.math.BigInteger.valueOf(Long.MAX_VALUE)) <= 0) { + stack.getOrCreateTag().putLong("total", total.longValue()); + } else { + stack.getOrCreateTag().putString("total", total.toString()); + } + } catch (Exception ignored) { } - // 将当前已存储的不同物品种类数缓存到 NBT(键名: "types"),用于客户端 tooltip 显示 - int typesCount = this.getCellStoredMap().size(); - stack.getOrCreateTag().putInt("types", typesCount); } isPersisted = true; } @@ -343,17 +314,19 @@ public class InfinityBigIntegerCellInventory implements StorageCell { @Override public long insert(AEKey what, long amount, Actionable mode, IActionSource source) { // 数量为0或类型不匹配直接返回 - if (amount == 0) + if (amount == 0) { return 0; + } // 不允许存储无限单元自身 - if (what instanceof AEItemKey itemKey && itemKey.getItem() instanceof InfinityBigIntegerCellItem) + if (what instanceof AEItemKey itemKey && itemKey.getItem() instanceof InfinityBigIntegerCellItem) { return 0; - // 如果没有UUID,生成UUID并初始化存储 + } + // 如果没有UUID,生成UUID并初始化存储(延迟创建全局存储以避免在 manager 未就绪时 NPE) if (!this.hasUUID()) { stack.getOrCreateTag().putUUID("uuid", UUID.randomUUID()); - getStorageInstance().getOrCreateCell(getUUID()); + InfinityStorageManager.INSTANCE.getOrCreateCell(getUUID()); // 确保 storedMap 初始化并从持久层加载数据 - this.getCellStoredMap(); + loadCellStoredMap(); } Object2ObjectMap map = this.getCellStoredMap(); BigInteger currentAmount = map.getOrDefault(what, BigInteger.ZERO); @@ -361,8 +334,6 @@ public class InfinityBigIntegerCellInventory implements StorageCell { // 实际插入,更新数量并保存 BigInteger newAmount = currentAmount.add(BigInteger.valueOf(amount)); map.put(what, newAmount); - // 更新 cached total - totalStored = totalStored.add(BigInteger.valueOf(amount)); this.saveChanges(); } return amount; @@ -383,11 +354,8 @@ public class InfinityBigIntegerCellInventory implements StorageCell { } else { ret = currentAmount.longValue(); } - if (mode == Actionable.MODULATE) { + if (mode == appeng.api.config.Actionable.MODULATE) { map.remove(what); - // 更新 cached total - // 如果 currentAmount 大于 Long.MAX_VALUE,totalStored 减去 currentAmount 会保留大整数 - totalStored = totalStored.subtract(currentAmount); this.saveChanges(); } return ret; @@ -395,8 +363,6 @@ public class InfinityBigIntegerCellInventory implements StorageCell { // 提取部分 if (mode == Actionable.MODULATE) { map.put(what, currentAmount.subtract(requested)); - // 更新 cached total - totalStored = totalStored.subtract(requested); this.saveChanges(); } return amount; @@ -408,6 +374,12 @@ public class InfinityBigIntegerCellInventory implements StorageCell { // 获取存储单元内所有物品的总数量(格式化字符串) public String getTotalStorage() { // 使用缓存的 totalStored,避免每次全表扫描 - return formatBigInteger(totalStored); + BigInteger total = BigInteger.ZERO; + for (BigInteger value : getCellStoredMap().values()) { + if (value.compareTo(BigInteger.ZERO) > 0) { + total = total.add(value); + } + } + return formatBigInteger(total); } } diff --git a/src/main/java/com/extendedae_plus/ae/items/InfinityBigIntegerCellItem.java b/src/main/java/com/extendedae_plus/ae/items/InfinityBigIntegerCellItem.java index dde5b96..f76df19 100644 --- a/src/main/java/com/extendedae_plus/ae/items/InfinityBigIntegerCellItem.java +++ b/src/main/java/com/extendedae_plus/ae/items/InfinityBigIntegerCellItem.java @@ -1,7 +1,6 @@ package com.extendedae_plus.ae.items; import com.extendedae_plus.ae.api.storage.InfinityBigIntegerCellInventory; -import com.google.common.base.Preconditions; import net.minecraft.ChatFormatting; import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.LongTag; @@ -14,7 +13,6 @@ import net.minecraft.world.level.Level; import org.jetbrains.annotations.NotNull; import javax.annotation.Nullable; -import java.math.BigInteger; import java.util.List; public class InfinityBigIntegerCellItem extends Item { @@ -39,15 +37,14 @@ public class InfinityBigIntegerCellItem extends Item { tooltip.add(Component.translatable("tooltip.extendedae_plus.infinity_biginteger_cell.summon1")); tooltip.add(Component.translatable("tooltip.extendedae_plus.infinity_biginteger_cell.summon2")); - Preconditions.checkArgument(stack.getItem() == this); - // 仅在 ItemStack 自身存在 UUID 时显示 UUID,避免触发持久化或加载逻辑 + // 优先使用 ItemStack 的 NBT 缓存信息显示 tooltip(客户端不应触发世界 I/O) CompoundTag tag = stack.getTag(); if (tag != null && tag.contains("uuid")) { String uuidStr = tag.getUUID("uuid").toString(); tooltip.add( Component.literal("UUID: ").withStyle(ChatFormatting.GRAY).append(Component.literal(uuidStr).withStyle(ChatFormatting.YELLOW)) ); - // 读取并显示已缓存的种类数量(types),表示当前存储了多少种不同的 AEKey + // types 表示缓存的种类数 if (tag.contains("types")) { try { int types = tag.getInt("types"); @@ -55,27 +52,25 @@ public class InfinityBigIntegerCellItem extends Item { Component.literal("Types: ").withStyle(ChatFormatting.GRAY).append(Component.literal(String.valueOf(types)).withStyle(ChatFormatting.GREEN)) ); } catch (Exception ignored) { - // ignore malformed value } } - // 读取并显示已缓存的 total(支持 long 或 string),使用格式化函数展示友好单位 + // total 支持 long 或 string 两种表现形式 if (tag.contains("total")) { - BigInteger total = BigInteger.ZERO; - Tag t = tag.get("total"); try { + java.math.BigInteger total; + Tag t = tag.get("total"); if (t instanceof LongTag) { - total = BigInteger.valueOf(tag.getLong("total")); + total = java.math.BigInteger.valueOf(tag.getLong("total")); } else { String s = tag.getString("total"); - total = new BigInteger(s); + total = new java.math.BigInteger(s); } + String formatted = InfinityBigIntegerCellInventory.formatBigInteger(total); + tooltip.add( + Component.literal("Byte: ").withStyle(ChatFormatting.GRAY).append(Component.literal(formatted).withStyle(ChatFormatting.AQUA)) + ); } catch (Exception ignored) { - // 解析失败保持为 0 } - String formatted = InfinityBigIntegerCellInventory.formatBigInteger(total); - tooltip.add( - Component.literal("Byte: ").withStyle(ChatFormatting.GRAY).append(Component.literal(formatted).withStyle(ChatFormatting.AQUA)) - ); } } } diff --git a/src/main/java/com/extendedae_plus/integration/jei/JeiRuntimeProxy.java b/src/main/java/com/extendedae_plus/integration/jei/JeiRuntimeProxy.java index 3965a63..bc5276e 100644 --- a/src/main/java/com/extendedae_plus/integration/jei/JeiRuntimeProxy.java +++ b/src/main/java/com/extendedae_plus/integration/jei/JeiRuntimeProxy.java @@ -2,6 +2,8 @@ package com.extendedae_plus.integration.jei; import com.extendedae_plus.mixin.jei.accessor.BookmarkOverlayAccessor; import mezz.jei.api.constants.VanillaTypes; +import mezz.jei.api.forge.ForgeTypes; +import mezz.jei.api.ingredients.IIngredientType; import mezz.jei.api.ingredients.ITypedIngredient; import mezz.jei.api.runtime.IBookmarkOverlay; import mezz.jei.api.runtime.IIngredientListOverlay; @@ -10,8 +12,13 @@ import mezz.jei.gui.bookmarks.BookmarkList; import mezz.jei.gui.bookmarks.IngredientBookmark; import mezz.jei.gui.overlay.elements.IElement; import net.minecraft.world.item.ItemStack; +import net.minecraftforge.fluids.FluidStack; +import net.minecraftforge.fml.ModList; +import org.spongepowered.asm.mixin.Pseudo; import javax.annotation.Nullable; +import java.lang.reflect.Field; +import java.lang.reflect.Method; import java.util.Collections; import java.util.List; import java.util.Optional; @@ -19,10 +26,12 @@ import java.util.Optional; /** * 线程安全地缓存并访问 JEI Runtime。 */ +@Pseudo public final class JeiRuntimeProxy { private static volatile IJeiRuntime RUNTIME; - private JeiRuntimeProxy() {} + private JeiRuntimeProxy() { + } static void setRuntime(IJeiRuntime runtime) { RUNTIME = runtime; @@ -135,9 +144,6 @@ public final class JeiRuntimeProxy { return Collections.emptyList(); } - /** - * 将物品添加到 JEI 书签 - */ public static void addBookmark(ItemStack stack) { IJeiRuntime rt = RUNTIME; if (rt == null) return; @@ -154,6 +160,95 @@ public final class JeiRuntimeProxy { } } + public static void addBookmark(FluidStack fluidStack) { + IJeiRuntime rt = RUNTIME; + if (rt == null) return; + + IBookmarkOverlay overlay = rt.getBookmarkOverlay(); + if (overlay instanceof BookmarkOverlayAccessor accessor) { + BookmarkList list = accessor.eap$getBookmarkList(); + Optional> typedOpt = rt.getIngredientManager() + .createTypedIngredient(ForgeTypes.FLUID_STACK, fluidStack); + typedOpt.ifPresent(typed -> { + IngredientBookmark bookmark = IngredientBookmark.create(typed, rt.getIngredientManager()); + list.add(bookmark); // add 内部会自动保存到配置 + }); + } + } + + /** + * 如果存在 Mekanism/appmek,则将 Mekanism 化学堆栈添加到 JEI 书签。 + */ + public static void addBookmark(Object chemicalStack) { + if (!ModList.get().isLoaded("mekanism") && !ModList.get().isLoaded("appmek")) return; + + IJeiRuntime rt = RUNTIME; + if (rt == null) return; + + IBookmarkOverlay overlay = rt.getBookmarkOverlay(); + if (overlay instanceof BookmarkOverlayAccessor accessor) { + BookmarkList list = accessor.eap$getBookmarkList(); + try { + if (chemicalStack == null) return; + + // Determine Mekanism JEI ingredient type constant by runtime class name + String clsName = chemicalStack.getClass().getName(); + String mekanismJeiClass = "mekanism.client.jei.MekanismJEI"; + Class jeiCls = Class.forName(mekanismJeiClass); + Field typeField = getField(clsName, jeiCls); + + if (typeField == null) return; + Object typeConst = typeField.get(null); + + // Use ingredient manager reflectively to create a typed ingredient + Object ingredientManager = rt.getIngredientManager(); + Method createTypedIngredient = ingredientManager.getClass().getMethod("createTypedIngredient", IIngredientType.class, Object.class); + Object opt = createTypedIngredient.invoke(ingredientManager, typeConst, chemicalStack); + if (!(opt instanceof Optional typedOpt)) return; + if (typedOpt.isPresent()) { + Object typed = typedOpt.get(); + // Find a compatible static create(...) method on IngredientBookmark where + // the second parameter is assignable from the actual ingredientManager instance. + Method createMethod = null; + for (Method m : IngredientBookmark.class.getMethods()) { + if (!m.getName().equals("create")) continue; + Class[] params = m.getParameterTypes(); + if (params.length != 2) continue; + // first param should accept the typed ingredient + boolean firstOk = params[0].isAssignableFrom(typed.getClass()) || params[0].isAssignableFrom(ITypedIngredient.class); + boolean secondOk = params[1].isAssignableFrom(ingredientManager.getClass()); + if (firstOk && secondOk) { + createMethod = m; + break; + } + } + if (createMethod != null) { + Object bookmark = createMethod.invoke(null, typed, ingredientManager); + if (bookmark != null) { + list.add((IngredientBookmark) bookmark); + } + } + } + } catch (Throwable e) { + e.printStackTrace(); + } + } + } + + private static @Nullable Field getField(String clsName, Class jeiCls) throws NoSuchFieldException { + Field typeField = null; + if ("mekanism.api.chemical.gas.GasStack".equals(clsName)) { + typeField = jeiCls.getField("TYPE_GAS"); + } else if ("mekanism.api.chemical.infuse.InfusionStack".equals(clsName)) { + typeField = jeiCls.getField("TYPE_INFUSION"); + } else if ("mekanism.api.chemical.pigment.PigmentStack".equals(clsName)) { + typeField = jeiCls.getField("TYPE_PIGMENT"); + } else if ("mekanism.api.chemical.slurry.SlurryStack".equals(clsName)) { + typeField = jeiCls.getField("TYPE_SLURRY"); + } + return typeField; + } + /** * 从 JEI 书签移除物品 */ diff --git a/src/main/java/com/extendedae_plus/mixin/advancedae/accessor/AdvPatternProviderMenuAdvancedAccessor.java b/src/main/java/com/extendedae_plus/mixin/advancedae/accessor/AdvPatternProviderMenuAdvancedAccessor.java index bf6987e..bd75482 100644 --- a/src/main/java/com/extendedae_plus/mixin/advancedae/accessor/AdvPatternProviderMenuAdvancedAccessor.java +++ b/src/main/java/com/extendedae_plus/mixin/advancedae/accessor/AdvPatternProviderMenuAdvancedAccessor.java @@ -7,6 +7,6 @@ import org.spongepowered.asm.mixin.gen.Accessor; @Mixin(AdvPatternProviderMenu.class) public interface AdvPatternProviderMenuAdvancedAccessor { - @Accessor("logic") + @Accessor(value = "logic", remap = false) AdvPatternProviderLogic eap$logic(); } diff --git a/src/main/java/com/extendedae_plus/mixin/advancedae/menu/AdvPatternProviderMenuAdvancedMixin.java b/src/main/java/com/extendedae_plus/mixin/advancedae/menu/AdvPatternProviderMenuAdvancedMixin.java index db4ae90..7cde360 100644 --- a/src/main/java/com/extendedae_plus/mixin/advancedae/menu/AdvPatternProviderMenuAdvancedMixin.java +++ b/src/main/java/com/extendedae_plus/mixin/advancedae/menu/AdvPatternProviderMenuAdvancedMixin.java @@ -9,6 +9,7 @@ import net.minecraft.world.inventory.MenuType; import net.pedroksl.advanced_ae.common.logic.AdvPatternProviderLogic; import net.pedroksl.advanced_ae.common.logic.AdvPatternProviderLogicHost; import net.pedroksl.advanced_ae.gui.advpatternprovider.AdvPatternProviderMenu; +import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.Unique; @@ -18,7 +19,8 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; @Mixin(AdvPatternProviderMenu.class) public abstract class AdvPatternProviderMenuAdvancedMixin implements PatternProviderMenuAdvancedSync { - @Shadow + @Final + @Shadow(remap = false) protected AdvPatternProviderLogic logic; // 选择一个未占用的 GUI 同步 id(AE2 已用到 7),这里使用 21 以避冲突 @@ -38,7 +40,7 @@ public abstract class AdvPatternProviderMenuAdvancedMixin implements PatternProv } // 构造器尾注入(public ctor) - @Inject(method = "(ILnet/minecraft/world/entity/player/Inventory;Lnet/pedroksl/advanced_ae/common/logic/AdvPatternProviderLogicHost;)V", at = @At("TAIL")) + @Inject(method = "(ILnet/minecraft/world/entity/player/Inventory;Lnet/pedroksl/advanced_ae/common/logic/AdvPatternProviderLogicHost;)V", at = @At("TAIL"), remap = false) private void eap$initAdvancedSync_Public(int id, Inventory playerInventory, AdvPatternProviderLogicHost host, CallbackInfo ci) { try { var l = this.logic; @@ -49,7 +51,7 @@ public abstract class AdvPatternProviderMenuAdvancedMixin implements PatternProv } // 构造器尾注入(protected ctor with MenuType) - @Inject(method = "(Lnet/minecraft/world/inventory/MenuType;ILnet/minecraft/world/entity/player/Inventory;Lnet/pedroksl/advanced_ae/common/logic/AdvPatternProviderLogicHost;)V", at = @At("TAIL")) + @Inject(method = "(Lnet/minecraft/world/inventory/MenuType;ILnet/minecraft/world/entity/player/Inventory;Lnet/pedroksl/advanced_ae/common/logic/AdvPatternProviderLogicHost;)V", at = @At("TAIL"), remap = false) private void eap$initAdvancedSync_Protected(MenuType menuType, int id, Inventory playerInventory, AdvPatternProviderLogicHost host, CallbackInfo ci) { try { var l = this.logic; diff --git a/src/main/java/com/extendedae_plus/mixin/advancedae/menu/AdvPatternProviderMenuDoublingMixin.java b/src/main/java/com/extendedae_plus/mixin/advancedae/menu/AdvPatternProviderMenuDoublingMixin.java index 7e63986..8b2e4e9 100644 --- a/src/main/java/com/extendedae_plus/mixin/advancedae/menu/AdvPatternProviderMenuDoublingMixin.java +++ b/src/main/java/com/extendedae_plus/mixin/advancedae/menu/AdvPatternProviderMenuDoublingMixin.java @@ -9,6 +9,7 @@ import net.minecraft.world.inventory.MenuType; import net.pedroksl.advanced_ae.common.logic.AdvPatternProviderLogic; import net.pedroksl.advanced_ae.common.logic.AdvPatternProviderLogicHost; import net.pedroksl.advanced_ae.gui.advpatternprovider.AdvPatternProviderMenu; +import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.Unique; @@ -18,7 +19,8 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; @Mixin(AdvPatternProviderMenu.class) public abstract class AdvPatternProviderMenuDoublingMixin implements PatternProviderMenuDoublingSync { - @Shadow + @Final + @Shadow(remap = false) protected AdvPatternProviderLogic logic; @Unique @@ -35,7 +37,7 @@ public abstract class AdvPatternProviderMenuDoublingMixin implements PatternProv } } - @Inject(method = "(ILnet/minecraft/world/entity/player/Inventory;Lnet/pedroksl/advanced_ae/common/logic/AdvPatternProviderLogicHost;)V", at = @At("TAIL")) + @Inject(method = "(ILnet/minecraft/world/entity/player/Inventory;Lnet/pedroksl/advanced_ae/common/logic/AdvPatternProviderLogicHost;)V", at = @At("TAIL"), remap = false) private void eap$initSmartSync_Public(int id, Inventory playerInventory, AdvPatternProviderLogicHost host, CallbackInfo ci) { try { var l = this.logic; @@ -45,7 +47,7 @@ public abstract class AdvPatternProviderMenuDoublingMixin implements PatternProv } catch (Throwable ignored) {} } - @Inject(method = "(Lnet/minecraft/world/inventory/MenuType;ILnet/minecraft/world/entity/player/Inventory;Lnet/pedroksl/advanced_ae/common/logic/AdvPatternProviderLogicHost;)V", at = @At("TAIL")) + @Inject(method = "(Lnet/minecraft/world/inventory/MenuType;ILnet/minecraft/world/entity/player/Inventory;Lnet/pedroksl/advanced_ae/common/logic/AdvPatternProviderLogicHost;)V", at = @At("TAIL"), remap = false) private void eap$initSmartSync_Protected(MenuType menuType, int id, Inventory playerInventory, AdvPatternProviderLogicHost host, CallbackInfo ci) { try { var l = this.logic; diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/accessor/MEStorageMenuAccessor.java b/src/main/java/com/extendedae_plus/mixin/ae2/accessor/MEStorageMenuAccessor.java index a3662ac..52ce7fe 100644 --- a/src/main/java/com/extendedae_plus/mixin/ae2/accessor/MEStorageMenuAccessor.java +++ b/src/main/java/com/extendedae_plus/mixin/ae2/accessor/MEStorageMenuAccessor.java @@ -10,23 +10,23 @@ import org.spongepowered.asm.mixin.gen.Accessor; @Mixin(MEStorageMenu.class) public interface MEStorageMenuAccessor { - @Accessor("storage") + @Accessor(value = "storage", remap = false) @Nullable MEStorage getStorage(); - @Accessor("powerSource") + @Accessor(value = "powerSource", remap = false) @Nullable IEnergySource getPowerSource(); - @Accessor("hasPower") + @Accessor(value = "hasPower", remap = false) boolean getHasPower(); // Access client-side config manager mirror used for syncing settings - @Accessor("clientCM") + @Accessor(value = "clientCM", remap = false) IConfigManager getClientCM(); // Access server-side config manager - @Accessor("serverCM") + @Accessor(value = "serverCM", remap = false) @Nullable IConfigManager getServerCM(); } diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/accessor/PatternEncodingTermMenuAccessor.java b/src/main/java/com/extendedae_plus/mixin/ae2/accessor/PatternEncodingTermMenuAccessor.java index 908962c..c87956a 100644 --- a/src/main/java/com/extendedae_plus/mixin/ae2/accessor/PatternEncodingTermMenuAccessor.java +++ b/src/main/java/com/extendedae_plus/mixin/ae2/accessor/PatternEncodingTermMenuAccessor.java @@ -7,9 +7,9 @@ import org.spongepowered.asm.mixin.gen.Accessor; @Mixin(PatternEncodingTermMenu.class) public interface PatternEncodingTermMenuAccessor { - @Accessor("encodedPatternSlot") + @Accessor(value = "encodedPatternSlot",remap = false) RestrictedInputSlot eap$getEncodedPatternSlot(); - @Accessor("blankPatternSlot") + @Accessor(value = "blankPatternSlot",remap = false) RestrictedInputSlot eap$getBlankPatternSlot(); } diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/accessor/PatternProviderLogicAccessor.java b/src/main/java/com/extendedae_plus/mixin/ae2/accessor/PatternProviderLogicAccessor.java index 1c93b75..6155a13 100644 --- a/src/main/java/com/extendedae_plus/mixin/ae2/accessor/PatternProviderLogicAccessor.java +++ b/src/main/java/com/extendedae_plus/mixin/ae2/accessor/PatternProviderLogicAccessor.java @@ -8,9 +8,9 @@ import org.spongepowered.asm.mixin.gen.Accessor; @Mixin(PatternProviderLogic.class) public interface PatternProviderLogicAccessor { - @Accessor("host") + @Accessor(value = "host", remap = false) PatternProviderLogicHost eap$host(); - @Accessor("mainNode") + @Accessor(value = "mainNode", remap = false) IManagedGridNode eap$mainNode(); } diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/accessor/PatternProviderLogicPatternInputsAccessor.java b/src/main/java/com/extendedae_plus/mixin/ae2/accessor/PatternProviderLogicPatternInputsAccessor.java index 2b898b5..3f62fd4 100644 --- a/src/main/java/com/extendedae_plus/mixin/ae2/accessor/PatternProviderLogicPatternInputsAccessor.java +++ b/src/main/java/com/extendedae_plus/mixin/ae2/accessor/PatternProviderLogicPatternInputsAccessor.java @@ -9,6 +9,6 @@ import java.util.Set; @Mixin(PatternProviderLogic.class) public interface PatternProviderLogicPatternInputsAccessor { - @Accessor("patternInputs") + @Accessor(value = "patternInputs",remap = false) Set eap$patternInputs(); } diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/accessor/PatternProviderMenuAdvancedAccessor.java b/src/main/java/com/extendedae_plus/mixin/ae2/accessor/PatternProviderMenuAdvancedAccessor.java index 0a0234d..18ca7b7 100644 --- a/src/main/java/com/extendedae_plus/mixin/ae2/accessor/PatternProviderMenuAdvancedAccessor.java +++ b/src/main/java/com/extendedae_plus/mixin/ae2/accessor/PatternProviderMenuAdvancedAccessor.java @@ -7,6 +7,6 @@ import org.spongepowered.asm.mixin.gen.Accessor; @Mixin(PatternProviderMenu.class) public interface PatternProviderMenuAdvancedAccessor { - @Accessor("logic") + @Accessor(value = "logic", remap = false) PatternProviderLogic eap$logic(); } diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/autopattern/CraftingTreeNodeAccessor.java b/src/main/java/com/extendedae_plus/mixin/ae2/autopattern/CraftingTreeNodeAccessor.java index 3f4794a..6d5b7b8 100644 --- a/src/main/java/com/extendedae_plus/mixin/ae2/autopattern/CraftingTreeNodeAccessor.java +++ b/src/main/java/com/extendedae_plus/mixin/ae2/autopattern/CraftingTreeNodeAccessor.java @@ -7,6 +7,6 @@ import org.spongepowered.asm.mixin.gen.Accessor; @Mixin(CraftingTreeNode.class) public interface CraftingTreeNodeAccessor { - @Accessor("what") + @Accessor(value = "what", remap = false) AEKey eap$getWhat(); } diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/autopattern/CraftingTreeProcessMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/autopattern/CraftingTreeProcessMixin.java index d6ce278..8f96a90 100644 --- a/src/main/java/com/extendedae_plus/mixin/ae2/autopattern/CraftingTreeProcessMixin.java +++ b/src/main/java/com/extendedae_plus/mixin/ae2/autopattern/CraftingTreeProcessMixin.java @@ -33,7 +33,8 @@ public abstract class CraftingTreeProcessMixin { @ModifyVariable( method = "(Lappeng/api/networking/crafting/ICraftingService;Lappeng/crafting/CraftingCalculation;Lappeng/api/crafting/IPatternDetails;Lappeng/crafting/CraftingTreeNode;)V", at = @At("HEAD"), - argsOnly = true + argsOnly = true, + remap = false ) private static IPatternDetails eap$replaceDetailsAtHead(IPatternDetails original, ICraftingService cc, CraftingCalculation job, IPatternDetails details, CraftingTreeNode craftingTreeNode) { try { diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/client/gui/AEBaseScreenMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/client/gui/AEBaseScreenMixin.java index 5d089eb..5f74500 100644 --- a/src/main/java/com/extendedae_plus/mixin/ae2/client/gui/AEBaseScreenMixin.java +++ b/src/main/java/com/extendedae_plus/mixin/ae2/client/gui/AEBaseScreenMixin.java @@ -312,7 +312,7 @@ public abstract class AEBaseScreenMixin { } - @Shadow + @Shadow(remap = false) protected void setTextContent(String id, Component content) {}; @Inject(method = "updateBeforeRender", at = @At("RETURN"), remap = false) diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/menu/CraftConfirmMenuGoBackMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/menu/CraftConfirmMenuGoBackMixin.java index 0bbc164..c5e2d23 100644 --- a/src/main/java/com/extendedae_plus/mixin/ae2/menu/CraftConfirmMenuGoBackMixin.java +++ b/src/main/java/com/extendedae_plus/mixin/ae2/menu/CraftConfirmMenuGoBackMixin.java @@ -1,10 +1,13 @@ package com.extendedae_plus.mixin.ae2.menu; +import appeng.api.stacks.AEFluidKey; +import appeng.api.stacks.AEItemKey; import appeng.menu.me.crafting.CraftConfirmMenu; import appeng.menu.me.crafting.CraftingPlanSummary; import appeng.menu.me.crafting.CraftingPlanSummaryEntry; import com.extendedae_plus.integration.jei.JeiRuntimeProxy; import net.minecraft.client.gui.screens.Screen; +import net.minecraftforge.fml.ModList; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; @@ -44,9 +47,27 @@ public class CraftConfirmMenuGoBackMixin { // 仅在按住 shift 时为缺失的条目添加 JEI 书签 for (CraftingPlanSummaryEntry entry : entries) { if (entry.getMissingAmount() > 0) { - JeiRuntimeProxy.addBookmark(entry.getWhat().wrapForDisplayOrFilter()); + var what = entry.getWhat(); + if (what instanceof AEItemKey aeItemKey) { + JeiRuntimeProxy.addBookmark(aeItemKey.getReadOnlyStack()); + } else if (what instanceof AEFluidKey aeFluidKey) { + JeiRuntimeProxy.addBookmark(aeFluidKey.toStack(1000)); + } else if (ModList.get().isLoaded("appmek") && ModList.get().isLoaded("mekanism")) { + try { + if (what != null) { + // avoid compile-time dependency on MekanismKey by reflection + Class mekanismKeyCls = Class.forName("me.ramidzkh.mekae2.ae2.MekanismKey"); + if (mekanismKeyCls.isInstance(what)) { + java.lang.reflect.Method m = mekanismKeyCls.getMethod("getStack"); + Object stack = m.invoke(what); + JeiRuntimeProxy.addBookmark(stack); + } + } + } catch (Throwable ignored) {} + } } } - } catch (Throwable ignored) {} + } catch (Throwable ignored) { + } } } \ No newline at end of file diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/menu/PatternEncodingTermMenuMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/menu/PatternEncodingTermMenuMixin.java index a9cc70a..6c76e0b 100644 --- a/src/main/java/com/extendedae_plus/mixin/ae2/menu/PatternEncodingTermMenuMixin.java +++ b/src/main/java/com/extendedae_plus/mixin/ae2/menu/PatternEncodingTermMenuMixin.java @@ -14,6 +14,7 @@ import appeng.menu.slot.RestrictedInputSlot; import com.extendedae_plus.mixin.ae2.accessor.MEStorageMenuAccessor; import net.minecraft.world.entity.player.Inventory; import net.minecraft.world.inventory.MenuType; +import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.Unique; @@ -28,7 +29,8 @@ public abstract class PatternEncodingTermMenuMixin { @Unique private boolean eap$blankAutoFilled = false; - @Shadow + @Final + @Shadow(remap = false) private RestrictedInputSlot blankPatternSlot; @Unique @@ -87,7 +89,7 @@ public abstract class PatternEncodingTermMenuMixin { } @Inject(method = "(Lnet/minecraft/world/inventory/MenuType;ILnet/minecraft/world/entity/player/Inventory;Lappeng/helpers/IPatternTerminalMenuHost;Z)V", - at = @At("TAIL")) + at = @At("TAIL"), remap = false) private void eap$autoFillBlankPattern(MenuType menuType, int id, Inventory ip, IPatternTerminalMenuHost host, boolean bindInventory, CallbackInfo ci) { @@ -95,7 +97,7 @@ public abstract class PatternEncodingTermMenuMixin { } @Inject(method = "(ILnet/minecraft/world/entity/player/Inventory;Lappeng/helpers/IPatternTerminalMenuHost;)V", - at = @At("TAIL")) + at = @At("TAIL"), remap = false) private void eap$autoFillCtor3(int id, Inventory ip, IPatternTerminalMenuHost host, CallbackInfo ci) { eap$tryFill(host, ip); } diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/menu/PatternProviderMenuAdvancedMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/menu/PatternProviderMenuAdvancedMixin.java index 8a1fb05..28174ac 100644 --- a/src/main/java/com/extendedae_plus/mixin/ae2/menu/PatternProviderMenuAdvancedMixin.java +++ b/src/main/java/com/extendedae_plus/mixin/ae2/menu/PatternProviderMenuAdvancedMixin.java @@ -9,6 +9,7 @@ import com.extendedae_plus.api.AdvancedBlockingHolder; import com.extendedae_plus.api.PatternProviderMenuAdvancedSync; import net.minecraft.world.entity.player.Inventory; import net.minecraft.world.inventory.MenuType; +import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.Unique; @@ -20,7 +21,8 @@ import static com.extendedae_plus.util.ExtendedAELogger.LOGGER; @Mixin(PatternProviderMenu.class) public abstract class PatternProviderMenuAdvancedMixin implements PatternProviderMenuAdvancedSync { - @Shadow + @Final + @Shadow(remap = false) protected PatternProviderLogic logic; // 选择一个未占用的 GUI 同步 id(AE2 已用到 7),这里使用 20 以避冲突 @@ -40,7 +42,7 @@ public abstract class PatternProviderMenuAdvancedMixin implements PatternProvide } // 构造器尾注入(public ctor) - @Inject(method = "(ILnet/minecraft/world/entity/player/Inventory;Lappeng/helpers/patternprovider/PatternProviderLogicHost;)V", at = @At("TAIL")) + @Inject(method = "(ILnet/minecraft/world/entity/player/Inventory;Lappeng/helpers/patternprovider/PatternProviderLogicHost;)V", at = @At("TAIL"), remap = false) private void eap$initAdvancedSync_Public(int id, Inventory playerInventory, PatternProviderLogicHost host, CallbackInfo ci) { try { var l = this.logic; @@ -51,7 +53,7 @@ public abstract class PatternProviderMenuAdvancedMixin implements PatternProvide } // 构造器尾注入(protected ctor with MenuType) - @Inject(method = "(Lnet/minecraft/world/inventory/MenuType;ILnet/minecraft/world/entity/player/Inventory;Lappeng/helpers/patternprovider/PatternProviderLogicHost;)V", at = @At("TAIL")) + @Inject(method = "(Lnet/minecraft/world/inventory/MenuType;ILnet/minecraft/world/entity/player/Inventory;Lappeng/helpers/patternprovider/PatternProviderLogicHost;)V", at = @At("TAIL"), remap = false) private void eap$initAdvancedSync_Protected(MenuType menuType, int id, Inventory playerInventory, PatternProviderLogicHost host, CallbackInfo ci) { try { var l = this.logic; diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/menu/PatternProviderMenuDoublingMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/menu/PatternProviderMenuDoublingMixin.java index 822c22f..3403b9d 100644 --- a/src/main/java/com/extendedae_plus/mixin/ae2/menu/PatternProviderMenuDoublingMixin.java +++ b/src/main/java/com/extendedae_plus/mixin/ae2/menu/PatternProviderMenuDoublingMixin.java @@ -9,6 +9,7 @@ import com.extendedae_plus.api.PatternProviderMenuDoublingSync; import com.extendedae_plus.api.SmartDoublingHolder; import net.minecraft.world.entity.player.Inventory; import net.minecraft.world.inventory.MenuType; +import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.Unique; @@ -20,7 +21,8 @@ import static com.extendedae_plus.util.ExtendedAELogger.LOGGER; @Mixin(PatternProviderMenu.class) public abstract class PatternProviderMenuDoublingMixin implements PatternProviderMenuDoublingSync { - @Shadow + @Final + @Shadow(remap = false) protected PatternProviderLogic logic; @Unique @@ -37,7 +39,7 @@ public abstract class PatternProviderMenuDoublingMixin implements PatternProvide } } - @Inject(method = "(ILnet/minecraft/world/entity/player/Inventory;Lappeng/helpers/patternprovider/PatternProviderLogicHost;)V", at = @At("TAIL")) + @Inject(method = "(ILnet/minecraft/world/entity/player/Inventory;Lappeng/helpers/patternprovider/PatternProviderLogicHost;)V", at = @At("TAIL"), remap = false) private void eap$initSmartSync_Public(int id, Inventory playerInventory, PatternProviderLogicHost host, CallbackInfo ci) { try { var l = this.logic; @@ -49,7 +51,7 @@ public abstract class PatternProviderMenuDoublingMixin implements PatternProvide } } - @Inject(method = "(Lnet/minecraft/world/inventory/MenuType;ILnet/minecraft/world/entity/player/Inventory;Lappeng/helpers/patternprovider/PatternProviderLogicHost;)V", at = @At("TAIL")) + @Inject(method = "(Lnet/minecraft/world/inventory/MenuType;ILnet/minecraft/world/entity/player/Inventory;Lappeng/helpers/patternprovider/PatternProviderLogicHost;)V", at = @At("TAIL"), remap = false) private void eap$initSmartSync_Protected(MenuType menuType, int id, Inventory playerInventory, PatternProviderLogicHost host, CallbackInfo ci) { try { var l = this.logic; diff --git a/src/main/java/com/extendedae_plus/mixin/extendedae/container/ContainerWirelessExPatternTerminalMixin.java b/src/main/java/com/extendedae_plus/mixin/extendedae/container/ContainerWirelessExPatternTerminalMixin.java index 4355a40..ce7a1ab 100644 --- a/src/main/java/com/extendedae_plus/mixin/extendedae/container/ContainerWirelessExPatternTerminalMixin.java +++ b/src/main/java/com/extendedae_plus/mixin/extendedae/container/ContainerWirelessExPatternTerminalMixin.java @@ -32,7 +32,7 @@ public abstract class ContainerWirelessExPatternTerminalMixin implements IAction private Player epp$player; // 明确目标构造签名:(int, Inventory, HostWirelessExPAT) - @Inject(method = "(ILnet/minecraft/world/entity/player/Inventory;Lcom/glodblock/github/extendedae/common/me/itemhost/HostWirelessExPAT;)V", at = @At("TAIL"), require = 0) + @Inject(method = "(ILnet/minecraft/world/entity/player/Inventory;Lcom/glodblock/github/extendedae/common/me/itemhost/HostWirelessExPAT;)V", at = @At("TAIL"), require = 0, remap = false) private void init(int id, net.minecraft.world.entity.player.Inventory playerInventory, HostWirelessExPAT host, CallbackInfo ci) { this.epp$player = playerInventory.player; // 注册上传动作:参数顺序必须与客户端 CGenericPacket 保持一致 diff --git a/src/main/java/com/extendedae_plus/mixin/jei/accessor/BookmarkOverlayAccessor.java b/src/main/java/com/extendedae_plus/mixin/jei/accessor/BookmarkOverlayAccessor.java index 508845d..0a526ec 100644 --- a/src/main/java/com/extendedae_plus/mixin/jei/accessor/BookmarkOverlayAccessor.java +++ b/src/main/java/com/extendedae_plus/mixin/jei/accessor/BookmarkOverlayAccessor.java @@ -7,6 +7,6 @@ import org.spongepowered.asm.mixin.gen.Accessor; @Mixin(BookmarkOverlay.class) public interface BookmarkOverlayAccessor { - @Accessor("bookmarkList") + @Accessor(value = "bookmarkList", remap = false) BookmarkList eap$getBookmarkList(); } diff --git a/src/main/java/com/extendedae_plus/util/storage/InfinityDataStorage.java b/src/main/java/com/extendedae_plus/util/storage/InfinityDataStorage.java index 4ef9036..71ec86a 100644 --- a/src/main/java/com/extendedae_plus/util/storage/InfinityDataStorage.java +++ b/src/main/java/com/extendedae_plus/util/storage/InfinityDataStorage.java @@ -44,8 +44,8 @@ public class InfinityDataStorage { */ public CompoundTag serializeNBT() { CompoundTag nbt = new CompoundTag(); - nbt.put("keys", keys); - nbt.put("amounts", amounts); + nbt.put("keys", this.keys); + nbt.put("amounts", this.amounts); return nbt; } diff --git a/src/main/java/com/extendedae_plus/util/storage/InfinityStorageManager.java b/src/main/java/com/extendedae_plus/util/storage/InfinityStorageManager.java index e24a5ab..26d880a 100644 --- a/src/main/java/com/extendedae_plus/util/storage/InfinityStorageManager.java +++ b/src/main/java/com/extendedae_plus/util/storage/InfinityStorageManager.java @@ -2,100 +2,124 @@ package com.extendedae_plus.util.storage; import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.ListTag; +import net.minecraft.nbt.NbtIo; +import net.minecraft.nbt.Tag; import net.minecraft.server.level.ServerLevel; -import net.minecraft.world.level.saveddata.SavedData; -import org.jetbrains.annotations.NotNull; +import net.minecraft.world.level.storage.LevelResource; +import org.jetbrains.annotations.Nullable; +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardCopyOption; +import java.nio.file.AtomicMoveNotSupportedException; import java.util.HashMap; import java.util.Map; import java.util.UUID; /** * InfinityStorageManager - *

- * 世界级别的持久化容器,集中管理所有 InfinityBigInteger 存储单元的序列化数据。 - * 功能要点: - * - 在世界加载时从存档恢复所有 cell 的数据 - * - 提供按 UUID 获取/创建单个 cell 的数据容器 - * - 在世界保存时将内存数据打包为 NBT 写回存档 + * + * 替代之前基于 SavedData 的实现,本类使用手动文件 I/O 在 world 目录下保存 NBT 数据, + * 以避免依赖 Minecraft 的 SavedData 机制。 + * 数据保持与之前兼容的 NBT 结构:根 Compound 包含 "list" => ListTag of Compound { uuid, data } */ -public class InfinityStorageManager extends SavedData { +public class InfinityStorageManager { + + public static final String FILE_NAME = "eap_infinity_biginteger_cells.dat"; + + /** 全局单例,由 mod 在 world load 时初始化 */ + public static volatile InfinityStorageManager INSTANCE = new InfinityStorageManager(); - /** - * SavedData 文件名常量 - */ - public static final String FILE_NAME = "eap_infinity_biginteger_cells"; - /** - * 全局单例实例(在世界加载时由 InfiniteBigIntegerStorageCell.onLevelLoad 填充) - */ - public static InfinityStorageManager INSTANCE = null; - /** - * UUID -> 数据 的内存映射 - */ private final Map cells = new HashMap<>(); + private Path saveFilePath = null; + public InfinityStorageManager() { - setDirty(); } /** - * 从 NBT 构造:用于在世界加载时从存档恢复数据 + * 初始化并从 world 保存目录加载数据;若文件不存在则保持空状态 */ - public InfinityStorageManager(CompoundTag nbt) { - ListTag cellList = nbt.getList("list", CompoundTag.TAG_COMPOUND); - for (int i = 0; i < cellList.size(); i++) { - CompoundTag cell = cellList.getCompound(i); - cells.put(cell.getUUID("uuid"), InfinityDataStorage.loadFromNBT(cell.getCompound("data"))); + public void initFromWorld(@Nullable ServerLevel serverLevel) { + if (serverLevel == null) return; + try { + File worldFolder = serverLevel.getServer().getWorldPath(LevelResource.ROOT).toFile(); + // 保存到 world// 文件夹下,避免与其它 mod 冲突 + File modDir = new File(worldFolder, "data"); + if (!modDir.exists()) modDir.mkdirs(); + saveFilePath = new File(modDir, FILE_NAME).toPath(); + if (Files.exists(saveFilePath)) { + CompoundTag root = NbtIo.readCompressed(saveFilePath.toFile()); + ListTag cellList = root.getList("list", Tag.TAG_COMPOUND); + for (int i = 0; i < cellList.size(); i++) { + CompoundTag cell = cellList.getCompound(i); + cells.put(cell.getUUID("uuid"), InfinityDataStorage.loadFromNBT(cell.getCompound("data"))); + } + } + } catch (IOException e) { + // 读取失败保持空,并打印栈追踪以便调试 + e.printStackTrace(); } - setDirty(); } /** - * 根据给定的 ServerLevel 获取或创建该世界对应的 SavedData 实例并缓存到 INSTANCE + * 保存当前内存数据到文件(会覆盖已有文件) */ - public static InfinityStorageManager getForLevel(ServerLevel level) { - if (INSTANCE == null && level != null) { - INSTANCE = level.getDataStorage().computeIfAbsent(InfinityStorageManager::new, InfinityStorageManager::new, FILE_NAME); + public synchronized void saveToFile() { + if (saveFilePath == null) return; + try { + CompoundTag root = new CompoundTag(); + ListTag cellList = new ListTag(); + for (Map.Entry entry : cells.entrySet()) { + // 跳过可能的 null key,防止写入时 NPE + if (entry.getKey() == null || entry.getValue() == null) continue; + CompoundTag cell = new CompoundTag(); + cell.putUUID("uuid", entry.getKey()); + cell.put("data", entry.getValue().serializeNBT()); + cellList.add(cell); + } + root.put("list", cellList); + // 使用压缩写入到临时文件,然后原子替换目标文件以避免半成品/0字节文件 + Path tmp = saveFilePath.resolveSibling(FILE_NAME + ".tmp"); + File tmpFile = tmp.toFile(); + // 确保临时文件的目录存在 + if (tmpFile.getParentFile() != null && !tmpFile.getParentFile().exists()) { + tmpFile.getParentFile().mkdirs(); + } + NbtIo.writeCompressed(root, tmpFile); + try { + Files.move(tmp, saveFilePath, StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.ATOMIC_MOVE); + } catch (AtomicMoveNotSupportedException ex) { + // 若底层文件系统不支持原子移动,退回到非原子替换 + Files.move(tmp, saveFilePath, StandardCopyOption.REPLACE_EXISTING); + } + } catch (IOException e) { + e.printStackTrace(); } - return INSTANCE; } - @Override - public @NotNull CompoundTag save(@NotNull CompoundTag nbt) { - // 将内存中的所有 cell 序列化为一个 ListTag - ListTag cellList = new ListTag(); - for (Map.Entry entry : cells.entrySet()) { - CompoundTag cell = new CompoundTag(); - cell.putUUID("uuid", entry.getKey()); - cell.put("data", entry.getValue().serializeNBT()); - cellList.add(cell); - } - nbt.put("list", cellList); - return nbt; - } - - /** - * 更新或添加某个 UUID 对应的数据并标记为脏(需要保存) - */ public void updateCell(UUID uuid, InfinityDataStorage infinityDataStorage) { + if (uuid == null) return; // 忽略无效 UUID cells.put(uuid, infinityDataStorage); - setDirty(); + saveToFile(); } - /** - * 获取或创建某个 UUID 对应的数据容器 - */ public InfinityDataStorage getOrCreateCell(UUID uuid) { + if (uuid == null) { + return InfinityDataStorage.EMPTY; + } if (!cells.containsKey(uuid)) { - updateCell(uuid, new InfinityDataStorage()); + InfinityDataStorage newCell = new InfinityDataStorage(); + cells.put(uuid, newCell); + saveToFile(); } return cells.get(uuid); } - /** - * 修改某个 UUID 对应的键与数量列表并保存(新的签名,stackAmounts 为 ListTag 字符串列表) - */ public void modifyCell(UUID cellID, ListTag stackKeys, ListTag stackAmounts) { + if (cellID == null) return; InfinityDataStorage cellToModify = getOrCreateCell(cellID); if (stackKeys != null && stackAmounts != null) { cellToModify.keys = stackKeys; @@ -104,11 +128,9 @@ public class InfinityStorageManager extends SavedData { updateCell(cellID, cellToModify); } - /** - * 删除某个 UUID 的持久化记录并标记为脏 - */ public void removeCell(UUID uuid) { + if (uuid == null) return; cells.remove(uuid); - setDirty(); + saveToFile(); } } diff --git a/src/main/resources/extendedae_plus.mixins.json b/src/main/resources/extendedae_plus.mixins.json index a1e9d61..aaad6df 100644 --- a/src/main/resources/extendedae_plus.mixins.json +++ b/src/main/resources/extendedae_plus.mixins.json @@ -16,8 +16,8 @@ "ae2.accessor.PatternAccessTermScreenAccessor", "ae2.accessor.PatternAccessTermScreenSlotsRowAccessor", "ae2.client.gui.AEBaseScreenMixin", - "ae2.client.gui.PatternEncodingTermScreenMixin", "ae2.client.gui.InterfaceScreenMixin", + "ae2.client.gui.PatternEncodingTermScreenMixin", "ae2.client.gui.PatternProviderCloseMixin", "ae2.client.gui.PatternProviderScreenMixin", "ae2.client.gui.SlotGridLayoutMixin",