From 572b6e6f17b455e97bba63fd7b9e0c4befba5931 Mon Sep 17 00:00:00 2001
From: C-H716 <1536152356@qq.com>
Date: Fri, 19 Sep 2025 18:16:18 +0800
Subject: [PATCH 01/34] =?UTF-8?q?fix:=20=E4=BD=BF=E7=94=A8=E8=87=AA?=
=?UTF-8?q?=E5=AE=9A=E4=B9=89I/O=E5=AD=98=E5=82=A8=E7=AE=A1=E7=90=86?=
=?UTF-8?q?=E5=99=A8=E6=9B=BF=E4=BB=A3=E5=8E=9F=E6=9C=89SavedData?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../com/extendedae_plus/ExtendedAEPlus.java | 10 +-
.../InfinityBigIntegerCellInventory.java | 235 +++++++++---------
.../ae/items/InfinityBigIntegerCellItem.java | 27 +-
.../util/storage/InfinityDataStorage.java | 4 +-
.../util/storage/InfinityStorageManager.java | 143 ++++++-----
5 files changed, 206 insertions(+), 213 deletions(-)
diff --git a/src/main/java/com/extendedae_plus/ExtendedAEPlus.java b/src/main/java/com/extendedae_plus/ExtendedAEPlus.java
index 25d675e..ca77fbf 100644
--- a/src/main/java/com/extendedae_plus/ExtendedAEPlus.java
+++ b/src/main/java/com/extendedae_plus/ExtendedAEPlus.java
@@ -53,12 +53,11 @@ public class ExtendedAEPlus {
// 注册到Forge事件总线
MinecraftForge.EVENT_BUS.register(this);
MinecraftForge.EVENT_BUS.addListener(ExtendedAEPlus::onLevelLoad);
- // 注册通用配置
- ModConfig.init();
- // 注册 InfinityBigIntegerCellInventory 的事件监听(tick flush 与停止时 flush)
+ // 注册每秒合并持久化队列的事件监听(Server tick end + stopping)
MinecraftForge.EVENT_BUS.addListener(InfinityBigIntegerCellInventory::onServerTick);
MinecraftForge.EVENT_BUS.addListener(InfinityBigIntegerCellInventory::onServerStopping);
-// ModLoadingContext.get().registerConfig(ModConfig.Type.COMMON, ModConfigs.COMMON_SPEC);
+ // 注册通用配置
+ ModConfig.init();
}
/**
@@ -120,7 +119,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..d23c236 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
@@ -24,10 +24,10 @@ import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.RoundingMode;
import java.text.DecimalFormat;
+import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentLinkedQueue;
-import static com.extendedae_plus.util.ExtendedAELogger.LOGGER;
/**
* InfinityBigIntegerCellInventory
*
@@ -48,6 +48,8 @@ public class InfinityBigIntegerCellInventory implements StorageCell {
// 待持久化队列(用于 debounce:在服务器 tick 中合并持久化)
private static final ConcurrentLinkedQueue PENDING_PERSIST = new ConcurrentLinkedQueue<>();
+ // 用于按 tick 计数,每 20 tick(约 1 秒)触发一次合并写入
+ private static int TICK_COUNTER = 0;
// 数字格式化对象,保留两位小数(复用以减少对象分配)
private static final DecimalFormat DF = new DecimalFormat("#.##");
@@ -59,8 +61,7 @@ public class InfinityBigIntegerCellInventory implements StorageCell {
private Object2ObjectMap storedMap = null;
// 标记是否已持久化到 SavedData
private boolean isPersisted = true;
- // 缓存的总存储量,避免每次调用进行全表扫描
- private BigInteger totalStored = BigInteger.ZERO;
+
/**
* 私有构造器:通过 createInventory 工厂方法调用
@@ -70,59 +71,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 +93,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());
}
}
@@ -176,34 +137,37 @@ public class InfinityBigIntegerCellInventory implements StorageCell {
// 判断物品堆栈是否有UUID
public boolean hasUUID() {
- return stack.hasTag() && stack.getOrCreateTag().contains("uuid");
+ return this.stack.hasTag() && this.stack.getOrCreateTag().contains("uuid");
}
// 获取物品堆栈的UUID
public UUID getUUID() {
- if (this.hasUUID())
- return stack.getOrCreateTag().getUUID("uuid");
- else
+ if (this.hasUUID()) {
+ return this.stack.getOrCreateTag().getUUID("uuid");
+ } else {
return null;
+ }
}
// 获取或初始化存储映射
private Object2ObjectMap getCellStoredMap() {
- if (storedMap == null) {
- storedMap = new Object2ObjectOpenHashMap<>();
+ if (this.storedMap == null) {
+ this.storedMap = new Object2ObjectOpenHashMap<>();
this.loadCellStoredMap();
}
- return storedMap;
+ return this.storedMap;
}
// 从存储中加载物品映射
private void loadCellStoredMap() {
boolean corruptedTag = false; // 标记数据是否损坏
- if (!stack.hasTag()) return;
+ if (!this.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 +185,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();
}
@@ -238,14 +200,37 @@ public class InfinityBigIntegerCellInventory implements StorageCell {
// 标记数据需要保存,并通知容器或直接持久化
private void saveChanges() {
// 标记为未持久化,交由容器或延迟任务合并写入以减少 I/O
- isPersisted = false;
- if (container != null) {
- // 当存在容器时,优先让容器统一处理持久化
- container.saveChanges();
- } else {
- // 如果没有容器,入队等待服务器 tick 在主线程统一持久化,避免频繁 I/O
- if (!PENDING_PERSIST.contains(this)) {
- PENDING_PERSIST.offer(this);
+ this.isPersisted = false;
+ // 将本实例加入待处理队列(去重)
+ if (!PENDING_PERSIST.contains(this)) {
+ PENDING_PERSIST.add(this);
+ }
+ if (this.container != null) {
+ // 当存在容器时,仍然通知容器以便 AE2 在合并时回调 persist()
+ this.container.saveChanges();
+ }
+ }
+
+ /**
+ * 每个服务器 tick 调用一次,用于按秒合并并执行待持久化项的 persist()
+ */
+ public static void onServerTick(TickEvent.ServerTickEvent event) {
+ if (event.phase != TickEvent.Phase.END) return;
+ TICK_COUNTER++;
+ if (TICK_COUNTER % 20 != 0) return; // 每 20 tick(约 1 秒)执行一次
+ onServerStopping(null);
+ }
+
+ /**
+ * 在服务器停止时强制刷新所有待持久化项
+ */
+ public static void onServerStopping(ServerStoppingEvent event) {
+ InfinityBigIntegerCellInventory inv;
+ while ((inv = PENDING_PERSIST.poll()) != null) {
+ try {
+ inv.persist();
+ } catch (Exception e) {
+ e.printStackTrace();
}
}
}
@@ -287,20 +272,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 (this.stack.getTag() != null) {
+ this.stack.getTag().remove("uuid");
+ this.stack.getTag().remove("types");
+ this.stack.getTag().remove("total");
+ }
+ initData();
}
+ return;
}
- return;
}
// 构建要保存的Key和数量列表(混合表示:long 或 string)
ListTag amountTags = new ListTag();
@@ -318,42 +308,58 @@ 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 (this.stack.getTag() != null) {
+ this.stack.getTag().remove("types");
+ this.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 (this.stack.getTag() == null) {
+ this.stack.setTag(new CompoundTag());
+ }
+ int typesCount = keys.size();
+ this.stack.getOrCreateTag().putInt("types", typesCount);
+ BigInteger total = BigInteger.ZERO;
+ for (Map.Entry e : map.object2ObjectEntrySet()) {
+ BigInteger v = e.getValue();
+ if (v.compareTo(BigInteger.ZERO) > 0) {
+ total = total.add(v);
+ }
+ }
+ if (total.compareTo(BigInteger.valueOf(Long.MAX_VALUE)) <= 0) {
+ this.stack.getOrCreateTag().putLong("total", total.longValue());
+ } else {
+ this.stack.getOrCreateTag().putString("total", total.toString());
+ }
+ } catch (Exception ignored) {
}
- // 将当前已存储的不同物品种类数缓存到 NBT(键名: "types"),用于客户端 tooltip 显示
- int typesCount = this.getCellStoredMap().size();
- stack.getOrCreateTag().putInt("types", typesCount);
}
- isPersisted = true;
+ this.isPersisted = true;
}
// 插入物品到存储单元
@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());
+ this.stack.getOrCreateTag().putUUID("uuid", UUID.randomUUID());
+ InfinityStorageManager.INSTANCE.getOrCreateCell(getUUID());
// 确保 storedMap 初始化并从持久层加载数据
- this.getCellStoredMap();
+ loadCellStoredMap();
}
Object2ObjectMap map = this.getCellStoredMap();
BigInteger currentAmount = map.getOrDefault(what, BigInteger.ZERO);
@@ -361,8 +367,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 +387,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 +396,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;
@@ -404,10 +403,4 @@ public class InfinityBigIntegerCellInventory implements StorageCell {
}
return 0;
}
-
- // 获取存储单元内所有物品的总数量(格式化字符串)
- public String getTotalStorage() {
- // 使用缓存的 totalStored,避免每次全表扫描
- return formatBigInteger(totalStored);
- }
}
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/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..9aa2f64 100644
--- a/src/main/java/com/extendedae_plus/util/storage/InfinityStorageManager.java
+++ b/src/main/java/com/extendedae_plus/util/storage/InfinityStorageManager.java
@@ -1,11 +1,18 @@
package com.extendedae_plus.util.storage;
+import com.extendedae_plus.ExtendedAEPlus;
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.util.HashMap;
import java.util.Map;
import java.util.UUID;
@@ -13,88 +20,89 @@ 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";
/**
- * SavedData 文件名常量
- */
- public static final String FILE_NAME = "eap_infinity_biginteger_cells";
- /**
- * 全局单例实例(在世界加载时由 InfiniteBigIntegerStorageCell.onLevelLoad 填充)
- */
- public static InfinityStorageManager INSTANCE = null;
- /**
- * UUID -> 数据 的内存映射
+ * 全局单例,由 mod 在 world load 时初始化
*/
+ public static volatile InfinityStorageManager INSTANCE = new InfinityStorageManager();
+
private final Map cells = new HashMap<>();
- public InfinityStorageManager() {
- setDirty();
+ private Path saveFilePath = null;
+
+ private InfinityStorageManager() {}
+
+ /**
+ * 初始化并从 world 保存目录加载数据;若文件不存在则保持空状态
+ */
+ 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, ExtendedAEPlus.MODID);
+ if (!modDir.exists()) {
+ modDir.mkdirs();
+ }
+ this.saveFilePath = new File(modDir, FILE_NAME).toPath();
+ if (Files.exists(this.saveFilePath)) {
+ CompoundTag root = NbtIo.readCompressed(this.saveFilePath.toFile());
+ ListTag cellList = root.getList("list", Tag.TAG_COMPOUND);
+ for (int i = 0; i < cellList.size(); i++) {
+ CompoundTag cell = cellList.getCompound(i);
+ this.cells.put(cell.getUUID("uuid"), InfinityDataStorage.loadFromNBT(cell.getCompound("data")));
+ }
+ }
+ } catch (IOException e) {
+ // 读取失败保持空,并打印栈追踪以便调试
+ e.printStackTrace();
+ }
}
/**
- * 从 NBT 构造:用于在世界加载时从存档恢复数据
+ * 保存当前内存数据到文件(会覆盖已有文件)
*/
- 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 synchronized void saveToFile() {
+ if (this.saveFilePath == null)
+ return;
+ try {
+ CompoundTag root = new CompoundTag();
+ ListTag cellList = new ListTag();
+ for (Map.Entry entry : this.cells.entrySet()) {
+ CompoundTag cell = new CompoundTag();
+ cell.putUUID("uuid", entry.getKey());
+ cell.put("data", entry.getValue().serializeNBT());
+ cellList.add(cell);
+ }
+ root.put("list", cellList);
+ // 使用压缩写入以节省空间
+ NbtIo.writeCompressed(root, this.saveFilePath.toFile());
+ } 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);
- }
- 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) {
- cells.put(uuid, infinityDataStorage);
- setDirty();
+ this.cells.put(uuid, infinityDataStorage);
+ saveToFile();
}
- /**
- * 获取或创建某个 UUID 对应的数据容器
- */
public InfinityDataStorage getOrCreateCell(UUID uuid) {
- if (!cells.containsKey(uuid)) {
- updateCell(uuid, new InfinityDataStorage());
+ if (!this.cells.containsKey(uuid)) {
+ InfinityDataStorage newCell = new InfinityDataStorage();
+ this.cells.put(uuid, newCell);
+ saveToFile();
}
- return cells.get(uuid);
+ return this.cells.get(uuid);
}
- /**
- * 修改某个 UUID 对应的键与数量列表并保存(新的签名,stackAmounts 为 ListTag 字符串列表)
- */
public void modifyCell(UUID cellID, ListTag stackKeys, ListTag stackAmounts) {
InfinityDataStorage cellToModify = getOrCreateCell(cellID);
if (stackKeys != null && stackAmounts != null) {
@@ -104,11 +112,8 @@ public class InfinityStorageManager extends SavedData {
updateCell(cellID, cellToModify);
}
- /**
- * 删除某个 UUID 的持久化记录并标记为脏
- */
public void removeCell(UUID uuid) {
- cells.remove(uuid);
- setDirty();
+ this.cells.remove(uuid);
+ saveToFile();
}
}
From ba22d3ae2e235c24842a21e3d5f7b9b4c51170e3 Mon Sep 17 00:00:00 2001
From: GaLicn <133291877+GaLicn@users.noreply.github.com>
Date: Fri, 19 Sep 2025 19:21:06 +0800
Subject: [PATCH 02/34] =?UTF-8?q?=E8=B0=83=E6=95=B4mixin?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
build.gradle | 2 +-
src/main/resources/extendedae_plus.mixins.json | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/build.gradle b/build.gradle
index b63a799..9c945b4 100644
--- a/build.gradle
+++ b/build.gradle
@@ -106,7 +106,7 @@ dependencies {
//mae2
- modRuntimeOnly "curse.maven:modern-ae2-additions-1028068:6827727"
+/* modRuntimeOnly "curse.maven:modern-ae2-additions-1028068:6827727"*/
modCompileOnly "curse.maven:modern-ae2-additions-1028068:6827727"
//aea
diff --git a/src/main/resources/extendedae_plus.mixins.json b/src/main/resources/extendedae_plus.mixins.json
index a69afbb..a1e9d61 100644
--- a/src/main/resources/extendedae_plus.mixins.json
+++ b/src/main/resources/extendedae_plus.mixins.json
@@ -21,6 +21,7 @@
"ae2.client.gui.PatternProviderCloseMixin",
"ae2.client.gui.PatternProviderScreenMixin",
"ae2.client.gui.SlotGridLayoutMixin",
+ "ae2.menu.CraftConfirmMenuGoBackMixin",
"extendedae.accessor.GuiExPatternTerminalAccessor",
"extendedae.accessor.GuiExPatternTerminalSlotsRowAccessor",
"extendedae.client.HighlightButtonMixin",
@@ -57,7 +58,6 @@
"ae2.helpers.PatternProviderLogicAdvancedMixin",
"ae2.helpers.PatternProviderLogicDoublingMixin",
"ae2.menu.ContainerPatternEncodingTermMenuMixin",
- "ae2.menu.CraftConfirmMenuGoBackMixin",
"ae2.menu.MEStorageMenuMixin",
"ae2.menu.PatternEncodingTermMenuMixin",
"ae2.menu.PatternProviderMenuAdvancedMixin",
From 0090d302f142df3c8321b8343c3126b3a9c56549 Mon Sep 17 00:00:00 2001
From: GaLicn <133291877+GaLicn@users.noreply.github.com>
Date: Fri, 19 Sep 2025 20:17:32 +0800
Subject: [PATCH 03/34] =?UTF-8?q?=E8=A3=85=E9=85=8D=E7=9F=A9=E9=98=B5?=
=?UTF-8?q?=E4=B8=8A=E4=BC=A0=E6=A0=B8=E5=BF=83?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../com/extendedae_plus/ExtendedAEPlus.java | 8 ++++++
.../content/matrix/UploadCoreBlock.java | 26 +++++++++++++++++++
.../content/matrix/UploadCoreBlockEntity.java | 24 +++++++++++++++++
.../init/ModBlockEntities.java | 7 +++++
.../com/extendedae_plus/init/ModBlocks.java | 11 ++++++++
.../com/extendedae_plus/init/ModItems.java | 6 +++++
.../util/ExtendedAEPatternUploadUtil.java | 26 +++++++++++++++++--
7 files changed, 106 insertions(+), 2 deletions(-)
create mode 100644 src/main/java/com/extendedae_plus/content/matrix/UploadCoreBlock.java
create mode 100644 src/main/java/com/extendedae_plus/content/matrix/UploadCoreBlockEntity.java
diff --git a/src/main/java/com/extendedae_plus/ExtendedAEPlus.java b/src/main/java/com/extendedae_plus/ExtendedAEPlus.java
index 25d675e..2688340 100644
--- a/src/main/java/com/extendedae_plus/ExtendedAEPlus.java
+++ b/src/main/java/com/extendedae_plus/ExtendedAEPlus.java
@@ -74,6 +74,14 @@ public class ExtendedAEPlus {
ModNetwork.register();
// 注册自定义 Curios 宿主定位器,便于将菜单宿主信息在服务端与客户端间同步
MenuLocators.register(CuriosItemLocator.class, CuriosItemLocator::writeToPacket, CuriosItemLocator::readFromPacket);
+
+ // 绑定装配矩阵上传核心的方块实体类型(延迟到注册完成后)
+ com.extendedae_plus.init.ModBlocks.ASSEMBLER_MATRIX_UPLOAD_CORE.get().setBlockEntity(
+ com.extendedae_plus.content.matrix.UploadCoreBlockEntity.class,
+ com.extendedae_plus.init.ModBlockEntities.UPLOAD_CORE_BE.get(),
+ null,
+ null
+ );
});
}
diff --git a/src/main/java/com/extendedae_plus/content/matrix/UploadCoreBlock.java b/src/main/java/com/extendedae_plus/content/matrix/UploadCoreBlock.java
new file mode 100644
index 0000000..9dcaddd
--- /dev/null
+++ b/src/main/java/com/extendedae_plus/content/matrix/UploadCoreBlock.java
@@ -0,0 +1,26 @@
+package com.extendedae_plus.content.matrix;
+
+import com.glodblock.github.extendedae.common.blocks.matrix.BlockAssemblerMatrixBase;
+import net.minecraft.world.item.Item;
+import net.minecraft.world.level.block.state.BlockBehaviour;
+
+/**
+ * ExtendedAE_Plus: 装配矩阵上传核心方块(内部功能块)。
+ * 仅用于作为多方块内部的“功能块”存在;是否允许自动上传由工具类检查集群中是否存在该核心决定。
+ */
+public class UploadCoreBlock extends BlockAssemblerMatrixBase {
+
+ public UploadCoreBlock() {
+ super();
+ }
+
+ public UploadCoreBlock(BlockBehaviour.Properties props) {
+ super(props);
+ }
+
+ @Override
+ public Item getPresentItem() {
+ // 由对应的 BlockItem 注册返回,上传核心不需要特殊的 PresentItem,可返回自身的 BlockItem
+ return com.extendedae_plus.init.ModItems.ASSEMBLER_MATRIX_UPLOAD_CORE.get();
+ }
+}
diff --git a/src/main/java/com/extendedae_plus/content/matrix/UploadCoreBlockEntity.java b/src/main/java/com/extendedae_plus/content/matrix/UploadCoreBlockEntity.java
new file mode 100644
index 0000000..210b6ec
--- /dev/null
+++ b/src/main/java/com/extendedae_plus/content/matrix/UploadCoreBlockEntity.java
@@ -0,0 +1,24 @@
+package com.extendedae_plus.content.matrix;
+
+import com.glodblock.github.extendedae.common.me.matrix.ClusterAssemblerMatrix;
+import com.glodblock.github.extendedae.common.tileentities.matrix.TileAssemblerMatrixFunction;
+import net.minecraft.core.BlockPos;
+import net.minecraft.world.level.block.entity.BlockEntityType;
+import net.minecraft.world.level.block.state.BlockState;
+
+/**
+ * ExtendedAE_Plus: 装配矩阵上传核心方块实体。
+ * 作为矩阵内部功能块,仅用于标记该矩阵允许被自动上传(工具类会在集群中查找此实体)。
+ */
+public class UploadCoreBlockEntity extends TileAssemblerMatrixFunction {
+
+ public UploadCoreBlockEntity(BlockPos pos, BlockState state) {
+ super((BlockEntityType>) com.extendedae_plus.init.ModBlockEntities.UPLOAD_CORE_BE.get(), pos, state);
+ }
+
+ @Override
+ public void add(ClusterAssemblerMatrix c) {
+ // 无需修改集群,仅作为存在性标记。
+ // 若后续需要限制为“最多一个”,可在 ExtendedAE_Plus 工具类或事件中做校验与提示。
+ }
+}
diff --git a/src/main/java/com/extendedae_plus/init/ModBlockEntities.java b/src/main/java/com/extendedae_plus/init/ModBlockEntities.java
index 7db517e..585d5ac 100644
--- a/src/main/java/com/extendedae_plus/init/ModBlockEntities.java
+++ b/src/main/java/com/extendedae_plus/init/ModBlockEntities.java
@@ -2,6 +2,7 @@ package com.extendedae_plus.init;
import com.extendedae_plus.ExtendedAEPlus;
import com.extendedae_plus.content.wireless.WirelessTransceiverBlockEntity;
+import com.extendedae_plus.content.matrix.UploadCoreBlockEntity;
import com.extendedae_plus.content.controller.NetworkPatternControllerBlockEntity;
import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraftforge.registries.DeferredRegister;
@@ -23,4 +24,10 @@ public final class ModBlockEntities {
BLOCK_ENTITY_TYPES.register("network_pattern_controller",
() -> BlockEntityType.Builder.of(NetworkPatternControllerBlockEntity::new,
ModBlocks.NETWORK_PATTERN_CONTROLLER.get()).build(null));
+
+ // 装配矩阵上传核心
+ public static final RegistryObject> UPLOAD_CORE_BE =
+ BLOCK_ENTITY_TYPES.register("assembler_matrix_upload_core",
+ () -> BlockEntityType.Builder.of(UploadCoreBlockEntity::new,
+ ModBlocks.ASSEMBLER_MATRIX_UPLOAD_CORE.get()).build(null));
}
diff --git a/src/main/java/com/extendedae_plus/init/ModBlocks.java b/src/main/java/com/extendedae_plus/init/ModBlocks.java
index 4316e2a..ae722b4 100644
--- a/src/main/java/com/extendedae_plus/init/ModBlocks.java
+++ b/src/main/java/com/extendedae_plus/init/ModBlocks.java
@@ -2,6 +2,7 @@ package com.extendedae_plus.init;
import com.extendedae_plus.ExtendedAEPlus;
import com.extendedae_plus.content.wireless.WirelessTransceiverBlock;
+import com.extendedae_plus.content.matrix.UploadCoreBlock;
import com.extendedae_plus.content.crafting.EPlusCraftingUnitType;
import appeng.block.crafting.CraftingUnitBlock;
import appeng.blockentity.crafting.CraftingBlockEntity;
@@ -39,6 +40,16 @@ public final class ModBlocks {
)
);
+ // 装配矩阵上传核心(内部功能块)
+ public static final RegistryObject ASSEMBLER_MATRIX_UPLOAD_CORE = BLOCKS.register(
+ "assembler_matrix_upload_core",
+ () -> {
+ var b = new UploadCoreBlock();
+ // 注意:方块实体绑定延后到 commonSetup 的 enqueueWork 中执行,避免注册阶段循环依赖
+ return b;
+ }
+ );
+
// Crafting Accelerators (reuse MAE2 textures/models)
public static final RegistryObject ACCELERATOR_4x = BLOCKS.register(
"4x_crafting_accelerator",
diff --git a/src/main/java/com/extendedae_plus/init/ModItems.java b/src/main/java/com/extendedae_plus/init/ModItems.java
index 6ec6a70..0f7b430 100644
--- a/src/main/java/com/extendedae_plus/init/ModItems.java
+++ b/src/main/java/com/extendedae_plus/init/ModItems.java
@@ -28,6 +28,12 @@ public final class ModItems {
() -> new BlockItem(ModBlocks.NETWORK_PATTERN_CONTROLLER.get(), new Item.Properties())
);
+ // 装配矩阵上传核心(方块物品)
+ public static final RegistryObject- ASSEMBLER_MATRIX_UPLOAD_CORE = ITEMS.register(
+ "assembler_matrix_upload_core",
+ () -> new BlockItem(ModBlocks.ASSEMBLER_MATRIX_UPLOAD_CORE.get(), new Item.Properties())
+ );
+
// Crafting Accelerators
public static final RegistryObject
- ACCELERATOR_4x = ITEMS.register(
"4x_crafting_accelerator",
diff --git a/src/main/java/com/extendedae_plus/util/ExtendedAEPatternUploadUtil.java b/src/main/java/com/extendedae_plus/util/ExtendedAEPatternUploadUtil.java
index d78af27..7911c9d 100644
--- a/src/main/java/com/extendedae_plus/util/ExtendedAEPatternUploadUtil.java
+++ b/src/main/java/com/extendedae_plus/util/ExtendedAEPatternUploadUtil.java
@@ -543,7 +543,7 @@ public class ExtendedAEPatternUploadUtil {
var tiles = grid.getMachines(com.glodblock.github.extendedae.common.tileentities.matrix.TileAssemblerMatrixPattern.class);
int idx = 0;
for (com.glodblock.github.extendedae.common.tileentities.matrix.TileAssemblerMatrixPattern tile : tiles) {
- if (tile != null && tile.isFormed() && tile.getMainNode().isActive()) {
+ if (tile != null && tile.isFormed() && tile.getMainNode().isActive() && clusterHasSingleUploadCore(tile)) {
var inv = tile.getExposedInventory();
if (inv != null) {
result.add(inv);
@@ -565,7 +565,7 @@ public class ExtendedAEPatternUploadUtil {
Set matrices = grid.getMachines(TileAssemblerMatrixBase.class);
int idx = 0;
for (TileAssemblerMatrixBase tile : matrices) {
- if (tile != null && tile.isFormed()) {
+ if (tile != null && tile.isFormed() && clusterHasSingleUploadCore(tile)) {
var capOpt = tile.getCapability(ForgeCapabilities.ITEM_HANDLER, null);
if (capOpt != null) {
var handler = capOpt.orElse(null);
@@ -631,6 +631,28 @@ public class ExtendedAEPatternUploadUtil {
return false;
}
+ /**
+ * 判断给定矩阵集群中是否存在且仅存在一个“装配矩阵上传核心”。
+ * 传入任意属于该集群的 Tile(如 Pattern/Crafter/Frame 等)。
+ */
+ private static boolean clusterHasSingleUploadCore(TileAssemblerMatrixBase any) {
+ try {
+ if (any == null || any.getCluster() == null) return false;
+ int cores = 0;
+ var it = any.getCluster().getBlockEntities();
+ while (it.hasNext()) {
+ var te = it.next();
+ if (te instanceof com.extendedae_plus.content.matrix.UploadCoreBlockEntity) {
+ cores++;
+ if (cores > 1) return false; // 至多一个
+ }
+ }
+ return cores == 1; // 恰好一个
+ } catch (Throwable t) {
+ return false;
+ }
+ }
+
/**
* 检查当前菜单是否为ExtendedAE的扩展样板管理终端
*
From 6e5b6b46f02094a6d9a77d800fbc3f4bad56944a Mon Sep 17 00:00:00 2001
From: GaLicn <133291877+GaLicn@users.noreply.github.com>
Date: Fri, 19 Sep 2025 20:40:33 +0800
Subject: [PATCH 04/34] =?UTF-8?q?=E8=A3=85=E9=85=8D=E7=9F=A9=E9=98=B5?=
=?UTF-8?q?=E4=B8=8A=E4=BC=A0=E6=A0=B8=E5=BF=83=E8=B4=B4=E5=9B=BE=E8=B5=84?=
=?UTF-8?q?=E6=BA=90?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../util/ExtendedAEPatternUploadUtil.java | 6 +++---
.../blockstates/assembler_matrix_upload_core.json | 5 +++++
.../assets/extendedae_plus/lang/en_us.json | 4 +++-
.../assets/extendedae_plus/lang/zh_cn.json | 9 ++++++++-
.../block/assembler_matrix_upload_core.json | 6 ++++++
.../models/item/assembler_matrix_upload_core.json | 3 +++
.../block/assembler_matrix_upload_core.png | Bin 0 -> 225 bytes
7 files changed, 28 insertions(+), 5 deletions(-)
create mode 100644 src/main/resources/assets/extendedae_plus/blockstates/assembler_matrix_upload_core.json
create mode 100644 src/main/resources/assets/extendedae_plus/models/block/assembler_matrix_upload_core.json
create mode 100644 src/main/resources/assets/extendedae_plus/models/item/assembler_matrix_upload_core.json
create mode 100644 src/main/resources/assets/extendedae_plus/textures/block/assembler_matrix_upload_core.png
diff --git a/src/main/java/com/extendedae_plus/util/ExtendedAEPatternUploadUtil.java b/src/main/java/com/extendedae_plus/util/ExtendedAEPatternUploadUtil.java
index 7911c9d..d9f2542 100644
--- a/src/main/java/com/extendedae_plus/util/ExtendedAEPatternUploadUtil.java
+++ b/src/main/java/com/extendedae_plus/util/ExtendedAEPatternUploadUtil.java
@@ -632,7 +632,8 @@ public class ExtendedAEPatternUploadUtil {
}
/**
- * 判断给定矩阵集群中是否存在且仅存在一个“装配矩阵上传核心”。
+ * 判断给定矩阵集群中是否存在“装配矩阵上传核心”。
+ * 要求:至少存在 1 个即可,不限制数量。
* 传入任意属于该集群的 Tile(如 Pattern/Crafter/Frame 等)。
*/
private static boolean clusterHasSingleUploadCore(TileAssemblerMatrixBase any) {
@@ -644,10 +645,9 @@ public class ExtendedAEPatternUploadUtil {
var te = it.next();
if (te instanceof com.extendedae_plus.content.matrix.UploadCoreBlockEntity) {
cores++;
- if (cores > 1) return false; // 至多一个
}
}
- return cores == 1; // 恰好一个
+ return cores >= 1; // 至少一个即可
} catch (Throwable t) {
return false;
}
diff --git a/src/main/resources/assets/extendedae_plus/blockstates/assembler_matrix_upload_core.json b/src/main/resources/assets/extendedae_plus/blockstates/assembler_matrix_upload_core.json
new file mode 100644
index 0000000..a099eb5
--- /dev/null
+++ b/src/main/resources/assets/extendedae_plus/blockstates/assembler_matrix_upload_core.json
@@ -0,0 +1,5 @@
+{
+ "variants": {
+ "": { "model": "extendedae_plus:block/assembler_matrix_upload_core" }
+ }
+}
diff --git a/src/main/resources/assets/extendedae_plus/lang/en_us.json b/src/main/resources/assets/extendedae_plus/lang/en_us.json
index 2187081..320a792 100644
--- a/src/main/resources/assets/extendedae_plus/lang/en_us.json
+++ b/src/main/resources/assets/extendedae_plus/lang/en_us.json
@@ -35,6 +35,7 @@
"block.extendedae_plus.256x_crafting_accelerator": "256x Crafting Accelerator",
"block.extendedae_plus.1024x_crafting_accelerator": "1024x Crafting Accelerator",
"block.extendedae_plus.network_pattern_controller": "Pattern Supplier State Controller",
+ "block.extendedae_plus.assembler_matrix_upload_core": "Assembler Matrix Upload Core",
"extendedae_plus.upload_to_matrix": "Upload to Assembly Matrix",
"extendedae_plus.upload_to_matrix.success": "Pattern uploaded to the assembly matrix",
@@ -85,5 +86,6 @@
"config.extendedae_plus.option.entityTickerCost": "Entity Ticker Base Energy Cost",
"config.extendedae_plus.option.entityTickerBlackList": "Entity Ticker Blacklist",
"config.extendedae_plus.option.entityTickerMultipliers": "Entity Ticker Extra Consumption Multipliers",
- "config.extendedae_plus.option.craftingPauseThreshold": "AE synthesis calculation pause check threshold"
+ "config.extendedae_plus.option.craftingPauseThreshold": "AE synthesis calculation pause check threshold",
+ "block.extendedae_plus.assembler_matrix_upload_core": "Assembler Matrix Upload Core"
}
\ No newline at end of file
diff --git a/src/main/resources/assets/extendedae_plus/lang/zh_cn.json b/src/main/resources/assets/extendedae_plus/lang/zh_cn.json
index d89594e..d7996ab 100644
--- a/src/main/resources/assets/extendedae_plus/lang/zh_cn.json
+++ b/src/main/resources/assets/extendedae_plus/lang/zh_cn.json
@@ -35,6 +35,7 @@
"block.extendedae_plus.256x_crafting_accelerator": "256x并行处理单元",
"block.extendedae_plus.1024x_crafting_accelerator": "1024x并行处理单元",
"block.extendedae_plus.network_pattern_controller": "样板供应器状态控制器",
+ "block.extendedae_plus.assembler_matrix_upload_core": "装配矩阵上传核心",
"extendedae_plus.upload_to_matrix": "上传到装配矩阵",
"extendedae_plus.upload_to_matrix.success": "样板已上传到装配矩阵",
@@ -85,5 +86,11 @@
"config.extendedae_plus.option.entityTickerCost": "实体加速器能量消耗基础值",
"config.extendedae_plus.option.entityTickerBlackList": "实体加速器黑名单",
"config.extendedae_plus.option.entityTickerMultipliers": "实体加速器额外消耗倍率",
- "config.extendedae_plus.option.craftingPauseThreshold": "AE合成计算暂停检查阈值"
+ "config.extendedae_plus.option.craftingPauseThreshold": "AE合成计算暂停检查阈值",
+ "block.extendedae_plus.assembler_matrix_upload_core.tooltip": "装配矩阵上传核心",
+ "block.extendedae_plus.assembler_matrix_upload_core.tooltip.upload": "上传到装配矩阵",
+ "block.extendedae_plus.assembler_matrix_upload_core.tooltip.upload_success": "样板已上传到装配矩阵",
+ "block.extendedae_plus.assembler_matrix_upload_core.tooltip.upload_fail_not_crafting": "仅支持上传合成样板,处理样板将被忽略",
+ "block.extendedae_plus.assembler_matrix_upload_core.tooltip.upload_fail_no_matrix": "未在当前网络中找到已成型的装配矩阵",
+ "block.extendedae_plus.assembler_matrix_upload_core.tooltip.upload_fail_full": "装配矩阵的样板仓已满或无法插入"
}
\ No newline at end of file
diff --git a/src/main/resources/assets/extendedae_plus/models/block/assembler_matrix_upload_core.json b/src/main/resources/assets/extendedae_plus/models/block/assembler_matrix_upload_core.json
new file mode 100644
index 0000000..768aff9
--- /dev/null
+++ b/src/main/resources/assets/extendedae_plus/models/block/assembler_matrix_upload_core.json
@@ -0,0 +1,6 @@
+{
+ "parent": "minecraft:block/cube_all",
+ "textures": {
+ "all": "extendedae_plus:block/assembler_matrix_upload_core"
+ }
+}
diff --git a/src/main/resources/assets/extendedae_plus/models/item/assembler_matrix_upload_core.json b/src/main/resources/assets/extendedae_plus/models/item/assembler_matrix_upload_core.json
new file mode 100644
index 0000000..5470af1
--- /dev/null
+++ b/src/main/resources/assets/extendedae_plus/models/item/assembler_matrix_upload_core.json
@@ -0,0 +1,3 @@
+{
+ "parent": "extendedae_plus:block/assembler_matrix_upload_core"
+}
diff --git a/src/main/resources/assets/extendedae_plus/textures/block/assembler_matrix_upload_core.png b/src/main/resources/assets/extendedae_plus/textures/block/assembler_matrix_upload_core.png
new file mode 100644
index 0000000000000000000000000000000000000000..bac350b1ca8ac3c6423aaea69b4134ef3d5ea517
GIT binary patch
literal 225
zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61SBU+%rFB|jKx9jP7LeL$-D$|W_!9ghFJ6_
z??>IlPn_yFcrzm=Qqph*?}?feHY4$My`3uj
z3MLC?c5y!7nsMO|1EbU4SwM6`>Bk(EE30QlOgS}Czj+z^ayB-$wx{*mU(cIxNb6{m
z=9hIpb_J||mf5EMzH5E*DWiM4|Ib*`arj-Pk3*e}!XE}hX8%hK43qvIU|QHwF2wN7
X%K4y}yVzc!GZ{Qx{an^LB{Ts5t+`n7
literal 0
HcmV?d00001
From 1279031535f3726e7a2af4eea90ef3fdcf810e23 Mon Sep 17 00:00:00 2001
From: C-H716 <1536152356@qq.com>
Date: Fri, 19 Sep 2025 20:49:13 +0800
Subject: [PATCH 05/34] =?UTF-8?q?feat:=20=E5=AE=9E=E7=8E=B0=E6=97=A0?=
=?UTF-8?q?=E9=99=90=E5=AD=98=E5=82=A8=E5=85=83=E4=BB=B6=E7=9A=84=E4=B8=B4?=
=?UTF-8?q?=E6=97=B6=E6=96=87=E4=BB=B6=E5=86=99=E5=85=A5=20+=20=E5=8E=9F?=
=?UTF-8?q?=E5=AD=90=E6=9B=BF=E6=8D=A2?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../com/extendedae_plus/ExtendedAEPlus.java | 4 -
.../InfinityBigIntegerCellInventory.java | 105 +++++++-----------
.../util/storage/InfinityStorageManager.java | 65 +++++++----
3 files changed, 83 insertions(+), 91 deletions(-)
diff --git a/src/main/java/com/extendedae_plus/ExtendedAEPlus.java b/src/main/java/com/extendedae_plus/ExtendedAEPlus.java
index ca77fbf..44a408a 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.*;
@@ -53,9 +52,6 @@ public class ExtendedAEPlus {
// 注册到Forge事件总线
MinecraftForge.EVENT_BUS.register(this);
MinecraftForge.EVENT_BUS.addListener(ExtendedAEPlus::onLevelLoad);
- // 注册每秒合并持久化队列的事件监听(Server tick end + stopping)
- MinecraftForge.EVENT_BUS.addListener(InfinityBigIntegerCellInventory::onServerTick);
- MinecraftForge.EVENT_BUS.addListener(InfinityBigIntegerCellInventory::onServerStopping);
// 注册通用配置
ModConfig.init();
}
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 d23c236..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,14 +17,11 @@ 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;
import java.math.RoundingMode;
import java.text.DecimalFormat;
-import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentLinkedQueue;
@@ -48,8 +45,6 @@ public class InfinityBigIntegerCellInventory implements StorageCell {
// 待持久化队列(用于 debounce:在服务器 tick 中合并持久化)
private static final ConcurrentLinkedQueue PENDING_PERSIST = new ConcurrentLinkedQueue<>();
- // 用于按 tick 计数,每 20 tick(约 1 秒)触发一次合并写入
- private static int TICK_COUNTER = 0;
// 数字格式化对象,保留两位小数(复用以减少对象分配)
private static final DecimalFormat DF = new DecimalFormat("#.##");
@@ -137,13 +132,13 @@ public class InfinityBigIntegerCellInventory implements StorageCell {
// 判断物品堆栈是否有UUID
public boolean hasUUID() {
- return this.stack.hasTag() && this.stack.getOrCreateTag().contains("uuid");
+ return stack.hasTag() && stack.getOrCreateTag().contains("uuid");
}
// 获取物品堆栈的UUID
public UUID getUUID() {
if (this.hasUUID()) {
- return this.stack.getOrCreateTag().getUUID("uuid");
+ return stack.getOrCreateTag().getUUID("uuid");
} else {
return null;
}
@@ -151,17 +146,17 @@ public class InfinityBigIntegerCellInventory implements StorageCell {
// 获取或初始化存储映射
private Object2ObjectMap getCellStoredMap() {
- if (this.storedMap == null) {
- this.storedMap = new Object2ObjectOpenHashMap<>();
+ if (storedMap == null) {
+ storedMap = new Object2ObjectOpenHashMap<>();
this.loadCellStoredMap();
}
- return this.storedMap;
+ return storedMap;
}
// 从存储中加载物品映射
private void loadCellStoredMap() {
boolean corruptedTag = false; // 标记数据是否损坏
- if (!this.stack.hasTag())
+ if (!stack.hasTag())
return;
ListTag keys = this.getCellStorage().keys;
ListTag amounts = this.getCellStorage().amounts;
@@ -200,38 +195,12 @@ public class InfinityBigIntegerCellInventory implements StorageCell {
// 标记数据需要保存,并通知容器或直接持久化
private void saveChanges() {
// 标记为未持久化,交由容器或延迟任务合并写入以减少 I/O
- this.isPersisted = false;
- // 将本实例加入待处理队列(去重)
- if (!PENDING_PERSIST.contains(this)) {
- PENDING_PERSIST.add(this);
- }
- if (this.container != null) {
- // 当存在容器时,仍然通知容器以便 AE2 在合并时回调 persist()
- this.container.saveChanges();
- }
- }
-
- /**
- * 每个服务器 tick 调用一次,用于按秒合并并执行待持久化项的 persist()
- */
- public static void onServerTick(TickEvent.ServerTickEvent event) {
- if (event.phase != TickEvent.Phase.END) return;
- TICK_COUNTER++;
- if (TICK_COUNTER % 20 != 0) return; // 每 20 tick(约 1 秒)执行一次
- onServerStopping(null);
- }
-
- /**
- * 在服务器停止时强制刷新所有待持久化项
- */
- public static void onServerStopping(ServerStoppingEvent event) {
- InfinityBigIntegerCellInventory inv;
- while ((inv = PENDING_PERSIST.poll()) != null) {
- try {
- inv.persist();
- } catch (Exception e) {
- e.printStackTrace();
- }
+ isPersisted = false;
+ if (container != null) {
+ // 当存在容器时,优先让容器统一处理持久化
+ container.saveChanges();
+ } else {
+ persist();
}
}
@@ -282,10 +251,10 @@ public class InfinityBigIntegerCellInventory implements StorageCell {
// 如果存储为空,移除UUID和全局存储中的数据,并清理缓存的 types/total
if (hasUUID()) {
InfinityStorageManager.INSTANCE.removeCell(getUUID());
- if (this.stack.getTag() != null) {
- this.stack.getTag().remove("uuid");
- this.stack.getTag().remove("types");
- this.stack.getTag().remove("total");
+ if (stack.getTag() != null) {
+ stack.getTag().remove("uuid");
+ stack.getTag().remove("types");
+ stack.getTag().remove("total");
}
initData();
}
@@ -311,36 +280,34 @@ public class InfinityBigIntegerCellInventory implements StorageCell {
if (keys.isEmpty()) {
InfinityStorageManager.INSTANCE.updateCell(this.getUUID(), new InfinityDataStorage());
// 清理缓存
- if (this.stack.getTag() != null) {
- this.stack.getTag().remove("types");
- this.stack.getTag().remove("total");
+ if (stack.getTag() != null) {
+ stack.getTag().remove("types");
+ stack.getTag().remove("total");
}
} else {
// amounts 现在为 CompoundTag 列表
InfinityStorageManager.INSTANCE.modifyCell(this.getUUID(), keys, amountTags);
// 缓存类型数量与总量到 ItemStack 的 NBT,避免每次 tooltip 或展示时重新统计
try {
- if (this.stack.getTag() == null) {
- this.stack.setTag(new CompoundTag());
- }
+ if (stack.getTag() == null) stack.setTag(new CompoundTag());
int typesCount = keys.size();
- this.stack.getOrCreateTag().putInt("types", typesCount);
- BigInteger total = BigInteger.ZERO;
- for (Map.Entry e : map.object2ObjectEntrySet()) {
- BigInteger v = e.getValue();
- if (v.compareTo(BigInteger.ZERO) > 0) {
+ 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(BigInteger.valueOf(Long.MAX_VALUE)) <= 0) {
- this.stack.getOrCreateTag().putLong("total", total.longValue());
+ if (total.compareTo(java.math.BigInteger.valueOf(Long.MAX_VALUE)) <= 0) {
+ stack.getOrCreateTag().putLong("total", total.longValue());
} else {
- this.stack.getOrCreateTag().putString("total", total.toString());
+ stack.getOrCreateTag().putString("total", total.toString());
}
} catch (Exception ignored) {
}
}
- this.isPersisted = true;
+ isPersisted = true;
}
// 插入物品到存储单元
@@ -356,7 +323,7 @@ public class InfinityBigIntegerCellInventory implements StorageCell {
}
// 如果没有UUID,生成UUID并初始化存储(延迟创建全局存储以避免在 manager 未就绪时 NPE)
if (!this.hasUUID()) {
- this.stack.getOrCreateTag().putUUID("uuid", UUID.randomUUID());
+ stack.getOrCreateTag().putUUID("uuid", UUID.randomUUID());
InfinityStorageManager.INSTANCE.getOrCreateCell(getUUID());
// 确保 storedMap 初始化并从持久层加载数据
loadCellStoredMap();
@@ -403,4 +370,16 @@ public class InfinityBigIntegerCellInventory implements StorageCell {
}
return 0;
}
+
+ // 获取存储单元内所有物品的总数量(格式化字符串)
+ public String getTotalStorage() {
+ // 使用缓存的 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/util/storage/InfinityStorageManager.java b/src/main/java/com/extendedae_plus/util/storage/InfinityStorageManager.java
index 9aa2f64..26d880a 100644
--- a/src/main/java/com/extendedae_plus/util/storage/InfinityStorageManager.java
+++ b/src/main/java/com/extendedae_plus/util/storage/InfinityStorageManager.java
@@ -1,6 +1,5 @@
package com.extendedae_plus.util.storage;
-import com.extendedae_plus.ExtendedAEPlus;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.ListTag;
import net.minecraft.nbt.NbtIo;
@@ -13,13 +12,15 @@ 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
- *
+ *
* 替代之前基于 SavedData 的实现,本类使用手动文件 I/O 在 world 目录下保存 NBT 数据,
* 以避免依赖 Minecraft 的 SavedData 机制。
* 数据保持与之前兼容的 NBT 结构:根 Compound 包含 "list" => ListTag of Compound { uuid, data }
@@ -28,16 +29,15 @@ public class InfinityStorageManager {
public static final String FILE_NAME = "eap_infinity_biginteger_cells.dat";
- /**
- * 全局单例,由 mod 在 world load 时初始化
- */
+ /** 全局单例,由 mod 在 world load 时初始化 */
public static volatile InfinityStorageManager INSTANCE = new InfinityStorageManager();
private final Map cells = new HashMap<>();
private Path saveFilePath = null;
- private InfinityStorageManager() {}
+ public InfinityStorageManager() {
+ }
/**
* 初始化并从 world 保存目录加载数据;若文件不存在则保持空状态
@@ -47,17 +47,15 @@ public class InfinityStorageManager {
try {
File worldFolder = serverLevel.getServer().getWorldPath(LevelResource.ROOT).toFile();
// 保存到 world// 文件夹下,避免与其它 mod 冲突
- File modDir = new File(worldFolder, ExtendedAEPlus.MODID);
- if (!modDir.exists()) {
- modDir.mkdirs();
- }
- this.saveFilePath = new File(modDir, FILE_NAME).toPath();
- if (Files.exists(this.saveFilePath)) {
- CompoundTag root = NbtIo.readCompressed(this.saveFilePath.toFile());
+ 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);
- this.cells.put(cell.getUUID("uuid"), InfinityDataStorage.loadFromNBT(cell.getCompound("data")));
+ cells.put(cell.getUUID("uuid"), InfinityDataStorage.loadFromNBT(cell.getCompound("data")));
}
}
} catch (IOException e) {
@@ -70,40 +68,58 @@ public class InfinityStorageManager {
* 保存当前内存数据到文件(会覆盖已有文件)
*/
public synchronized void saveToFile() {
- if (this.saveFilePath == null)
- return;
+ if (saveFilePath == null) return;
try {
CompoundTag root = new CompoundTag();
ListTag cellList = new ListTag();
- for (Map.Entry entry : this.cells.entrySet()) {
+ 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);
- // 使用压缩写入以节省空间
- NbtIo.writeCompressed(root, this.saveFilePath.toFile());
+ // 使用压缩写入到临时文件,然后原子替换目标文件以避免半成品/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();
}
}
public void updateCell(UUID uuid, InfinityDataStorage infinityDataStorage) {
- this.cells.put(uuid, infinityDataStorage);
+ if (uuid == null) return; // 忽略无效 UUID
+ cells.put(uuid, infinityDataStorage);
saveToFile();
}
public InfinityDataStorage getOrCreateCell(UUID uuid) {
- if (!this.cells.containsKey(uuid)) {
+ if (uuid == null) {
+ return InfinityDataStorage.EMPTY;
+ }
+ if (!cells.containsKey(uuid)) {
InfinityDataStorage newCell = new InfinityDataStorage();
- this.cells.put(uuid, newCell);
+ cells.put(uuid, newCell);
saveToFile();
}
- return this.cells.get(uuid);
+ return cells.get(uuid);
}
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;
@@ -113,7 +129,8 @@ public class InfinityStorageManager {
}
public void removeCell(UUID uuid) {
- this.cells.remove(uuid);
+ if (uuid == null) return;
+ cells.remove(uuid);
saveToFile();
}
}
From 045b363c1b053e6326b2e457c36ac3cdacfb31e7 Mon Sep 17 00:00:00 2001
From: GaLicn <133291877+GaLicn@users.noreply.github.com>
Date: Fri, 19 Sep 2025 21:25:37 +0800
Subject: [PATCH 06/34] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E9=85=8D=E6=96=B9?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../com/extendedae_plus/init/ModCreativeTabs.java | 2 ++
.../recipes/assembler_matrix_upload_core.json | 11 +++++++++++
2 files changed, 13 insertions(+)
create mode 100644 src/main/resources/data/extendedae_plus/recipes/assembler_matrix_upload_core.json
diff --git a/src/main/java/com/extendedae_plus/init/ModCreativeTabs.java b/src/main/java/com/extendedae_plus/init/ModCreativeTabs.java
index f30b2d6..293ac8e 100644
--- a/src/main/java/com/extendedae_plus/init/ModCreativeTabs.java
+++ b/src/main/java/com/extendedae_plus/init/ModCreativeTabs.java
@@ -21,6 +21,8 @@ public final class ModCreativeTabs {
// 将本模组物品加入创造物品栏
output.accept(ModItems.WIRELESS_TRANSCEIVER.get());
output.accept(ModItems.NETWORK_PATTERN_CONTROLLER.get());
+ // 装配矩阵上传核心
+ output.accept(ModItems.ASSEMBLER_MATRIX_UPLOAD_CORE.get());
output.accept(ModItems.ACCELERATOR_4x.get());
output.accept(ModItems.ACCELERATOR_16x.get());
output.accept(ModItems.ACCELERATOR_64x.get());
diff --git a/src/main/resources/data/extendedae_plus/recipes/assembler_matrix_upload_core.json b/src/main/resources/data/extendedae_plus/recipes/assembler_matrix_upload_core.json
new file mode 100644
index 0000000..12ca196
--- /dev/null
+++ b/src/main/resources/data/extendedae_plus/recipes/assembler_matrix_upload_core.json
@@ -0,0 +1,11 @@
+{
+ "type": "minecraft:crafting_shapeless",
+ "ingredients": [
+ { "item": "expatternprovider:assembler_matrix_wall" },
+ { "item": "minecraft:lever" }
+ ],
+ "result": {
+ "item": "extendedae_plus:assembler_matrix_upload_core",
+ "count": 1
+ }
+}
From ba79d0e31a885665a2380daef5ee6a0803ff9d0a Mon Sep 17 00:00:00 2001
From: C-H716 <1536152356@qq.com>
Date: Sat, 20 Sep 2025 02:32:11 +0800
Subject: [PATCH 07/34] =?UTF-8?q?fix:=20=E5=9B=9E=E9=80=80=E6=97=A0?=
=?UTF-8?q?=E9=99=90=E7=A3=81=E7=9B=98=E4=BF=AE=E6=94=B9=E8=87=B3=E4=B8=8A?=
=?UTF-8?q?=E4=B8=80=E7=89=88=E6=9C=AC=EF=BC=8C=E5=86=8D=E6=AC=A1=E4=BF=AE?=
=?UTF-8?q?=E5=A4=8D=EF=BC=8C=E6=B7=BB=E5=8A=A0=E7=89=88=E6=9C=AC=E5=8F=B7?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../InfinityBigIntegerCellInventory.java | 92 +++++++++++++++----
.../util/storage/InfinityDataStorage.java | 6 +-
.../util/storage/InfinityStorageManager.java | 18 +++-
3 files changed, 94 insertions(+), 22 deletions(-)
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..e678dfc 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
@@ -16,6 +16,8 @@ import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.ListTag;
import net.minecraft.network.chat.Component;
+import net.minecraft.server.MinecraftServer;
+import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.item.ItemStack;
import net.minecraftforge.event.TickEvent;
import net.minecraftforge.event.server.ServerStoppingEvent;
@@ -23,7 +25,6 @@ import net.minecraftforge.event.server.ServerStoppingEvent;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.RoundingMode;
-import java.text.DecimalFormat;
import java.util.UUID;
import java.util.concurrent.ConcurrentLinkedQueue;
@@ -48,8 +49,7 @@ public class InfinityBigIntegerCellInventory implements StorageCell {
// 待持久化队列(用于 debounce:在服务器 tick 中合并持久化)
private static final ConcurrentLinkedQueue PENDING_PERSIST = new ConcurrentLinkedQueue<>();
- // 数字格式化对象,保留两位小数(复用以减少对象分配)
- private static final DecimalFormat DF = new DecimalFormat("#.##");
+ // 数字格式化对象:改为方法局部以避免静态非线程安全问题
// 关联的 ItemStack(含可能的 uuid NBT)
private final ItemStack stack;
@@ -106,6 +106,17 @@ public class InfinityBigIntegerCellInventory implements StorageCell {
// 在服务器停止时被调用,立即强制持久化队列中的所有实例
public static void onServerStopping(ServerStoppingEvent event) {
+ // 尝试在服务端停止流程开始时初始化 SavedData(确保 INSTANCE 可用以便后续持久化)
+ try {
+ MinecraftServer server = event.getServer();
+ if (server != null) {
+ for (ServerLevel level : server.getAllLevels()) {
+ InfinityStorageManager.getForLevel(level);
+ }
+ }
+ } catch (Throwable ignored) {
+ }
+
InfinityBigIntegerCellInventory inv;
while ((inv = PENDING_PERSIST.poll()) != null) {
try {
@@ -116,7 +127,7 @@ public class InfinityBigIntegerCellInventory implements StorageCell {
LOGGER.info("InfinityBigIntegerCellInventory onServerStopping error1: {}", ignored.getMessage());
}
}
- // 额外尝试将全局存储管理器标记为脏以确保 SavedData 被写回(在单人模式下可能直接由系统触发)
+ // 将全局存储管理器标记为脏以确保 SavedData 被写回
try {
var stor = getStorageInstance();
if (stor != null) stor.setDirty();
@@ -127,7 +138,8 @@ public class InfinityBigIntegerCellInventory implements StorageCell {
// 将 BigInteger 格式化为带单位的字符串,保留两位小数
public static String formatBigInteger(BigInteger number) {
- // 使用局部 DF(非线程安全),但 Minecraft 通常在主线程运行
+ // 使用方法局部的 DecimalFormat,避免静态共享的非线程安全问题
+ java.text.DecimalFormat df = new java.text.DecimalFormat("#.##");
BigDecimal bd = new BigDecimal(number);
BigDecimal thousand = new BigDecimal(1000);
String[] units = new String[]{"", "K", "M", "G", "T", "P", "E", "Z", "Y"};
@@ -139,17 +151,23 @@ public class InfinityBigIntegerCellInventory implements StorageCell {
if (idx == 0) {
return bd.setScale(0, RoundingMode.DOWN).toPlainString();
}
- return DF.format(bd.doubleValue()) + units[idx];
+ return df.format(bd.doubleValue()) + units[idx];
}
// 获取当前存储单元的数据存储对象
private InfinityDataStorage getCellStorage() {
if (this.getUUID() == null) {
- // 如果没有UUID,返回空存储
- return InfinityDataStorage.EMPTY;
+ // 如果没有UUID,返回空存储(返回新实例以避免共享可变状态)
+ return InfinityDataStorage.empty();
} else {
+ // 在访问全局 SavedData 之前做防御性检查,避免在客户端或尚未初始化的情况下 NPE
+ InfinityStorageManager mgr = getStorageInstance();
+ if (mgr == null) {
+ // SavedData 尚未就绪,视为空存储(返回新实例以避免共享可变状态)
+ return InfinityDataStorage.empty();
+ }
// 否则获取或创建对应UUID的存储
- return getStorageInstance().getOrCreateCell(getUUID());
+ return mgr.getOrCreateCell(getUUID());
}
}
@@ -200,8 +218,9 @@ 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;
+ InfinityDataStorage storage = this.getCellStorage();
+ ListTag keys = storage.keys;
+ ListTag amounts = storage.amounts;
int len = Math.min(keys.size(), amounts.size());
for (int i = 0; i < len; i++) {
AEKey key = AEKey.fromTagGeneric(keys.getCompound(i));
@@ -233,6 +252,12 @@ public class InfinityBigIntegerCellInventory implements StorageCell {
if (corruptedTag) {
this.saveChanges();
}
+ // 打印加载后的摘要日志
+ try {
+ var uuid = this.getUUID();
+ LOGGER.info("Loaded cell {}: types={}, totalCached={}", uuid, this.getCellStoredMap().size(), this.getTotalStorage());
+ } catch (Throwable ignored) {
+ }
}
// 标记数据需要保存,并通知容器或直接持久化
@@ -289,16 +314,32 @@ public class InfinityBigIntegerCellInventory implements StorageCell {
public void persist() {
if (this.isPersisted)
return;
+ // 若全局存储管理器尚未就绪,重新入队并在未来 tick 重试
+ InfinityStorageManager mgr = getStorageInstance();
+ if (mgr == null) {
+ try {
+ var id = this.getUUID();
+ LOGGER.info("Persist deferred for cell {}: storage manager not ready", id);
+ } catch (Throwable ignored) {
+ }
+ if (!PENDING_PERSIST.contains(this)) {
+ PENDING_PERSIST.offer(this);
+ }
+ return;
+ }
+
Object2ObjectMap map = this.getCellStoredMap();
if (map.isEmpty()) {
// 如果存储为空,移除UUID和全局存储中的数据
if (this.hasUUID()) {
- getStorageInstance().removeCell(getUUID());
+ mgr.removeCell(getUUID());
if (stack.getTag() != null) {
stack.getTag().remove("uuid");
// 移除缓存的 total 字段
stack.getTag().remove("total");
}
+ // 标记为已持久化,避免重复尝试
+ isPersisted = true;
}
return;
}
@@ -320,10 +361,10 @@ public class InfinityBigIntegerCellInventory implements StorageCell {
}
// 如果没有Key,更新为空存储,否则保存数据
if (keys.isEmpty()) {
- getStorageInstance().updateCell(this.getUUID(), new InfinityDataStorage());
+ mgr.updateCell(this.getUUID(), new InfinityDataStorage());
} else {
// amounts 现在为 CompoundTag 列表
- getStorageInstance().modifyCell(this.getUUID(), keys, amountTags);
+ mgr.modifyCell(this.getUUID(), keys, amountTags);
}
// 将缓存的 totalStored 同步到 ItemStack 的 NBT,优先使用 long
if (stack.getOrCreateTag() != null) {
@@ -337,6 +378,12 @@ public class InfinityBigIntegerCellInventory implements StorageCell {
stack.getOrCreateTag().putInt("types", typesCount);
}
isPersisted = true;
+ // 打印持久化摘要日志
+ try {
+ var uuid = this.getUUID();
+ LOGGER.info("Persisted cell {}: types={}, totalCached={}", uuid, this.getCellStoredMap().size(), this.getTotalStorage());
+ } catch (Throwable ignored) {
+ }
}
// 插入物品到存储单元
@@ -348,12 +395,19 @@ public class InfinityBigIntegerCellInventory implements StorageCell {
// 不允许存储无限单元自身
if (what instanceof AEItemKey itemKey && itemKey.getItem() instanceof InfinityBigIntegerCellItem)
return 0;
- // 如果没有UUID,生成UUID并初始化存储
+ // 如果没有UUID,尝试在服务器端且存储管理器已就绪时生成UUID并初始化存储
if (!this.hasUUID()) {
- stack.getOrCreateTag().putUUID("uuid", UUID.randomUUID());
- getStorageInstance().getOrCreateCell(getUUID());
- // 确保 storedMap 初始化并从持久层加载数据
- this.getCellStoredMap();
+ InfinityStorageManager mgr = getStorageInstance();
+ // 仅在 SavedData 已就绪(通常在服务器端)时创建 UUID 并注册持久化存储
+ if (mgr != null) {
+ stack.getOrCreateTag().putUUID("uuid", UUID.randomUUID());
+ mgr.getOrCreateCell(getUUID());
+ // 确保 storedMap 初始化并从持久层加载数据
+ this.getCellStoredMap();
+ } else {
+ // SavedData 未就绪:把插入作为本地内存操作(不生成 UUID,不持久化),确保 storedMap 初始化以容纳临时数据
+ this.getCellStoredMap();
+ }
}
Object2ObjectMap map = this.getCellStoredMap();
BigInteger currentAmount = map.getOrDefault(what, BigInteger.ZERO);
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..c56e421 100644
--- a/src/main/java/com/extendedae_plus/util/storage/InfinityDataStorage.java
+++ b/src/main/java/com/extendedae_plus/util/storage/InfinityDataStorage.java
@@ -18,8 +18,10 @@ import net.minecraft.nbt.Tag;
*/
public class InfinityDataStorage {
- /** 空实例(表示没有数据) */
- public static final InfinityDataStorage EMPTY = new InfinityDataStorage();
+ /** 空实例的访问器(返回新实例以避免共享可变状态) */
+ public static InfinityDataStorage empty() {
+ return new InfinityDataStorage();
+ }
/** 序列化的键列表(NBT ListTag,元素为 CompoundTag) */
public ListTag keys;
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..f6a31a9 100644
--- a/src/main/java/com/extendedae_plus/util/storage/InfinityStorageManager.java
+++ b/src/main/java/com/extendedae_plus/util/storage/InfinityStorageManager.java
@@ -10,6 +10,8 @@ import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
+import static com.extendedae_plus.util.ExtendedAELogger.LOGGER;
+
/**
* InfinityStorageManager
*
@@ -21,6 +23,10 @@ import java.util.UUID;
*/
public class InfinityStorageManager extends SavedData {
+ // 当前磁盘格式版本号,增加字段用于向后/向前兼容
+ private static final int FORMAT_VERSION = 1;
+
+
/**
* SavedData 文件名常量
*/
@@ -42,10 +48,18 @@ public class InfinityStorageManager extends SavedData {
* 从 NBT 构造:用于在世界加载时从存档恢复数据
*/
public InfinityStorageManager(CompoundTag nbt) {
+ // 读取格式版本,缺省视为 1(兼容旧档)
+ int version = nbt.contains("format_version") ? nbt.getInt("format_version") : 1;
+ LOGGER.info("Loading InfinityStorageManager format_version={}", version);
+
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")));
+ java.util.UUID uuid = cell.getUUID("uuid");
+ CompoundTag dataTag = cell.getCompound("data");
+ InfinityDataStorage data = InfinityDataStorage.loadFromNBT(dataTag);
+ cells.put(uuid, data);
+ LOGGER.info("Loaded InfinityDataStorage for uuid {}: keys={}, amounts={}", uuid, data.keys.size(), data.amounts.size());
}
setDirty();
}
@@ -71,6 +85,8 @@ public class InfinityStorageManager extends SavedData {
cellList.add(cell);
}
nbt.put("list", cellList);
+ // 写入当前格式版本号,便于未来迁移与兼容判断
+ nbt.putInt("format_version", FORMAT_VERSION);
return nbt;
}
From bc3b280fdb88eef2d2a528854344b57f4b0173c5 Mon Sep 17 00:00:00 2001
From: C-H716 <1536152356@qq.com>
Date: Sat, 20 Sep 2025 10:11:36 +0800
Subject: [PATCH 08/34] =?UTF-8?q?fix:=20=E5=A2=9E=E5=8A=A0=E5=AF=B9?=
=?UTF-8?q?=E5=89=8Dfix=E7=89=88=E6=9C=ACdat=E7=9A=84=E8=AF=BB=E5=8F=96?=
=?UTF-8?q?=E6=94=AF=E6=8C=81?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../util/storage/InfinityStorageManager.java | 64 ++++++++++++++++++-
1 file changed, 63 insertions(+), 1 deletion(-)
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 f6a31a9..24bbe28 100644
--- a/src/main/java/com/extendedae_plus/util/storage/InfinityStorageManager.java
+++ b/src/main/java/com/extendedae_plus/util/storage/InfinityStorageManager.java
@@ -2,10 +2,18 @@ package com.extendedae_plus.util.storage;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.ListTag;
+import net.minecraft.nbt.NbtIo;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.saveddata.SavedData;
+import net.minecraft.world.level.storage.LevelResource;
import org.jetbrains.annotations.NotNull;
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.AtomicMoveNotSupportedException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.StandardCopyOption;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
@@ -50,7 +58,14 @@ public class InfinityStorageManager extends SavedData {
public InfinityStorageManager(CompoundTag nbt) {
// 读取格式版本,缺省视为 1(兼容旧档)
int version = nbt.contains("format_version") ? nbt.getInt("format_version") : 1;
- LOGGER.info("Loading InfinityStorageManager format_version={}", version);
+ if (!nbt.contains("format_version")) {
+ // 旧档未包含 format_version,标记为需要写回以便下次保存写入版本号
+ LOGGER.info("Found legacy InfinityStorageManager dat without format_version; treating as version {} and marking dirty for upgrade", version);
+ // 立即标记为脏以触发尽快写盘(升级写入 format_version)
+ this.setDirty();
+ } else {
+ LOGGER.info("Loading InfinityStorageManager format_version={}", version);
+ }
ListTag cellList = nbt.getList("list", CompoundTag.TAG_COMPOUND);
for (int i = 0; i < cellList.size(); i++) {
@@ -69,6 +84,53 @@ public class InfinityStorageManager extends SavedData {
*/
public static InfinityStorageManager getForLevel(ServerLevel level) {
if (INSTANCE == null && level != null) {
+ // 迁移逻辑:若旧的压缩 dat 文件存在且缺少 format_version,则读取并补上 version,备份原文件
+ try {
+ Path dataDir = level.getServer().getWorldPath(new LevelResource("data"));
+ Path filePath = dataDir.resolve(FILE_NAME + ".dat");
+ File oldFile = filePath.toFile();
+ if (oldFile.exists()) {
+ // 检测是否为 gzip(压缩 NBT)
+ boolean isGzip = false;
+ try (var fis = Files.newInputStream(filePath)) {
+ int b1 = fis.read();
+ int b2 = fis.read();
+ isGzip = (b1 == 0x1F && b2 == 0x8B);
+ } catch (IOException ignored) {
+ }
+ if (isGzip) {
+ try {
+ var root = NbtIo.readCompressed(oldFile);
+ if (!root.contains("format_version")) {
+ // 备份旧文件
+ Path bak = filePath.resolveSibling(FILE_NAME + ".dat.bak");
+ try {
+ Files.copy(filePath, bak);
+ } catch (IOException ignored) {
+ }
+ // 写回并加入 format_version
+ root.putInt("format_version", FORMAT_VERSION);
+ Path tmp = filePath.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, filePath, StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.ATOMIC_MOVE);
+ } catch (AtomicMoveNotSupportedException ex) {
+ Files.move(tmp, filePath, StandardCopyOption.REPLACE_EXISTING);
+ }
+ LOGGER.info("Migrated old compressed {} to include format_version", filePath);
+ }
+ } catch (IOException ex) {
+ LOGGER.info("Failed to migrate old compressed file {}: {}", filePath, ex.getMessage());
+ }
+ }
+ }
+ } catch (Throwable ignored) {
+ }
+
INSTANCE = level.getDataStorage().computeIfAbsent(InfinityStorageManager::new, InfinityStorageManager::new, FILE_NAME);
}
return INSTANCE;
From b812dac9deedb17d5a4656a12161e67e68533171 Mon Sep 17 00:00:00 2001
From: GaLicn <133291877+GaLicn@users.noreply.github.com>
Date: Sat, 20 Sep 2025 14:10:23 +0800
Subject: [PATCH 09/34] fix
---
src/main/java/com/extendedae_plus/ExtendedAEPlus.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/main/java/com/extendedae_plus/ExtendedAEPlus.java b/src/main/java/com/extendedae_plus/ExtendedAEPlus.java
index 23b2770..9d993f8 100644
--- a/src/main/java/com/extendedae_plus/ExtendedAEPlus.java
+++ b/src/main/java/com/extendedae_plus/ExtendedAEPlus.java
@@ -124,7 +124,7 @@ public class ExtendedAEPlus {
private static void onLevelLoad(LevelEvent.Load event) {
if (event.getLevel() instanceof ServerLevel serverLevel) {
// 初始化自定义的文件 I/O 存储管理器
- InfinityStorageManager.INSTANCE.initFromWorld(serverLevel);
+ InfinityStorageManager.getForLevel(serverLevel);
}
}
}
From 39f89183e737d0a1d567c4524a26b99e970ed2ef Mon Sep 17 00:00:00 2001
From: GaLicn <133291877+GaLicn@users.noreply.github.com>
Date: Sat, 20 Sep 2025 15:09:38 +0800
Subject: [PATCH 10/34] =?UTF-8?q?=E4=BF=AE=E5=A4=8Djei=E4=B8=8D=E5=AD=98?=
=?UTF-8?q?=E5=9C=A8=E6=97=B6classnotfonud=E7=9A=84=E9=97=AE=E9=A2=98?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../extendedae_plus/client/InputEvents.java | 148 +++++++++++-------
1 file changed, 88 insertions(+), 60 deletions(-)
diff --git a/src/main/java/com/extendedae_plus/client/InputEvents.java b/src/main/java/com/extendedae_plus/client/InputEvents.java
index 50ee7b2..6a921a4 100644
--- a/src/main/java/com/extendedae_plus/client/InputEvents.java
+++ b/src/main/java/com/extendedae_plus/client/InputEvents.java
@@ -18,6 +18,7 @@ import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.client.event.ScreenEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod;
+import net.minecraftforge.fml.ModList;
import org.lwjgl.glfw.GLFW;
import java.util.Optional;
@@ -28,88 +29,115 @@ public final class InputEvents {
@SubscribeEvent
public static void onMouseButtonPre(ScreenEvent.MouseButtonPressed.Pre event) {
+ // 若未安装 JEI,直接跳过,避免触发 JEI 类加载导致的 NoClassDefFoundError
+ if (!ModList.get().isLoaded("jei")) {
+ return;
+ }
+ // 若 JEI 运行时尚未就绪,跳过
+ if (JeiRuntimeProxy.get() == null) {
+ return;
+ }
// 优先处理:Shift + 左键(拉取或下单)
if (event.getButton() == GLFW.GLFW_MOUSE_BUTTON_LEFT && Screen.hasShiftDown()) {
- double mouseX = event.getMouseX();
- double mouseY = event.getMouseY();
- Optional> hovered = JeiRuntimeProxy.getIngredientUnderMouse(mouseX, mouseY);
- if (hovered.isEmpty()) {
- hovered = JeiRuntimeProxy.getIngredientUnderMouse();
- }
- if (hovered.isPresent()) {
- // 若 JEI 作弊模式开启,则放行给 JEI 处理(Shift+左键=一组)
- if (JeiRuntimeProxy.isJeiCheatModeEnabled()) {
- return;
+ try {
+ double mouseX = event.getMouseX();
+ double mouseY = event.getMouseY();
+ Optional> hovered = JeiRuntimeProxy.getIngredientUnderMouse(mouseX, mouseY);
+ if (hovered.isEmpty()) {
+ hovered = JeiRuntimeProxy.getIngredientUnderMouse();
}
- ITypedIngredient> typed = hovered.get();
- GenericStack stack = GenericEntryStackHelper.ingredientToStack(typed);
- if (stack != null) {
- // 发送到服务端:若网络有库存则拉取一组到空槽,否则若可合成则打开下单界面
- ModNetwork.CHANNEL.sendToServer(new PullFromJeiOrCraftC2SPacket(stack));
- // 消费此次点击,避免 JEI/原版对左键的其它处理
- event.setCanceled(true);
- return;
+ if (hovered.isPresent()) {
+ // 若 JEI 作弊模式开启,则放行给 JEI 处理(Shift+左键=一组)
+ if (JeiRuntimeProxy.isJeiCheatModeEnabled()) {
+ return;
+ }
+ ITypedIngredient> typed = hovered.get();
+ GenericStack stack = GenericEntryStackHelper.ingredientToStack(typed);
+ if (stack != null) {
+ // 发送到服务端:若网络有库存则拉取一组到空槽,否则若可合成则打开下单界面
+ ModNetwork.CHANNEL.sendToServer(new PullFromJeiOrCraftC2SPacket(stack));
+ // 消费此次点击,避免 JEI/原版对左键的其它处理
+ event.setCanceled(true);
+ return;
+ }
}
+ } catch (Throwable ignored) {
+ // 兼容 JEI 版本差异或运行时异常
}
}
// 中键:打开 AE 下单界面(保持原有功能)
if (event.getButton() == GLFW.GLFW_MOUSE_BUTTON_MIDDLE) {
- // 优先在 JEI 配方界面基于坐标获取;若无,再从覆盖层/书签获取
- double mouseX = event.getMouseX();
- double mouseY = event.getMouseY();
- Optional> hovered = JeiRuntimeProxy.getIngredientUnderMouse(mouseX, mouseY);
- if (hovered.isEmpty()) {
- hovered = JeiRuntimeProxy.getIngredientUnderMouse();
+ try {
+ // 优先在 JEI 配方界面基于坐标获取;若无,再从覆盖层/书签获取
+ double mouseX = event.getMouseX();
+ double mouseY = event.getMouseY();
+ Optional> hovered = JeiRuntimeProxy.getIngredientUnderMouse(mouseX, mouseY);
+ if (hovered.isEmpty()) {
+ hovered = JeiRuntimeProxy.getIngredientUnderMouse();
+ }
+ if (hovered.isEmpty()) return;
+
+ ITypedIngredient> typed = hovered.get();
+ // 若 JEI 作弊模式开启,则放行给 JEI 处理(中键=一组)
+ if (JeiRuntimeProxy.isJeiCheatModeEnabled()) {
+ return;
+ }
+ GenericStack stack = GenericEntryStackHelper.ingredientToStack(typed);
+ if (stack == null) return;
+
+ // 发送到服务端,让其验证并打开 CraftAmountMenu
+ ModNetwork.CHANNEL.sendToServer(new OpenCraftFromJeiC2SPacket(stack));
+
+ // 消费此次点击,避免 JEI/原版对中键的其它处理
+ event.setCanceled(true);
+ } catch (Throwable ignored) {
+ // 兼容 JEI 版本差异或运行时异常
}
- if (hovered.isEmpty()) return;
-
- ITypedIngredient> typed = hovered.get();
- // 若 JEI 作弊模式开启,则放行给 JEI 处理(中键=一组)
- if (JeiRuntimeProxy.isJeiCheatModeEnabled()) {
- return;
- }
- GenericStack stack = GenericEntryStackHelper.ingredientToStack(typed);
- if (stack == null) return;
-
- // 发送到服务端,让其验证并打开 CraftAmountMenu
- ModNetwork.CHANNEL.sendToServer(new OpenCraftFromJeiC2SPacket(stack));
-
- // 消费此次点击,避免 JEI/原版对中键的其它处理
- event.setCanceled(true);
}
}
@SubscribeEvent
public static void onKeyPressedPre(ScreenEvent.KeyPressed.Pre event) {
+ // 若未安装 JEI,直接跳过
+ if (!ModList.get().isLoaded("jei")) {
+ return;
+ }
+ if (JeiRuntimeProxy.get() == null) {
+ return;
+ }
if (event.getKeyCode() != GLFW.GLFW_KEY_F) return;
// 仅当鼠标确实悬停在 JEI 配料上时触发
- Optional> hovered = JeiRuntimeProxy.getIngredientUnderMouse();
- if (hovered.isEmpty()) return;
+ try {
+ Optional> hovered = JeiRuntimeProxy.getIngredientUnderMouse();
+ if (hovered.isEmpty()) return;
- ITypedIngredient> typed = hovered.get();
+ ITypedIngredient> typed = hovered.get();
- // 通用获取显示名称(兼容物品/流体等)
- String name = JeiRuntimeProxy.getTypedIngredientDisplayName(typed);
- if (name == null || name.isEmpty()) return;
+ // 通用获取显示名称(兼容物品/流体等)
+ String name = JeiRuntimeProxy.getTypedIngredientDisplayName(typed);
+ if (name == null || name.isEmpty()) return;
- // 写入 AE2 终端的搜索框
- var screen = Minecraft.getInstance().screen;
- if (screen instanceof MEStorageScreen> me) {
- try {
- MEStorageScreenAccessor acc = (MEStorageScreenAccessor) me;
- acc.eap$getSearchField().setValue(name);
- acc.eap$setSearchText(name); // 同步到 Repo 并刷新
- event.setCanceled(true);
- } catch (Throwable ignored) {
+ // 写入 AE2 终端的搜索框
+ var screen = Minecraft.getInstance().screen;
+ if (screen instanceof MEStorageScreen> me) {
+ try {
+ MEStorageScreenAccessor acc = (MEStorageScreenAccessor) me;
+ acc.eap$getSearchField().setValue(name);
+ acc.eap$setSearchText(name); // 同步到 Repo 并刷新
+ event.setCanceled(true);
+ } catch (Throwable ignored) {
+ }
+ }else if (screen instanceof GuiExPatternTerminal> gpt) {
+ try {
+ GuiExPatternTerminalAccessor acc = (GuiExPatternTerminalAccessor) gpt;
+ acc.getSearchOutField().setValue(name);
+ event.setCanceled(true);
+ }catch (Throwable ignored) {}
}
- }else if (screen instanceof GuiExPatternTerminal> gpt) {
- try {
- GuiExPatternTerminalAccessor acc = (GuiExPatternTerminalAccessor) gpt;
- acc.getSearchOutField().setValue(name);
- event.setCanceled(true);
- }catch (Throwable ignored) {}
+ } catch (Throwable ignored) {
+ // 兼容 JEI 版本差异或运行时异常
}
}
}
From d6ac6e8045dcb54a61f478ab61d0cfab84df526c Mon Sep 17 00:00:00 2001
From: C-H716 <1536152356@qq.com>
Date: Sat, 20 Sep 2025 16:29:46 +0800
Subject: [PATCH 11/34] =?UTF-8?q?fix:=20=E8=BF=9B=E4=B8=80=E6=AD=A5?=
=?UTF-8?q?=E7=A1=AE=E4=BF=9D=E6=B8=B8=E6=88=8F=E5=9C=A8=E7=89=B9=E6=AE=8A?=
=?UTF-8?q?=E5=85=B3=E9=97=AD=E6=83=85=E5=86=B5=E4=B8=8B=E8=83=BD=E6=AD=A3?=
=?UTF-8?q?=E7=A1=AE=E4=BF=9D=E5=AD=98=E6=95=B0=E6=8D=AE=EF=BC=8C=E5=A6=82?=
=?UTF-8?q?Alt=20F4?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
gradle.properties | 2 +-
.../InfinityBigIntegerCellInventory.java | 144 ++++++++++--------
.../util/storage/InfinityStorageManager.java | 19 +++
3 files changed, 104 insertions(+), 61 deletions(-)
diff --git a/gradle.properties b/gradle.properties
index 32815b9..2c5731e 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-fix
+mod_version = 1.4.2-fix1
maven_group = com.extendedae_plus
archives_name = extendedae_plus
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 e678dfc..efbf16b 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
@@ -21,6 +21,7 @@ import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.item.ItemStack;
import net.minecraftforge.event.TickEvent;
import net.minecraftforge.event.server.ServerStoppingEvent;
+import net.minecraftforge.eventbus.api.SubscribeEvent;
import java.math.BigDecimal;
import java.math.BigInteger;
@@ -88,54 +89,6 @@ public class InfinityBigIntegerCellInventory implements StorageCell {
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) {
- // 尝试在服务端停止流程开始时初始化 SavedData(确保 INSTANCE 可用以便后续持久化)
- try {
- MinecraftServer server = event.getServer();
- if (server != null) {
- for (ServerLevel level : server.getAllLevels()) {
- InfinityStorageManager.getForLevel(level);
- }
- }
- } catch (Throwable ignored) {
- }
-
- 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) {
// 使用方法局部的 DecimalFormat,避免静态共享的非线程安全问题
@@ -252,12 +205,6 @@ public class InfinityBigIntegerCellInventory implements StorageCell {
if (corruptedTag) {
this.saveChanges();
}
- // 打印加载后的摘要日志
- try {
- var uuid = this.getUUID();
- LOGGER.info("Loaded cell {}: types={}, totalCached={}", uuid, this.getCellStoredMap().size(), this.getTotalStorage());
- } catch (Throwable ignored) {
- }
}
// 标记数据需要保存,并通知容器或直接持久化
@@ -378,12 +325,6 @@ public class InfinityBigIntegerCellInventory implements StorageCell {
stack.getOrCreateTag().putInt("types", typesCount);
}
isPersisted = true;
- // 打印持久化摘要日志
- try {
- var uuid = this.getUUID();
- LOGGER.info("Persisted cell {}: types={}, totalCached={}", uuid, this.getCellStoredMap().size(), this.getTotalStorage());
- } catch (Throwable ignored) {
- }
}
// 插入物品到存储单元
@@ -464,4 +405,87 @@ public class InfinityBigIntegerCellInventory implements StorageCell {
// 使用缓存的 totalStored,避免每次全表扫描
return formatBigInteger(totalStored);
}
+
+ /** 定时 tick 保存计数器 */
+ private static int SAVE_TICK_COUNTER = 0;
+
+ /** tick 间隔,单位为服务器 tick(20 tick ≈ 1 秒) */
+ private static final int SAVE_TICK_INTERVAL = 20; // 约30秒
+
+ /** 服务器 tick 事件:处理队列并定时保存 */
+ @SubscribeEvent
+ public static void onServerTick(TickEvent.ServerTickEvent event) {
+ if (event.phase != TickEvent.Phase.END) return;
+
+ // 处理待持久化队列
+ InfinityBigIntegerCellInventory inv;
+ while ((inv = PENDING_PERSIST.poll()) != null) {
+ try {
+ if (!inv.isPersisted) {
+ inv.persist();
+ }
+ } catch (Throwable ex) {
+ LOGGER.info("InfinityBigIntegerCellInventory onServerTick error: {}", ex.getMessage());
+ }
+ }
+
+ // 定时标记全局存储为脏
+ try {
+ if (++SAVE_TICK_COUNTER >= SAVE_TICK_INTERVAL) {
+ SAVE_TICK_COUNTER = 0;
+ if (InfinityStorageManager.INSTANCE != null) {
+ InfinityStorageManager.INSTANCE.setDirty();
+ }
+ }
+ } catch (Throwable ex) {
+ LOGGER.info("InfinityBigIntegerCellInventory tick dirty set error: {}", ex.getMessage());
+ }
+ }
+
+ // 在服务器停止时被调用,立即强制持久化队列中的所有实例
+ @SubscribeEvent
+ public static void onServerStopping(ServerStoppingEvent event) {
+ // 尝试在服务端停止流程开始时初始化 SavedData(确保 INSTANCE 可用以便后续持久化)
+ try {
+ MinecraftServer server = event.getServer();
+ if (server != null) {
+ for (ServerLevel level : server.getAllLevels()) {
+ InfinityStorageManager.getForLevel(level);
+ }
+ }
+ } catch (Throwable ignored) {
+ LOGGER.info("InfinityBigIntegerCellInventory onServerStopping init error: {}", ignored.getMessage());
+ }
+
+ InfinityBigIntegerCellInventory inv;
+ while ((inv = PENDING_PERSIST.poll()) != null) {
+ try {
+ if (!inv.isPersisted) {
+ inv.persist();
+ }
+ } catch (Throwable ex) {
+ LOGGER.info("InfinityBigIntegerCellInventory onServerStopping persist error: {}", ex.getMessage()); }
+ }
+
+ // 强制写回磁盘,确保直接关闭游戏也能保存
+ try {
+ InfinityStorageManager mgr = InfinityStorageManager.INSTANCE;
+ if (mgr != null) {
+ MinecraftServer server = event.getServer();
+ if (server != null) {
+ for (ServerLevel level : server.getAllLevels()) {
+ try {
+ mgr.forceSaveAll(level);
+ LOGGER.info("Stop forceSaveAll for level {}", level.dimension().location());
+ } catch (Throwable ex) {
+ LOGGER.info("InfinityBigIntegerCellInventory forceSaveAll error for level {}: {}",
+ level.dimension().location(), ex.getMessage());
+ }
+ }
+ }
+ }
+ } catch (Throwable ex) {
+ LOGGER.info("InfinityBigIntegerCellInventory onServerStopping forceSaveAll error: {}", ex.getMessage());
+ }
+ }
}
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 24bbe28..5f8f3fe 100644
--- a/src/main/java/com/extendedae_plus/util/storage/InfinityStorageManager.java
+++ b/src/main/java/com/extendedae_plus/util/storage/InfinityStorageManager.java
@@ -189,4 +189,23 @@ public class InfinityStorageManager extends SavedData {
cells.remove(uuid);
setDirty();
}
+
+ /**
+ * 强制将内存中的所有 InfinityDataStorage 写入磁盘
+ * 可在服务器停止或 tick 中调用,确保数据不会丢失
+ */
+ public void forceSaveAll(ServerLevel level) {
+ try {
+ if (level != null) {
+ this.setDirty();
+ CompoundTag nbt = new CompoundTag();
+ this.save(nbt);
+ File file = level.getServer().getWorldPath(new LevelResource("data"))
+ .resolve(FILE_NAME + ".dat").toFile();
+ NbtIo.writeCompressed(nbt, file);
+ }
+ } catch (Throwable ex) {
+ LOGGER.info("InfinityStorageManager forceSaveAll error: {}", ex.getMessage());
+ }
+ }
}
From 55628250bcccfb9f7aa8a63810177af0b711889f Mon Sep 17 00:00:00 2001
From: C-H716 <1536152356@qq.com>
Date: Sat, 20 Sep 2025 19:28:11 +0800
Subject: [PATCH 12/34] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E6=8C=87?=
=?UTF-8?q?=E4=BB=A4=E7=94=A8=E4=BA=8E=E8=8E=B7=E5=8F=96=E5=AD=98=E6=A1=A3?=
=?UTF-8?q?=E5=8A=A0=E8=BD=BD=E7=9A=84=E5=85=A8=E9=83=A8=E6=97=A0=E9=99=90?=
=?UTF-8?q?=E5=AD=98=E5=82=A8=E5=85=83=E4=BB=B6?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
gradle.properties | 2 +-
.../com/extendedae_plus/ExtendedAEPlus.java | 8 +++
.../ae/items/InfinityBigIntegerCellItem.java | 14 +++++
.../command/InfinityDiskGiveCommand.java | 61 +++++++++++++++++++
.../util/storage/InfinityStorageManager.java | 20 ++++++
5 files changed, 104 insertions(+), 1 deletion(-)
create mode 100644 src/main/java/com/extendedae_plus/command/InfinityDiskGiveCommand.java
diff --git a/gradle.properties b/gradle.properties
index 2c5731e..b86c90f 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-fix1
+mod_version = 1.4.2-fix2
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 25d675e..3e02a54 100644
--- a/src/main/java/com/extendedae_plus/ExtendedAEPlus.java
+++ b/src/main/java/com/extendedae_plus/ExtendedAEPlus.java
@@ -17,6 +17,8 @@ import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.event.level.LevelEvent;
import net.minecraftforge.eventbus.api.IEventBus;
import net.minecraftforge.eventbus.api.SubscribeEvent;
+import net.minecraftforge.event.RegisterCommandsEvent;
+import com.extendedae_plus.command.InfinityDiskGiveCommand;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent;
import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent;
@@ -53,6 +55,8 @@ public class ExtendedAEPlus {
// 注册到Forge事件总线
MinecraftForge.EVENT_BUS.register(this);
MinecraftForge.EVENT_BUS.addListener(ExtendedAEPlus::onLevelLoad);
+ // 注册命令注册监听
+ MinecraftForge.EVENT_BUS.addListener(this::onRegisterCommands);
// 注册通用配置
ModConfig.init();
// 注册 InfinityBigIntegerCellInventory 的事件监听(tick flush 与停止时 flush)
@@ -123,4 +127,8 @@ public class ExtendedAEPlus {
InfinityStorageManager.getForLevel(serverLevel);
}
}
+
+ private void onRegisterCommands(RegisterCommandsEvent event) {
+ InfinityDiskGiveCommand.register(event.getDispatcher());
+ }
}
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..75baebf 100644
--- a/src/main/java/com/extendedae_plus/ae/items/InfinityBigIntegerCellItem.java
+++ b/src/main/java/com/extendedae_plus/ae/items/InfinityBigIntegerCellItem.java
@@ -7,15 +7,18 @@ import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.LongTag;
import net.minecraft.nbt.Tag;
import net.minecraft.network.chat.Component;
+import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.TooltipFlag;
import net.minecraft.world.level.Level;
+import net.minecraftforge.registries.ForgeRegistries;
import org.jetbrains.annotations.NotNull;
import javax.annotation.Nullable;
import java.math.BigInteger;
import java.util.List;
+import java.util.Objects;
public class InfinityBigIntegerCellItem extends Item {
@@ -79,4 +82,15 @@ public class InfinityBigIntegerCellItem extends Item {
}
}
}
+
+ /**
+ * 创建一个带有指定 UUID 的 Infinity 磁盘 ItemStack
+ */
+ public static ItemStack withUUID(java.util.UUID uuid) {
+ ItemStack stack = new ItemStack(Objects.requireNonNull(
+ ForgeRegistries.ITEMS.getValue(new ResourceLocation("extendedae_plus", "infinity_biginteger_cell")
+ )));
+ stack.getOrCreateTag().putUUID("uuid", uuid);
+ return stack;
+ }
}
\ No newline at end of file
diff --git a/src/main/java/com/extendedae_plus/command/InfinityDiskGiveCommand.java b/src/main/java/com/extendedae_plus/command/InfinityDiskGiveCommand.java
new file mode 100644
index 0000000..306e483
--- /dev/null
+++ b/src/main/java/com/extendedae_plus/command/InfinityDiskGiveCommand.java
@@ -0,0 +1,61 @@
+package com.extendedae_plus.command;
+
+import com.extendedae_plus.ae.items.InfinityBigIntegerCellItem;
+import com.extendedae_plus.util.storage.InfinityStorageManager;
+import com.mojang.brigadier.CommandDispatcher;
+import com.mojang.brigadier.context.CommandContext;
+import net.minecraft.commands.CommandSourceStack;
+import net.minecraft.commands.Commands;
+import net.minecraft.network.chat.Component;
+import net.minecraft.server.level.ServerLevel;
+import net.minecraft.server.level.ServerPlayer;
+import net.minecraft.world.item.ItemStack;
+
+import java.util.UUID;
+
+/**
+ * /eap give_infinity_disks
+ * 为执行命令的玩家生成当前世界已加载的所有 Infinity 磁盘(按 UUID)并发放到玩家物品栏
+ */
+public class InfinityDiskGiveCommand {
+
+ public static void register(CommandDispatcher dispatcher) {
+ dispatcher.register(Commands.literal("eap").then(
+ Commands.literal("give_infinity_disks").executes(InfinityDiskGiveCommand::execute)
+ ));
+ }
+
+ private static int execute(CommandContext ctx) {
+ CommandSourceStack source = ctx.getSource();
+ try {
+ ServerPlayer player = source.getPlayerOrException();
+ if (player.level() == null || !(player.level() instanceof ServerLevel)) {
+ source.sendFailure(Component.literal("This command must be run on server side."));
+ return 0;
+ }
+ InfinityStorageManager mgr = InfinityStorageManager.getForLevel((ServerLevel) player.level());
+ if (mgr == null) {
+ source.sendFailure(Component.literal("InfinityStorageManager is not initialized."));
+ return 0;
+ }
+
+ int given = 0;
+ for (UUID id : mgr.getAllLoadedUUIDs()) {
+ ItemStack stack = InfinityBigIntegerCellItem.withUUID(id);
+ if (!player.getInventory().add(stack)) {
+ // 若玩家物品栏已满,则扔在地上
+ player.drop(stack, false);
+ }
+ given++;
+ }
+ final int finalGiven = given;
+ source.sendSuccess(() -> Component.literal("Gave " + finalGiven + " infinity disks."), false);
+ return given;
+ } catch (Exception ex) {
+ source.sendFailure(Component.literal("Error: " + ex.getMessage()));
+ return 0;
+ }
+ }
+}
+
+
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 5f8f3fe..6ba1080 100644
--- a/src/main/java/com/extendedae_plus/util/storage/InfinityStorageManager.java
+++ b/src/main/java/com/extendedae_plus/util/storage/InfinityStorageManager.java
@@ -48,6 +48,13 @@ public class InfinityStorageManager extends SavedData {
*/
private final Map cells = new HashMap<>();
+ /**
+ * 返回当前已加载的所有 UUID 的不可变视图,用于命令或调试用途
+ */
+ public java.util.Set getAllLoadedUUIDs() {
+ return java.util.Collections.unmodifiableSet(cells.keySet());
+ }
+
public InfinityStorageManager() {
setDirty();
}
@@ -203,6 +210,19 @@ public class InfinityStorageManager extends SavedData {
File file = level.getServer().getWorldPath(new LevelResource("data"))
.resolve(FILE_NAME + ".dat").toFile();
NbtIo.writeCompressed(nbt, file);
+
+ // 打印所有已保存的无限磁盘 UUID 及相关信息
+ StringBuilder sb = new StringBuilder();
+ sb.append("Saving Infinity Disks (UUIDs and info):\n");
+ for (Map.Entry entry : cells.entrySet()) {
+ UUID uuid = entry.getKey();
+ InfinityDataStorage data = entry.getValue();
+ int types = (data.keys != null) ? data.keys.size() : 0;
+ sb.append(" - UUID: ").append(uuid)
+ .append(", Types: ").append(types)
+ .append("\n");
+ }
+ LOGGER.info(sb.toString());
}
} catch (Throwable ex) {
LOGGER.info("InfinityStorageManager forceSaveAll error: {}", ex.getMessage());
From 45121b8647a5ec2f0a26bb2868928a578f1b36bc Mon Sep 17 00:00:00 2001
From: GaLicn <133291877+GaLicn@users.noreply.github.com>
Date: Mon, 22 Sep 2025 17:45:18 +0800
Subject: [PATCH 13/34] =?UTF-8?q?=E5=A2=9E=E5=8A=A0me=E9=A2=91=E9=81=93?=
=?UTF-8?q?=E5=8D=A1?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../ae/items/ChannelCardItem.java | 63 +++++++++++++++
.../bridge/InterfaceWirelessLinkBridge.java | 8 ++
.../com/extendedae_plus/init/ModItems.java | 7 ++
.../extendedae_plus/init/UpgradeCards.java | 12 ++-
.../InterfaceLogicChannelCardMixin.java | 74 ++++++++++++++++++
.../InterfaceLogicChannelLinkBridge.java | 8 ++
.../helpers/InterfaceLogicTickerMixin.java | 30 +++++++
.../endpoint/InterfaceNodeEndpointImpl.java | 49 ++++++++++++
.../assets/extendedae_plus/lang/en_us.json | 7 +-
.../assets/extendedae_plus/lang/zh_cn.json | 7 +-
.../models/item/channel_card.json | 6 ++
.../textures/item/channel_card.png | Bin 0 -> 455 bytes
.../resources/extendedae_plus.mixins.json | 4 +-
13 files changed, 271 insertions(+), 4 deletions(-)
create mode 100644 src/main/java/com/extendedae_plus/ae/items/ChannelCardItem.java
create mode 100644 src/main/java/com/extendedae_plus/bridge/InterfaceWirelessLinkBridge.java
create mode 100644 src/main/java/com/extendedae_plus/mixin/ae2/helpers/InterfaceLogicChannelCardMixin.java
create mode 100644 src/main/java/com/extendedae_plus/mixin/ae2/helpers/InterfaceLogicChannelLinkBridge.java
create mode 100644 src/main/java/com/extendedae_plus/mixin/ae2/helpers/InterfaceLogicTickerMixin.java
create mode 100644 src/main/java/com/extendedae_plus/wireless/endpoint/InterfaceNodeEndpointImpl.java
create mode 100644 src/main/resources/assets/extendedae_plus/models/item/channel_card.json
create mode 100644 src/main/resources/assets/extendedae_plus/textures/item/channel_card.png
diff --git a/src/main/java/com/extendedae_plus/ae/items/ChannelCardItem.java b/src/main/java/com/extendedae_plus/ae/items/ChannelCardItem.java
new file mode 100644
index 0000000..1d68551
--- /dev/null
+++ b/src/main/java/com/extendedae_plus/ae/items/ChannelCardItem.java
@@ -0,0 +1,63 @@
+package com.extendedae_plus.ae.items;
+
+import appeng.items.materials.UpgradeCardItem;
+import net.minecraft.nbt.CompoundTag;
+import net.minecraft.network.chat.Component;
+import net.minecraft.world.item.Item;
+import net.minecraft.world.item.ItemStack;
+import net.minecraft.world.item.TooltipFlag;
+import net.minecraft.world.InteractionHand;
+import net.minecraft.world.InteractionResultHolder;
+import net.minecraft.world.entity.player.Player;
+import net.minecraft.world.level.Level;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.List;
+
+/**
+ * 频道卡(MVP):仅存储一个 long 类型的频道号到 NBT:"channel"。
+ * 继承 AE2 的 UpgradeCardItem 以复用升级卡判定与提示框架。
+ */
+public class ChannelCardItem extends UpgradeCardItem {
+ public static final String TAG_CHANNEL = "channel";
+
+ public ChannelCardItem(Item.Properties properties) {
+ super(properties);
+ }
+
+ public static void setChannel(ItemStack stack, long channel) {
+ CompoundTag tag = stack.getOrCreateTag();
+ tag.putLong(TAG_CHANNEL, channel);
+ }
+
+ public static long getChannel(ItemStack stack) {
+ CompoundTag tag = stack.getTag();
+ return tag != null && tag.contains(TAG_CHANNEL) ? tag.getLong(TAG_CHANNEL) : 0L;
+ }
+
+ @Override
+ public void appendHoverText(ItemStack stack, @Nullable Level level, List lines, TooltipFlag flag) {
+ super.appendHoverText(stack, level, lines, flag);
+ long ch = getChannel(stack);
+ if (ch == 0L) {
+ lines.add(Component.translatable("item.extendedae_plus.channel_card.channel.unset"));
+ } else {
+ lines.add(Component.translatable("item.extendedae_plus.channel_card.channel", ch));
+ }
+ }
+
+ @Override
+ public InteractionResultHolder use(Level level, Player player, InteractionHand hand) {
+ ItemStack stack = player.getItemInHand(hand);
+ if (!level.isClientSide) {
+ long ch = getChannel(stack);
+ boolean dec = player.isShiftKeyDown();
+ long next = dec ? Math.max(0L, ch - 1L) : ch + 1L;
+ if (next != ch) {
+ setChannel(stack, next);
+ player.displayClientMessage(Component.translatable("item.extendedae_plus.channel_card.set", next), true);
+ }
+ }
+ return InteractionResultHolder.sidedSuccess(stack, level.isClientSide);
+ }
+}
diff --git a/src/main/java/com/extendedae_plus/bridge/InterfaceWirelessLinkBridge.java b/src/main/java/com/extendedae_plus/bridge/InterfaceWirelessLinkBridge.java
new file mode 100644
index 0000000..097b091
--- /dev/null
+++ b/src/main/java/com/extendedae_plus/bridge/InterfaceWirelessLinkBridge.java
@@ -0,0 +1,8 @@
+package com.extendedae_plus.bridge;
+
+/**
+ * 非 mixin 包下的桥接接口,供 mixin 进行 instanceof 检测和回调。
+ */
+public interface InterfaceWirelessLinkBridge {
+ void extendedae_plus$updateWirelessLink();
+}
diff --git a/src/main/java/com/extendedae_plus/init/ModItems.java b/src/main/java/com/extendedae_plus/init/ModItems.java
index 0f7b430..79b4209 100644
--- a/src/main/java/com/extendedae_plus/init/ModItems.java
+++ b/src/main/java/com/extendedae_plus/init/ModItems.java
@@ -5,6 +5,7 @@ import appeng.api.parts.PartModels;
import appeng.items.parts.PartModelsHelper;
import com.extendedae_plus.ExtendedAEPlus;
import com.extendedae_plus.ae.definitions.upgrades.EntitySpeedCardItem;
+import com.extendedae_plus.ae.items.ChannelCardItem;
import com.extendedae_plus.ae.items.EntitySpeedTickerPartItem;
import com.extendedae_plus.ae.items.InfinityBigIntegerCellItem;
import net.minecraft.world.item.BlockItem;
@@ -76,6 +77,12 @@ public final class ModItems {
"infinity_biginteger_cell", InfinityBigIntegerCellItem::new
);
+ // 频道卡(作为 AE 升级卡使用)
+ public static final RegistryObject CHANNEL_CARD = ITEMS.register(
+ "channel_card",
+ () -> new ChannelCardItem(new Item.Properties())
+ );
+
/**
* 为 PartItem 注册 AE2 部件模型。
diff --git a/src/main/java/com/extendedae_plus/init/UpgradeCards.java b/src/main/java/com/extendedae_plus/init/UpgradeCards.java
index ddd73d4..05dec0d 100644
--- a/src/main/java/com/extendedae_plus/init/UpgradeCards.java
+++ b/src/main/java/com/extendedae_plus/init/UpgradeCards.java
@@ -1,10 +1,15 @@
package com.extendedae_plus.init;
import appeng.api.upgrades.Upgrades;
+import appeng.core.definitions.AEBlocks;
import appeng.core.definitions.AEItems;
+import appeng.core.definitions.AEParts;
+import appeng.core.localization.GuiText;
import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent;
-
+/**
+ *
+ */
public class UpgradeCards {
public UpgradeCards(final FMLCommonSetupEvent event) {
event.enqueueWork(() -> {
@@ -12,6 +17,11 @@ public class UpgradeCards {
Upgrades.add(AEItems.ENERGY_CARD, ModItems.ENTITY_TICKER_PART_ITEM.get(), 8, "group.entity_ticker.name");
// 使用单一的 UpgradeCard Item 作为注册键,总共允许安装 4 张(不同等级由 ItemStack NBT 区分)
Upgrades.add(ModItems.ENTITY_SPEED_CARD.get(), ModItems.ENTITY_TICKER_PART_ITEM.get(), 4, "group.entity_ticker.name");
+
+ // 新增:频道卡仅允许安装在 ME 接口(方块与部件)上,每台最多 1 张
+ String interfaceGroup = GuiText.Interface.getTranslationKey();
+ Upgrades.add(ModItems.CHANNEL_CARD.get(), AEBlocks.INTERFACE, 1, interfaceGroup);
+ Upgrades.add(ModItems.CHANNEL_CARD.get(), AEParts.INTERFACE, 1, interfaceGroup);
});
}
}
\ No newline at end of file
diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/helpers/InterfaceLogicChannelCardMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/helpers/InterfaceLogicChannelCardMixin.java
new file mode 100644
index 0000000..659b120
--- /dev/null
+++ b/src/main/java/com/extendedae_plus/mixin/ae2/helpers/InterfaceLogicChannelCardMixin.java
@@ -0,0 +1,74 @@
+package com.extendedae_plus.mixin.ae2.helpers;
+
+import appeng.api.upgrades.IUpgradeInventory;
+import appeng.helpers.InterfaceLogic;
+import appeng.helpers.InterfaceLogicHost;
+import com.extendedae_plus.ae.items.ChannelCardItem;
+import com.extendedae_plus.bridge.InterfaceWirelessLinkBridge;
+import com.extendedae_plus.init.ModItems;
+import com.extendedae_plus.wireless.IWirelessEndpoint;
+import com.extendedae_plus.wireless.WirelessSlaveLink;
+import com.extendedae_plus.wireless.endpoint.InterfaceNodeEndpointImpl;
+import net.minecraft.server.level.ServerLevel;
+import net.minecraft.world.item.ItemStack;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.Shadow;
+import org.spongepowered.asm.mixin.Unique;
+import org.spongepowered.asm.mixin.injection.At;
+import org.spongepowered.asm.mixin.injection.Inject;
+import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
+
+@Mixin(InterfaceLogic.class)
+public abstract class InterfaceLogicChannelCardMixin implements InterfaceWirelessLinkBridge {
+
+ @Shadow(remap = false) public abstract IUpgradeInventory getUpgrades();
+
+ @Shadow(remap = false) public abstract appeng.api.networking.IGridNode getActionableNode();
+
+ @Shadow(remap = false) protected InterfaceLogicHost host;
+
+ @Unique
+ private WirelessSlaveLink extendedae_plus$link;
+
+ @Inject(method = "onUpgradesChanged", at = @At("TAIL"), remap = false)
+ private void extendedae_plus$onUpgradesChangedTail(CallbackInfo ci) {
+ handleChannelCardChange();
+ }
+
+ @Inject(method = "readFromNBT", at = @At("TAIL"), remap = false)
+ private void extendedae_plus$afterReadNBT(net.minecraft.nbt.CompoundTag tag, CallbackInfo ci) {
+ // 重载后根据卡状态恢复连接
+ handleChannelCardChange();
+ }
+
+ @Inject(method = "clearContent", at = @At("HEAD"), remap = false)
+ private void extendedae_plus$onClearContent(CallbackInfo ci) {
+ if (extendedae_plus$link != null) {
+ extendedae_plus$link.onUnloadOrRemove();
+ }
+ }
+
+ @Unique
+ private void handleChannelCardChange() {
+ var inv = getUpgrades();
+ long channel = 0L;
+ for (ItemStack stack : inv) {
+ if (!stack.isEmpty() && stack.getItem() == ModItems.CHANNEL_CARD.get()) {
+ channel = ChannelCardItem.getChannel(stack);
+ break;
+ }
+ }
+ if (extendedae_plus$link == null) {
+ IWirelessEndpoint endpoint = new InterfaceNodeEndpointImpl(host, this::getActionableNode);
+ extendedae_plus$link = new WirelessSlaveLink(endpoint);
+ }
+ extendedae_plus$link.setFrequency(channel);
+ }
+
+ @Override
+ public void extendedae_plus$updateWirelessLink() {
+ if (extendedae_plus$link != null) {
+ extendedae_plus$link.updateStatus();
+ }
+ }
+}
diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/helpers/InterfaceLogicChannelLinkBridge.java b/src/main/java/com/extendedae_plus/mixin/ae2/helpers/InterfaceLogicChannelLinkBridge.java
new file mode 100644
index 0000000..498d023
--- /dev/null
+++ b/src/main/java/com/extendedae_plus/mixin/ae2/helpers/InterfaceLogicChannelLinkBridge.java
@@ -0,0 +1,8 @@
+package com.extendedae_plus.bridge;
+
+/**
+ * 旧名兼容:已迁移到非 mixin 包,避免 Mixin 处理器禁止直接引用。
+ */
+public interface InterfaceLogicChannelLinkBridge {
+ void extendedae_plus$updateWirelessLink();
+}
diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/helpers/InterfaceLogicTickerMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/helpers/InterfaceLogicTickerMixin.java
new file mode 100644
index 0000000..77f9cbc
--- /dev/null
+++ b/src/main/java/com/extendedae_plus/mixin/ae2/helpers/InterfaceLogicTickerMixin.java
@@ -0,0 +1,30 @@
+package com.extendedae_plus.mixin.ae2.helpers;
+
+import appeng.helpers.InterfaceLogic;
+import com.extendedae_plus.bridge.InterfaceWirelessLinkBridge;
+import org.spongepowered.asm.mixin.Final;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.Shadow;
+import org.spongepowered.asm.mixin.injection.At;
+import org.spongepowered.asm.mixin.injection.Inject;
+import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
+
+/**
+ * 注入到 InterfaceLogic.Ticker 的每tick回调,驱动无线链接状态更新。
+ */
+@Mixin(targets = "appeng.helpers.InterfaceLogic$Ticker")
+public abstract class InterfaceLogicTickerMixin {
+
+ // Mixin 访问内部类的外部引用字段(javac 生成名 this$0)
+ @Shadow(remap = false)
+ @Final
+ private InterfaceLogic this$0;
+
+ @Inject(method = "tickingRequest", at = @At("TAIL"), remap = false)
+ private void extendedae_plus$tickTail(appeng.api.networking.IGridNode node, int ticksSinceLastCall,
+ CallbackInfoReturnable cir) {
+ if (this$0 instanceof InterfaceWirelessLinkBridge bridge) {
+ bridge.extendedae_plus$updateWirelessLink();
+ }
+ }
+}
diff --git a/src/main/java/com/extendedae_plus/wireless/endpoint/InterfaceNodeEndpointImpl.java b/src/main/java/com/extendedae_plus/wireless/endpoint/InterfaceNodeEndpointImpl.java
new file mode 100644
index 0000000..6736f4c
--- /dev/null
+++ b/src/main/java/com/extendedae_plus/wireless/endpoint/InterfaceNodeEndpointImpl.java
@@ -0,0 +1,49 @@
+package com.extendedae_plus.wireless.endpoint;
+
+import appeng.api.networking.IGridNode;
+import appeng.helpers.InterfaceLogicHost;
+import com.extendedae_plus.wireless.IWirelessEndpoint;
+import net.minecraft.core.BlockPos;
+import net.minecraft.server.level.ServerLevel;
+import net.minecraft.world.level.Level;
+
+import java.util.Objects;
+import java.util.function.Supplier;
+
+/**
+ * IWirelessEndpoint 实现:基于 InterfaceLogicHost 与节点提供者。
+ */
+public class InterfaceNodeEndpointImpl implements IWirelessEndpoint {
+ private final InterfaceLogicHost host;
+ private final Supplier nodeSupplier;
+
+ public InterfaceNodeEndpointImpl(InterfaceLogicHost host, Supplier nodeSupplier) {
+ this.host = Objects.requireNonNull(host);
+ this.nodeSupplier = Objects.requireNonNull(nodeSupplier);
+ }
+
+ @Override
+ public ServerLevel getServerLevel() {
+ var be = host.getBlockEntity();
+ if (be == null) return null;
+ Level lvl = be.getLevel();
+ return (lvl instanceof ServerLevel) ? (ServerLevel) lvl : null;
+ }
+
+ @Override
+ public BlockPos getBlockPos() {
+ var be = host.getBlockEntity();
+ return be != null ? be.getBlockPos() : BlockPos.ZERO;
+ }
+
+ @Override
+ public IGridNode getGridNode() {
+ return nodeSupplier.get();
+ }
+
+ @Override
+ public boolean isEndpointRemoved() {
+ var be = host.getBlockEntity();
+ return be == null || be.isRemoved();
+ }
+}
diff --git a/src/main/resources/assets/extendedae_plus/lang/en_us.json b/src/main/resources/assets/extendedae_plus/lang/en_us.json
index 320a792..ef955fa 100644
--- a/src/main/resources/assets/extendedae_plus/lang/en_us.json
+++ b/src/main/resources/assets/extendedae_plus/lang/en_us.json
@@ -87,5 +87,10 @@
"config.extendedae_plus.option.entityTickerBlackList": "Entity Ticker Blacklist",
"config.extendedae_plus.option.entityTickerMultipliers": "Entity Ticker Extra Consumption Multipliers",
"config.extendedae_plus.option.craftingPauseThreshold": "AE synthesis calculation pause check threshold",
- "block.extendedae_plus.assembler_matrix_upload_core": "Assembler Matrix Upload Core"
+ "block.extendedae_plus.assembler_matrix_upload_core": "Assembler Matrix Upload Core",
+
+ "item.extendedae_plus.channel_card": "Channel Card",
+ "item.extendedae_plus.channel_card.channel": "Frequency: %s",
+ "item.extendedae_plus.channel_card.channel.unset": "Frequency: Unset",
+ "item.extendedae_plus.channel_card.set": "Frequency set to: %s"
}
\ No newline at end of file
diff --git a/src/main/resources/assets/extendedae_plus/lang/zh_cn.json b/src/main/resources/assets/extendedae_plus/lang/zh_cn.json
index d7996ab..210532c 100644
--- a/src/main/resources/assets/extendedae_plus/lang/zh_cn.json
+++ b/src/main/resources/assets/extendedae_plus/lang/zh_cn.json
@@ -92,5 +92,10 @@
"block.extendedae_plus.assembler_matrix_upload_core.tooltip.upload_success": "样板已上传到装配矩阵",
"block.extendedae_plus.assembler_matrix_upload_core.tooltip.upload_fail_not_crafting": "仅支持上传合成样板,处理样板将被忽略",
"block.extendedae_plus.assembler_matrix_upload_core.tooltip.upload_fail_no_matrix": "未在当前网络中找到已成型的装配矩阵",
- "block.extendedae_plus.assembler_matrix_upload_core.tooltip.upload_fail_full": "装配矩阵的样板仓已满或无法插入"
+ "block.extendedae_plus.assembler_matrix_upload_core.tooltip.upload_fail_full": "装配矩阵的样板仓已满或无法插入",
+
+ "item.extendedae_plus.channel_card": "频道卡",
+ "item.extendedae_plus.channel_card.channel": "频率:%s",
+ "item.extendedae_plus.channel_card.channel.unset": "频率:未设置",
+ "item.extendedae_plus.channel_card.set": "已设置频率:%s"
}
\ No newline at end of file
diff --git a/src/main/resources/assets/extendedae_plus/models/item/channel_card.json b/src/main/resources/assets/extendedae_plus/models/item/channel_card.json
new file mode 100644
index 0000000..8699938
--- /dev/null
+++ b/src/main/resources/assets/extendedae_plus/models/item/channel_card.json
@@ -0,0 +1,6 @@
+{
+ "parent": "item/generated",
+ "textures": {
+ "layer0": "extendedae_plus:item/channel_card"
+ }
+}
diff --git a/src/main/resources/assets/extendedae_plus/textures/item/channel_card.png b/src/main/resources/assets/extendedae_plus/textures/item/channel_card.png
new file mode 100644
index 0000000000000000000000000000000000000000..2fe67ab91aa171c096bdf1c628d56947b2663360
GIT binary patch
literal 455
zcmV;&0XY7NP)oOTmiB@DmIl35%|JuY>F2;O!3E$3d;}_*)Yk`AHwmg5
xM&r^95{Cy$a#9jFGr=+qx+ZJ^h^~e#J^)VZn#5&}mr?)#002ovPDHLkV1gDyzjXir
literal 0
HcmV?d00001
diff --git a/src/main/resources/extendedae_plus.mixins.json b/src/main/resources/extendedae_plus.mixins.json
index aaad6df..a856a94 100644
--- a/src/main/resources/extendedae_plus.mixins.json
+++ b/src/main/resources/extendedae_plus.mixins.json
@@ -67,7 +67,9 @@
"extendedae.common.TileExPatternProviderMixin",
"extendedae.container.ContainerExPatternProviderMixin",
"extendedae.container.ContainerExPatternTerminalMixin",
- "extendedae.container.ContainerWirelessExPatternTerminalMixin"
+ "extendedae.container.ContainerWirelessExPatternTerminalMixin",
+ "ae2.helpers.InterfaceLogicChannelCardMixin",
+ "ae2.helpers.InterfaceLogicTickerMixin"
],
"injectors": {
"defaultRequire": 1
From 8a4950f2446e2588cd55fcbca73e1b18027b57c0 Mon Sep 17 00:00:00 2001
From: GaLicn <133291877+GaLicn@users.noreply.github.com>
Date: Mon, 22 Sep 2025 18:24:29 +0800
Subject: [PATCH 14/34] =?UTF-8?q?=E4=B8=BA=E6=A0=B7=E6=9D=BF=E4=BE=9B?=
=?UTF-8?q?=E5=BA=94=E5=99=A8=E6=B7=BB=E5=8A=A0=E5=8D=87=E7=BA=A7=E6=A7=BD?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../bridge/IUpgradableMenu.java | 12 ++
.../PatternProviderScreenUpgradesMixin.java | 48 ++++++++
...PatternProviderLogicHostUpgradesMixin.java | 21 ++++
.../PatternProviderLogicTickerMixin.java | 30 +++++
.../PatternProviderLogicUpgradesMixin.java | 112 ++++++++++++++++++
.../PatternProviderMenuUpgradesMixin.java | 49 ++++++++
.../endpoint/GenericNodeEndpointImpl.java | 49 ++++++++
.../resources/extendedae_plus.mixins.json | 5 +
8 files changed, 326 insertions(+)
create mode 100644 src/main/java/com/extendedae_plus/bridge/IUpgradableMenu.java
create mode 100644 src/main/java/com/extendedae_plus/mixin/ae2/client/gui/PatternProviderScreenUpgradesMixin.java
create mode 100644 src/main/java/com/extendedae_plus/mixin/ae2/helpers/patternprovider/PatternProviderLogicHostUpgradesMixin.java
create mode 100644 src/main/java/com/extendedae_plus/mixin/ae2/helpers/patternprovider/PatternProviderLogicTickerMixin.java
create mode 100644 src/main/java/com/extendedae_plus/mixin/ae2/helpers/patternprovider/PatternProviderLogicUpgradesMixin.java
create mode 100644 src/main/java/com/extendedae_plus/mixin/ae2/menu/PatternProviderMenuUpgradesMixin.java
create mode 100644 src/main/java/com/extendedae_plus/wireless/endpoint/GenericNodeEndpointImpl.java
diff --git a/src/main/java/com/extendedae_plus/bridge/IUpgradableMenu.java b/src/main/java/com/extendedae_plus/bridge/IUpgradableMenu.java
new file mode 100644
index 0000000..470ccd5
--- /dev/null
+++ b/src/main/java/com/extendedae_plus/bridge/IUpgradableMenu.java
@@ -0,0 +1,12 @@
+package com.extendedae_plus.bridge;
+
+import appeng.api.upgrades.IUpgradeInventory;
+import appeng.menu.ToolboxMenu;
+
+public interface IUpgradableMenu {
+ ToolboxMenu getToolbox();
+ IUpgradeInventory getUpgrades();
+ default boolean hasUpgrade(net.minecraft.world.level.ItemLike upgradeCard) {
+ return getUpgrades().isInstalled(upgradeCard);
+ }
+}
diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/client/gui/PatternProviderScreenUpgradesMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/client/gui/PatternProviderScreenUpgradesMixin.java
new file mode 100644
index 0000000..5e94289
--- /dev/null
+++ b/src/main/java/com/extendedae_plus/mixin/ae2/client/gui/PatternProviderScreenUpgradesMixin.java
@@ -0,0 +1,48 @@
+package com.extendedae_plus.mixin.ae2.client.gui;
+
+import appeng.api.upgrades.Upgrades;
+import appeng.client.gui.AEBaseScreen;
+import appeng.client.gui.implementations.PatternProviderScreen;
+import appeng.client.gui.style.ScreenStyle;
+import appeng.client.gui.widgets.ToolboxPanel;
+import appeng.client.gui.widgets.UpgradesPanel;
+import appeng.core.localization.GuiText;
+import appeng.menu.SlotSemantics;
+import appeng.menu.implementations.PatternProviderMenu;
+import com.extendedae_plus.bridge.IUpgradableMenu;
+import net.minecraft.network.chat.Component;
+import net.minecraft.world.entity.player.Inventory;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.Unique;
+import org.spongepowered.asm.mixin.injection.At;
+import org.spongepowered.asm.mixin.injection.Inject;
+import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@Mixin(value = PatternProviderScreen.class, remap = false)
+public abstract class PatternProviderScreenUpgradesMixin extends AEBaseScreen {
+
+ @Inject(method = "", at = @At("TAIL"))
+ private void extendedae_plus$initUpgrades(PatternProviderMenu menu, Inventory playerInventory, Component title, ScreenStyle style, CallbackInfo ci) {
+ this.widgets.add("upgrades", new UpgradesPanel(
+ menu.getSlots(SlotSemantics.UPGRADE),
+ this::extendedae_plus$getCompatibleUpgrades));
+ if (((IUpgradableMenu) menu).getToolbox().isPresent()) {
+ this.widgets.add("toolbox", new ToolboxPanel(style, ((IUpgradableMenu) menu).getToolbox().getName()));
+ }
+ }
+
+ @Unique
+ private List extendedae_plus$getCompatibleUpgrades() {
+ var list = new ArrayList();
+ list.add(GuiText.CompatibleUpgrades.text());
+ list.addAll(Upgrades.getTooltipLinesForMachine(((IUpgradableMenu) menu).getUpgrades().getUpgradableItem()));
+ return list;
+ }
+
+ public PatternProviderScreenUpgradesMixin(C menu, Inventory playerInventory, Component title, ScreenStyle style) {
+ super(menu, playerInventory, title, style);
+ }
+}
diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/helpers/patternprovider/PatternProviderLogicHostUpgradesMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/helpers/patternprovider/PatternProviderLogicHostUpgradesMixin.java
new file mode 100644
index 0000000..9254e5d
--- /dev/null
+++ b/src/main/java/com/extendedae_plus/mixin/ae2/helpers/patternprovider/PatternProviderLogicHostUpgradesMixin.java
@@ -0,0 +1,21 @@
+package com.extendedae_plus.mixin.ae2.helpers.patternprovider;
+
+import appeng.api.upgrades.IUpgradeInventory;
+import appeng.api.upgrades.IUpgradeableObject;
+import appeng.helpers.patternprovider.PatternProviderLogic;
+import appeng.helpers.patternprovider.PatternProviderLogicHost;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.Shadow;
+
+/**
+ * 让 PatternProviderLogicHost 作为 IUpgradeableObject 的代理,菜单可从 host 获取升级槽。
+ */
+@Mixin(value = PatternProviderLogicHost.class, remap = false)
+public interface PatternProviderLogicHostUpgradesMixin extends IUpgradeableObject {
+ @Shadow PatternProviderLogic getLogic();
+
+ @Override
+ default IUpgradeInventory getUpgrades() {
+ return ((IUpgradeableObject) this.getLogic()).getUpgrades();
+ }
+}
diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/helpers/patternprovider/PatternProviderLogicTickerMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/helpers/patternprovider/PatternProviderLogicTickerMixin.java
new file mode 100644
index 0000000..8baebff
--- /dev/null
+++ b/src/main/java/com/extendedae_plus/mixin/ae2/helpers/patternprovider/PatternProviderLogicTickerMixin.java
@@ -0,0 +1,30 @@
+package com.extendedae_plus.mixin.ae2.helpers.patternprovider;
+
+import appeng.helpers.patternprovider.PatternProviderLogic;
+import com.extendedae_plus.bridge.InterfaceWirelessLinkBridge;
+import org.spongepowered.asm.mixin.Final;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.Shadow;
+import org.spongepowered.asm.mixin.injection.At;
+import org.spongepowered.asm.mixin.injection.Inject;
+import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
+
+/**
+ * 注入到 PatternProviderLogic.Ticker 的每tick回调,驱动无线链接状态更新。
+ */
+@Mixin(targets = "appeng.helpers.patternprovider.PatternProviderLogic$Ticker", remap = false)
+public abstract class PatternProviderLogicTickerMixin {
+
+ // Mixin 访问内部类的外部引用字段(javac 生成名 this$0)
+ @Shadow(remap = false)
+ @Final
+ private PatternProviderLogic this$0;
+
+ @Inject(method = "tickingRequest", at = @At("TAIL"))
+ private void extendedae_plus$tickTail(appeng.api.networking.IGridNode node, int ticksSinceLastCall,
+ CallbackInfoReturnable cir) {
+ if (this$0 instanceof InterfaceWirelessLinkBridge bridge) {
+ bridge.extendedae_plus$updateWirelessLink();
+ }
+ }
+}
diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/helpers/patternprovider/PatternProviderLogicUpgradesMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/helpers/patternprovider/PatternProviderLogicUpgradesMixin.java
new file mode 100644
index 0000000..82e5bb0
--- /dev/null
+++ b/src/main/java/com/extendedae_plus/mixin/ae2/helpers/patternprovider/PatternProviderLogicUpgradesMixin.java
@@ -0,0 +1,112 @@
+package com.extendedae_plus.mixin.ae2.helpers.patternprovider;
+
+import appeng.api.networking.IManagedGridNode;
+import appeng.api.networking.security.IActionSource;
+import appeng.api.upgrades.IUpgradeInventory;
+import appeng.api.upgrades.IUpgradeableObject;
+import appeng.api.upgrades.UpgradeInventories;
+import appeng.helpers.patternprovider.PatternProviderLogic;
+import appeng.helpers.patternprovider.PatternProviderLogicHost;
+import com.extendedae_plus.ae.items.ChannelCardItem;
+import com.extendedae_plus.init.ModItems;
+import com.extendedae_plus.wireless.WirelessSlaveLink;
+import com.extendedae_plus.wireless.endpoint.GenericNodeEndpointImpl;
+import com.extendedae_plus.bridge.InterfaceWirelessLinkBridge;
+import org.spongepowered.asm.mixin.Final;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.Shadow;
+import org.spongepowered.asm.mixin.Unique;
+import org.spongepowered.asm.mixin.injection.At;
+import org.spongepowered.asm.mixin.injection.Inject;
+import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
+
+import net.minecraft.nbt.CompoundTag;
+import net.minecraft.world.item.ItemStack;
+
+import java.util.List;
+
+/**
+ * 为 PatternProviderLogic 注入升级槽,实现 IUpgradeableObject。
+ * 仅负责升级槽的持久化/掉落/清空与初始化,不改变原有逻辑。
+ */
+@Mixin(value = PatternProviderLogic.class, remap = false)
+public abstract class PatternProviderLogicUpgradesMixin implements IUpgradeableObject, InterfaceWirelessLinkBridge {
+ @Unique
+ private IUpgradeInventory extendedae_plus$upgrades = UpgradeInventories.empty();
+
+ @Unique
+ private WirelessSlaveLink extendedae_plus$link;
+
+ @Final
+ @Shadow
+ private PatternProviderLogicHost host;
+
+ @Final
+ @Shadow
+ private IManagedGridNode mainNode;
+
+ @Final
+ @Shadow
+ private IActionSource actionSource;
+
+ @Unique
+ private void extendedae_plus$onUpgradesChanged() {
+ this.host.saveChanges();
+ // 读取频道卡,更新无线链接频率
+ long channel = 0L;
+ for (var stack : this.extendedae_plus$upgrades) {
+ if (!stack.isEmpty() && stack.getItem() == ModItems.CHANNEL_CARD.get()) {
+ channel = ChannelCardItem.getChannel(stack);
+ break;
+ }
+ }
+ if (extendedae_plus$link == null) {
+ var endpoint = new GenericNodeEndpointImpl(() -> host.getBlockEntity(), () -> this.mainNode.getNode());
+ extendedae_plus$link = new WirelessSlaveLink(endpoint);
+ }
+ extendedae_plus$link.setFrequency(channel);
+ extendedae_plus$link.updateStatus();
+ }
+
+ @Override
+ public IUpgradeInventory getUpgrades() {
+ return this.extendedae_plus$upgrades;
+ }
+
+ @Inject(method = "(Lappeng/api/networking/IManagedGridNode;Lappeng/helpers/patternprovider/PatternProviderLogicHost;I)V",
+ at = @At("TAIL"))
+ private void extendedae_plus$initUpgrades(IManagedGridNode mainNode, PatternProviderLogicHost host, int patternInventorySize, CallbackInfo ci) {
+ this.extendedae_plus$upgrades = UpgradeInventories.forMachine(host.getTerminalIcon().getItem(), 1, this::extendedae_plus$onUpgradesChanged);
+ }
+
+ @Inject(method = "writeToNBT", at = @At("TAIL"))
+ private void extendedae_plus$saveUpgrades(CompoundTag tag, CallbackInfo ci) {
+ this.extendedae_plus$upgrades.writeToNBT(tag, "upgrades");
+ }
+
+ @Inject(method = "readFromNBT", at = @At("TAIL"))
+ private void extendedae_plus$loadUpgrades(CompoundTag tag, CallbackInfo ci) {
+ this.extendedae_plus$upgrades.readFromNBT(tag, "upgrades");
+ }
+
+ @Inject(method = "addDrops", at = @At("TAIL"))
+ private void extendedae_plus$dropUpgrades(List drops, CallbackInfo ci) {
+ for (var stack : this.extendedae_plus$upgrades) {
+ if (!stack.isEmpty()) {
+ drops.add(stack);
+ }
+ }
+ }
+
+ @Inject(method = "clearContent", at = @At("TAIL"))
+ private void extendedae_plus$clearUpgrades(CallbackInfo ci) {
+ this.extendedae_plus$upgrades.clear();
+ }
+
+ @Override
+ public void extendedae_plus$updateWirelessLink() {
+ if (extendedae_plus$link != null) {
+ extendedae_plus$link.updateStatus();
+ }
+ }
+}
diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/menu/PatternProviderMenuUpgradesMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/menu/PatternProviderMenuUpgradesMixin.java
new file mode 100644
index 0000000..514b523
--- /dev/null
+++ b/src/main/java/com/extendedae_plus/mixin/ae2/menu/PatternProviderMenuUpgradesMixin.java
@@ -0,0 +1,49 @@
+package com.extendedae_plus.mixin.ae2.menu;
+
+import appeng.api.upgrades.IUpgradeInventory;
+import appeng.api.upgrades.IUpgradeableObject;
+import appeng.helpers.patternprovider.PatternProviderLogic;
+import appeng.helpers.patternprovider.PatternProviderLogicHost;
+import appeng.menu.AEBaseMenu;
+import appeng.menu.ToolboxMenu;
+import appeng.menu.implementations.PatternProviderMenu;
+import com.extendedae_plus.bridge.IUpgradableMenu;
+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;
+import org.spongepowered.asm.mixin.injection.At;
+import org.spongepowered.asm.mixin.injection.Inject;
+import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
+
+@Mixin(value = PatternProviderMenu.class, remap = false)
+public abstract class PatternProviderMenuUpgradesMixin extends AEBaseMenu implements IUpgradableMenu {
+ @Final
+ @Shadow protected PatternProviderLogic logic;
+
+ @Unique
+ private ToolboxMenu extendedae_plus$toolbox;
+
+ @Inject(method = "(Lnet/minecraft/world/inventory/MenuType;ILnet/minecraft/world/entity/player/Inventory;Lappeng/helpers/patternprovider/PatternProviderLogicHost;)V",
+ at = @At("TAIL"))
+ private void extendedae_plus$initUpgrades(MenuType> menuType, int id, Inventory playerInventory, PatternProviderLogicHost host, CallbackInfo ci) {
+ this.extendedae_plus$toolbox = new ToolboxMenu(this);
+ this.setupUpgrades(((IUpgradeableObject) host).getUpgrades());
+ }
+
+ @Override
+ public ToolboxMenu getToolbox() {
+ return this.extendedae_plus$toolbox;
+ }
+
+ @Override
+ public IUpgradeInventory getUpgrades() {
+ return ((IUpgradeableObject) this.logic).getUpgrades();
+ }
+
+ public PatternProviderMenuUpgradesMixin(MenuType> menuType, int id, Inventory playerInventory, Object host) {
+ super(menuType, id, playerInventory, host);
+ }
+}
diff --git a/src/main/java/com/extendedae_plus/wireless/endpoint/GenericNodeEndpointImpl.java b/src/main/java/com/extendedae_plus/wireless/endpoint/GenericNodeEndpointImpl.java
new file mode 100644
index 0000000..84cd191
--- /dev/null
+++ b/src/main/java/com/extendedae_plus/wireless/endpoint/GenericNodeEndpointImpl.java
@@ -0,0 +1,49 @@
+package com.extendedae_plus.wireless.endpoint;
+
+import appeng.api.networking.IGridNode;
+import com.extendedae_plus.wireless.IWirelessEndpoint;
+import net.minecraft.core.BlockPos;
+import net.minecraft.server.level.ServerLevel;
+import net.minecraft.world.level.Level;
+import net.minecraft.world.level.block.entity.BlockEntity;
+
+import java.util.Objects;
+import java.util.function.Supplier;
+
+/**
+ * 通用 IWirelessEndpoint:通过提供方块实体与节点的 Supplier 实现。
+ */
+public class GenericNodeEndpointImpl implements IWirelessEndpoint {
+ private final Supplier blockEntitySupplier;
+ private final Supplier nodeSupplier;
+
+ public GenericNodeEndpointImpl(Supplier blockEntitySupplier, Supplier nodeSupplier) {
+ this.blockEntitySupplier = Objects.requireNonNull(blockEntitySupplier);
+ this.nodeSupplier = Objects.requireNonNull(nodeSupplier);
+ }
+
+ @Override
+ public ServerLevel getServerLevel() {
+ var be = blockEntitySupplier.get();
+ if (be == null) return null;
+ Level lvl = be.getLevel();
+ return (lvl instanceof ServerLevel) ? (ServerLevel) lvl : null;
+ }
+
+ @Override
+ public BlockPos getBlockPos() {
+ var be = blockEntitySupplier.get();
+ return be != null ? be.getBlockPos() : BlockPos.ZERO;
+ }
+
+ @Override
+ public IGridNode getGridNode() {
+ return nodeSupplier.get();
+ }
+
+ @Override
+ public boolean isEndpointRemoved() {
+ var be = blockEntitySupplier.get();
+ return be == null || be.isRemoved();
+ }
+}
diff --git a/src/main/resources/extendedae_plus.mixins.json b/src/main/resources/extendedae_plus.mixins.json
index a856a94..b584236 100644
--- a/src/main/resources/extendedae_plus.mixins.json
+++ b/src/main/resources/extendedae_plus.mixins.json
@@ -20,6 +20,7 @@
"ae2.client.gui.PatternEncodingTermScreenMixin",
"ae2.client.gui.PatternProviderCloseMixin",
"ae2.client.gui.PatternProviderScreenMixin",
+ "ae2.client.gui.PatternProviderScreenUpgradesMixin",
"ae2.client.gui.SlotGridLayoutMixin",
"ae2.menu.CraftConfirmMenuGoBackMixin",
"extendedae.accessor.GuiExPatternTerminalAccessor",
@@ -62,6 +63,10 @@
"ae2.menu.PatternEncodingTermMenuMixin",
"ae2.menu.PatternProviderMenuAdvancedMixin",
"ae2.menu.PatternProviderMenuDoublingMixin",
+ "ae2.helpers.patternprovider.PatternProviderLogicUpgradesMixin",
+ "ae2.helpers.patternprovider.PatternProviderLogicHostUpgradesMixin",
+ "ae2.menu.PatternProviderMenuUpgradesMixin",
+ "ae2.helpers.patternprovider.PatternProviderLogicTickerMixin",
"ae2WTlib.ContainerUWirelessExPatternTerminalMixin",
"extendedae.common.PartExPatternProviderMixin",
"extendedae.common.TileExPatternProviderMixin",
From 1589f179cdc6d39f2d4f72ddf1810179a380ca4c Mon Sep 17 00:00:00 2001
From: GaLicn <133291877+GaLicn@users.noreply.github.com>
Date: Mon, 22 Sep 2025 19:01:13 +0800
Subject: [PATCH 15/34] =?UTF-8?q?=E6=94=AF=E6=8C=81=E6=9B=B4=E5=A4=9A?=
=?UTF-8?q?=E6=96=B9=E5=9D=97=E6=8F=92=E5=85=A5=E9=A2=91=E9=81=93=E5=8D=A1?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../extendedae_plus/init/UpgradeCards.java | 33 ++++++++++
.../automation/IOBusPartChannelCardMixin.java | 65 +++++++++++++++++++
.../IOBusPartTickerChannelCardMixin.java | 24 +++++++
.../StorageBusPartChannelCardMixin.java | 63 ++++++++++++++++++
.../StorageBusPartTickerChannelCardMixin.java | 24 +++++++
.../resources/extendedae_plus.mixins.json | 4 ++
6 files changed, 213 insertions(+)
create mode 100644 src/main/java/com/extendedae_plus/mixin/ae2/parts/automation/IOBusPartChannelCardMixin.java
create mode 100644 src/main/java/com/extendedae_plus/mixin/ae2/parts/automation/IOBusPartTickerChannelCardMixin.java
create mode 100644 src/main/java/com/extendedae_plus/mixin/ae2/parts/storagebus/StorageBusPartChannelCardMixin.java
create mode 100644 src/main/java/com/extendedae_plus/mixin/ae2/parts/storagebus/StorageBusPartTickerChannelCardMixin.java
diff --git a/src/main/java/com/extendedae_plus/init/UpgradeCards.java b/src/main/java/com/extendedae_plus/init/UpgradeCards.java
index 05dec0d..d3a0089 100644
--- a/src/main/java/com/extendedae_plus/init/UpgradeCards.java
+++ b/src/main/java/com/extendedae_plus/init/UpgradeCards.java
@@ -6,6 +6,14 @@ import appeng.core.definitions.AEItems;
import appeng.core.definitions.AEParts;
import appeng.core.localization.GuiText;
import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent;
+import static com.glodblock.github.extendedae.common.EPPItemAndBlock.EX_PATTERN_PROVIDER;
+import static com.glodblock.github.extendedae.common.EPPItemAndBlock.EX_PATTERN_PROVIDER_PART;
+import static com.glodblock.github.extendedae.common.EPPItemAndBlock.EX_INTERFACE;
+import static com.glodblock.github.extendedae.common.EPPItemAndBlock.EX_INTERFACE_PART;
+import static com.glodblock.github.extendedae.common.EPPItemAndBlock.OVERSIZE_INTERFACE;
+import static com.glodblock.github.extendedae.common.EPPItemAndBlock.OVERSIZE_INTERFACE_PART;
+import static com.glodblock.github.extendedae.common.EPPItemAndBlock.EX_IMPORT_BUS;
+import static com.glodblock.github.extendedae.common.EPPItemAndBlock.EX_EXPORT_BUS;
/**
*
@@ -22,6 +30,31 @@ public class UpgradeCards {
String interfaceGroup = GuiText.Interface.getTranslationKey();
Upgrades.add(ModItems.CHANNEL_CARD.get(), AEBlocks.INTERFACE, 1, interfaceGroup);
Upgrades.add(ModItems.CHANNEL_CARD.get(), AEParts.INTERFACE, 1, interfaceGroup);
+
+ // 新增:样板供应器(方块与部件)支持频道卡,每台最多 1 张
+ String patternProviderGroup = "group.pattern_provider.name";
+ Upgrades.add(ModItems.CHANNEL_CARD.get(), AEBlocks.PATTERN_PROVIDER, 1, patternProviderGroup);
+ Upgrades.add(ModItems.CHANNEL_CARD.get(), AEParts.PATTERN_PROVIDER, 1, patternProviderGroup);
+
+ // ExtendedAE 的扩展样板供应器(方块与部件)
+ Upgrades.add(ModItems.CHANNEL_CARD.get(),EX_PATTERN_PROVIDER, 1, patternProviderGroup);
+ Upgrades.add(ModItems.CHANNEL_CARD.get(),EX_PATTERN_PROVIDER_PART, 1, patternProviderGroup);
+
+ //EAE 的扩展接口与超大接口(方块与部件)支持频道卡
+ Upgrades.add(ModItems.CHANNEL_CARD.get(), EX_INTERFACE, 1, interfaceGroup);
+ Upgrades.add(ModItems.CHANNEL_CARD.get(), EX_INTERFACE_PART, 1, interfaceGroup);
+ Upgrades.add(ModItems.CHANNEL_CARD.get(), OVERSIZE_INTERFACE, 1, interfaceGroup);
+ Upgrades.add(ModItems.CHANNEL_CARD.get(), OVERSIZE_INTERFACE_PART, 1, interfaceGroup);
+
+ //AE2 的输入/输出/存储总线支持频道卡(部件)
+ String ioBusGroup = GuiText.IOBuses.getTranslationKey();
+ Upgrades.add(ModItems.CHANNEL_CARD.get(), AEParts.IMPORT_BUS, 1, ioBusGroup);
+ Upgrades.add(ModItems.CHANNEL_CARD.get(), AEParts.EXPORT_BUS, 1, ioBusGroup);
+ Upgrades.add(ModItems.CHANNEL_CARD.get(), AEParts.STORAGE_BUS, 1, ioBusGroup);
+
+ //EAE 的扩展输入/输出总线支持频道卡(部件)
+ Upgrades.add(ModItems.CHANNEL_CARD.get(), EX_IMPORT_BUS, 1, ioBusGroup);
+ Upgrades.add(ModItems.CHANNEL_CARD.get(), EX_EXPORT_BUS, 1, ioBusGroup);
});
}
}
\ No newline at end of file
diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/parts/automation/IOBusPartChannelCardMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/parts/automation/IOBusPartChannelCardMixin.java
new file mode 100644
index 0000000..c556c8f
--- /dev/null
+++ b/src/main/java/com/extendedae_plus/mixin/ae2/parts/automation/IOBusPartChannelCardMixin.java
@@ -0,0 +1,65 @@
+package com.extendedae_plus.mixin.ae2.parts.automation;
+
+import appeng.api.networking.security.IActionHost;
+import appeng.api.upgrades.IUpgradeInventory;
+import appeng.api.upgrades.IUpgradeableObject;
+import appeng.helpers.InterfaceLogicHost;
+import appeng.parts.automation.IOBusPart;
+import com.extendedae_plus.ae.items.ChannelCardItem;
+import com.extendedae_plus.bridge.InterfaceWirelessLinkBridge;
+import com.extendedae_plus.init.ModItems;
+import com.extendedae_plus.wireless.WirelessSlaveLink;
+import com.extendedae_plus.wireless.endpoint.GenericNodeEndpointImpl;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.Unique;
+import org.spongepowered.asm.mixin.injection.At;
+import org.spongepowered.asm.mixin.injection.Inject;
+import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
+
+/**
+ * 给 AE2 的 I/O 总线注入频道卡联动:在升级变更时读取频道并更新无线链接。
+ */
+@Mixin(value = IOBusPart.class, remap = false)
+public abstract class IOBusPartChannelCardMixin implements InterfaceWirelessLinkBridge, IUpgradeableObject {
+
+ @Unique
+ private WirelessSlaveLink extendedae_plus$link;
+
+ @Inject(method = "upgradesChanged", at = @At("TAIL"))
+ private void extendedae_plus$onUpgradesChanged(CallbackInfo ci) {
+ IUpgradeInventory inv = this.getUpgrades();
+ long channel = 0L;
+ boolean found = false;
+ for (var stack : inv) {
+ if (!stack.isEmpty() && stack.getItem() == ModItems.CHANNEL_CARD.get()) {
+ channel = ChannelCardItem.getChannel(stack);
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ // 无频道卡则断开
+ if (extendedae_plus$link != null) {
+ extendedae_plus$link.setFrequency(0L);
+ extendedae_plus$link.updateStatus();
+ }
+ return;
+ }
+ if (extendedae_plus$link == null) {
+ var endpoint = new GenericNodeEndpointImpl(
+ () -> ((appeng.parts.AEBasePart)(Object)this).getHost().getBlockEntity(),
+ () -> ((IActionHost)(Object)this).getActionableNode()
+ );
+ extendedae_plus$link = new WirelessSlaveLink(endpoint);
+ }
+ extendedae_plus$link.setFrequency(channel);
+ extendedae_plus$link.updateStatus();
+ }
+
+ @Override
+ public void extendedae_plus$updateWirelessLink() {
+ if (extendedae_plus$link != null) {
+ extendedae_plus$link.updateStatus();
+ }
+ }
+}
diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/parts/automation/IOBusPartTickerChannelCardMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/parts/automation/IOBusPartTickerChannelCardMixin.java
new file mode 100644
index 0000000..c5bd28f
--- /dev/null
+++ b/src/main/java/com/extendedae_plus/mixin/ae2/parts/automation/IOBusPartTickerChannelCardMixin.java
@@ -0,0 +1,24 @@
+package com.extendedae_plus.mixin.ae2.parts.automation;
+
+import appeng.api.networking.IGridNode;
+import appeng.api.networking.ticking.TickRateModulation;
+import appeng.parts.automation.IOBusPart;
+import com.extendedae_plus.bridge.InterfaceWirelessLinkBridge;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.injection.At;
+import org.spongepowered.asm.mixin.injection.Inject;
+import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
+
+/**
+ * 在 I/O 总线的 tickingRequest 尾部驱动无线链接刷新。
+ */
+@Mixin(value = IOBusPart.class, remap = false)
+public abstract class IOBusPartTickerChannelCardMixin {
+
+ @Inject(method = "tickingRequest", at = @At("TAIL"))
+ private void extendedae_plus$tickTail(IGridNode node, int ticksSinceLastCall, CallbackInfoReturnable cir) {
+ if (this instanceof InterfaceWirelessLinkBridge bridge) {
+ bridge.extendedae_plus$updateWirelessLink();
+ }
+ }
+}
diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/parts/storagebus/StorageBusPartChannelCardMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/parts/storagebus/StorageBusPartChannelCardMixin.java
new file mode 100644
index 0000000..2abb221
--- /dev/null
+++ b/src/main/java/com/extendedae_plus/mixin/ae2/parts/storagebus/StorageBusPartChannelCardMixin.java
@@ -0,0 +1,63 @@
+package com.extendedae_plus.mixin.ae2.parts.storagebus;
+
+import appeng.api.networking.security.IActionHost;
+import appeng.api.upgrades.IUpgradeInventory;
+import appeng.api.upgrades.IUpgradeableObject;
+import appeng.parts.storagebus.StorageBusPart;
+import com.extendedae_plus.ae.items.ChannelCardItem;
+import com.extendedae_plus.bridge.InterfaceWirelessLinkBridge;
+import com.extendedae_plus.init.ModItems;
+import com.extendedae_plus.wireless.WirelessSlaveLink;
+import com.extendedae_plus.wireless.endpoint.GenericNodeEndpointImpl;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.Unique;
+import org.spongepowered.asm.mixin.injection.At;
+import org.spongepowered.asm.mixin.injection.Inject;
+import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
+
+/**
+ * 给 AE2 的存储总线注入频道卡联动:在升级变更时读取频道并更新无线链接。
+ */
+@Mixin(value = StorageBusPart.class, remap = false)
+public abstract class StorageBusPartChannelCardMixin implements InterfaceWirelessLinkBridge, IUpgradeableObject {
+
+ @Unique
+ private WirelessSlaveLink extendedae_plus$link;
+
+ @Inject(method = "upgradesChanged", at = @At("TAIL"))
+ private void extendedae_plus$onUpgradesChanged(CallbackInfo ci) {
+ IUpgradeInventory inv = this.getUpgrades();
+ long channel = 0L;
+ boolean found = false;
+ for (var stack : inv) {
+ if (!stack.isEmpty() && stack.getItem() == ModItems.CHANNEL_CARD.get()) {
+ channel = ChannelCardItem.getChannel(stack);
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ if (extendedae_plus$link != null) {
+ extendedae_plus$link.setFrequency(0L);
+ extendedae_plus$link.updateStatus();
+ }
+ return;
+ }
+ if (extendedae_plus$link == null) {
+ var endpoint = new GenericNodeEndpointImpl(
+ () -> ((appeng.parts.AEBasePart)(Object)this).getHost().getBlockEntity(),
+ () -> ((IActionHost)(Object)this).getActionableNode()
+ );
+ extendedae_plus$link = new WirelessSlaveLink(endpoint);
+ }
+ extendedae_plus$link.setFrequency(channel);
+ extendedae_plus$link.updateStatus();
+ }
+
+ @Override
+ public void extendedae_plus$updateWirelessLink() {
+ if (extendedae_plus$link != null) {
+ extendedae_plus$link.updateStatus();
+ }
+ }
+}
diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/parts/storagebus/StorageBusPartTickerChannelCardMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/parts/storagebus/StorageBusPartTickerChannelCardMixin.java
new file mode 100644
index 0000000..636b4e4
--- /dev/null
+++ b/src/main/java/com/extendedae_plus/mixin/ae2/parts/storagebus/StorageBusPartTickerChannelCardMixin.java
@@ -0,0 +1,24 @@
+package com.extendedae_plus.mixin.ae2.parts.storagebus;
+
+import appeng.api.networking.IGridNode;
+import appeng.api.networking.ticking.TickRateModulation;
+import appeng.parts.storagebus.StorageBusPart;
+import com.extendedae_plus.bridge.InterfaceWirelessLinkBridge;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.injection.At;
+import org.spongepowered.asm.mixin.injection.Inject;
+import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
+
+/**
+ * 在存储总线的 tickingRequest 尾部驱动无线链接刷新。
+ */
+@Mixin(value = StorageBusPart.class, remap = false)
+public abstract class StorageBusPartTickerChannelCardMixin {
+
+ @Inject(method = "tickingRequest", at = @At("TAIL"))
+ private void extendedae_plus$tickTail(IGridNode node, int ticksSinceLastCall, CallbackInfoReturnable cir) {
+ if (this instanceof InterfaceWirelessLinkBridge bridge) {
+ bridge.extendedae_plus$updateWirelessLink();
+ }
+ }
+}
diff --git a/src/main/resources/extendedae_plus.mixins.json b/src/main/resources/extendedae_plus.mixins.json
index b584236..48e0c2e 100644
--- a/src/main/resources/extendedae_plus.mixins.json
+++ b/src/main/resources/extendedae_plus.mixins.json
@@ -67,6 +67,10 @@
"ae2.helpers.patternprovider.PatternProviderLogicHostUpgradesMixin",
"ae2.menu.PatternProviderMenuUpgradesMixin",
"ae2.helpers.patternprovider.PatternProviderLogicTickerMixin",
+ "ae2.parts.automation.IOBusPartChannelCardMixin",
+ "ae2.parts.automation.IOBusPartTickerChannelCardMixin",
+ "ae2.parts.storagebus.StorageBusPartChannelCardMixin",
+ "ae2.parts.storagebus.StorageBusPartTickerChannelCardMixin",
"ae2WTlib.ContainerUWirelessExPatternTerminalMixin",
"extendedae.common.PartExPatternProviderMixin",
"extendedae.common.TileExPatternProviderMixin",
From 4745be106344e49fcd4f32053cd2fbf1a08e737c Mon Sep 17 00:00:00 2001
From: GaLicn <133291877+GaLicn@users.noreply.github.com>
Date: Mon, 22 Sep 2025 19:21:55 +0800
Subject: [PATCH 16/34] =?UTF-8?q?=E6=94=AF=E6=8C=81=E6=9B=B4=E5=A4=9A?=
=?UTF-8?q?=E6=96=B9=E5=9D=97=E6=8F=92=E5=85=A5=E9=A2=91=E9=81=93=E5=8D=A1?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/main/java/com/extendedae_plus/init/UpgradeCards.java | 3 ++-
src/main/resources/assets/extendedae_plus/lang/en_us.json | 3 ++-
src/main/resources/assets/extendedae_plus/lang/zh_cn.json | 5 ++++-
3 files changed, 8 insertions(+), 3 deletions(-)
diff --git a/src/main/java/com/extendedae_plus/init/UpgradeCards.java b/src/main/java/com/extendedae_plus/init/UpgradeCards.java
index d3a0089..c7b9c33 100644
--- a/src/main/java/com/extendedae_plus/init/UpgradeCards.java
+++ b/src/main/java/com/extendedae_plus/init/UpgradeCards.java
@@ -48,9 +48,10 @@ public class UpgradeCards {
//AE2 的输入/输出/存储总线支持频道卡(部件)
String ioBusGroup = GuiText.IOBuses.getTranslationKey();
+ String storageGroup = "group.storage.name";
Upgrades.add(ModItems.CHANNEL_CARD.get(), AEParts.IMPORT_BUS, 1, ioBusGroup);
Upgrades.add(ModItems.CHANNEL_CARD.get(), AEParts.EXPORT_BUS, 1, ioBusGroup);
- Upgrades.add(ModItems.CHANNEL_CARD.get(), AEParts.STORAGE_BUS, 1, ioBusGroup);
+ Upgrades.add(ModItems.CHANNEL_CARD.get(), AEParts.STORAGE_BUS, 1, storageGroup);
//EAE 的扩展输入/输出总线支持频道卡(部件)
Upgrades.add(ModItems.CHANNEL_CARD.get(), EX_IMPORT_BUS, 1, ioBusGroup);
diff --git a/src/main/resources/assets/extendedae_plus/lang/en_us.json b/src/main/resources/assets/extendedae_plus/lang/en_us.json
index ef955fa..07156e3 100644
--- a/src/main/resources/assets/extendedae_plus/lang/en_us.json
+++ b/src/main/resources/assets/extendedae_plus/lang/en_us.json
@@ -92,5 +92,6 @@
"item.extendedae_plus.channel_card": "Channel Card",
"item.extendedae_plus.channel_card.channel": "Frequency: %s",
"item.extendedae_plus.channel_card.channel.unset": "Frequency: Unset",
- "item.extendedae_plus.channel_card.set": "Frequency set to: %s"
+ "item.extendedae_plus.channel_card.set": "Frequency set to: %s",
+ "group.storage.name": "StorageBus"
}
\ No newline at end of file
diff --git a/src/main/resources/assets/extendedae_plus/lang/zh_cn.json b/src/main/resources/assets/extendedae_plus/lang/zh_cn.json
index 210532c..a245edb 100644
--- a/src/main/resources/assets/extendedae_plus/lang/zh_cn.json
+++ b/src/main/resources/assets/extendedae_plus/lang/zh_cn.json
@@ -97,5 +97,8 @@
"item.extendedae_plus.channel_card": "频道卡",
"item.extendedae_plus.channel_card.channel": "频率:%s",
"item.extendedae_plus.channel_card.channel.unset": "频率:未设置",
- "item.extendedae_plus.channel_card.set": "已设置频率:%s"
+ "item.extendedae_plus.channel_card.set": "已设置频率:%s",
+ "group.pattern_provider.name": "样板供应器",
+ "group.entity_ticker.name": "实体加速器",
+ "group.storage.name": "存储总线"
}
\ No newline at end of file
From fd075c76f47c4ca38baf3084e95a4cc222fc85f7 Mon Sep 17 00:00:00 2001
From: GaLicn <133291877+GaLicn@users.noreply.github.com>
Date: Tue, 23 Sep 2025 10:40:02 +0800
Subject: [PATCH 17/34] =?UTF-8?q?=E4=BF=AE=E5=A4=8Diobus=E5=88=9D=E5=A7=8B?=
=?UTF-8?q?=E5=8C=96?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../bridge/InterfaceWirelessLinkBridge.java | 14 ++
.../mixin/ae2/CraftingCPUClusterMixin.java | 62 ++++----
.../ae2/parts/AEBasePartClientSyncMixin.java | 68 +++++++++
.../automation/IOBusPartChannelCardMixin.java | 132 +++++++++++++++---
.../StorageBusPartChannelCardMixin.java | 128 ++++++++++++++---
.../resources/extendedae_plus.mixins.json | 1 +
6 files changed, 335 insertions(+), 70 deletions(-)
create mode 100644 src/main/java/com/extendedae_plus/mixin/ae2/parts/AEBasePartClientSyncMixin.java
diff --git a/src/main/java/com/extendedae_plus/bridge/InterfaceWirelessLinkBridge.java b/src/main/java/com/extendedae_plus/bridge/InterfaceWirelessLinkBridge.java
index 097b091..11b9013 100644
--- a/src/main/java/com/extendedae_plus/bridge/InterfaceWirelessLinkBridge.java
+++ b/src/main/java/com/extendedae_plus/bridge/InterfaceWirelessLinkBridge.java
@@ -5,4 +5,18 @@ package com.extendedae_plus.bridge;
*/
public interface InterfaceWirelessLinkBridge {
void extendedae_plus$updateWirelessLink();
+
+ /**
+ * 获取无线连接状态(服务端返回真实状态,客户端返回同步状态)
+ */
+ default boolean extendedae_plus$isWirelessConnected() {
+ return false;
+ }
+
+ /**
+ * 设置客户端的无线连接状态(仅在客户端使用)
+ */
+ default void extendedae_plus$setClientWirelessState(boolean connected) {
+ // 默认实现为空
+ }
}
diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/CraftingCPUClusterMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/CraftingCPUClusterMixin.java
index 8faf483..7c76d90 100644
--- a/src/main/java/com/extendedae_plus/mixin/ae2/CraftingCPUClusterMixin.java
+++ b/src/main/java/com/extendedae_plus/mixin/ae2/CraftingCPUClusterMixin.java
@@ -1,36 +1,48 @@
package com.extendedae_plus.mixin.ae2;
import appeng.blockentity.crafting.CraftingBlockEntity;
+import appeng.blockentity.crafting.CraftingMonitorBlockEntity;
import appeng.me.cluster.implementations.CraftingCPUCluster;
+import appeng.me.helpers.MachineSource;
import org.spongepowered.asm.mixin.Mixin;
-import org.spongepowered.asm.mixin.injection.At;
-import org.spongepowered.asm.mixin.injection.Constant;
-import org.spongepowered.asm.mixin.injection.ModifyConstant;
-import org.spongepowered.asm.mixin.injection.Redirect;
+import org.spongepowered.asm.mixin.Overwrite;
+import org.spongepowered.asm.mixin.Shadow;
+
+import java.util.List;
@Mixin(value = CraftingCPUCluster.class, remap = false, priority = 2000)
public abstract class CraftingCPUClusterMixin {
- // 1) 提升“单方块线程上限”的常量,避免抛出 IAE 的 IllegalArgumentException
- @ModifyConstant(
- method = "addBlockEntity(Lappeng/blockentity/crafting/CraftingBlockEntity;)V",
- constant = @Constant(intValue = 16)
- )
- private int extendedae_plus$raisePerUnitLimit(int original) {
- // 放宽到极大值,完全取消单方块 16 线程的硬限制
- return Integer.MAX_VALUE;
- }
+
+ @Shadow private List blockEntities;
+ @Shadow private List status;
+ @Shadow private MachineSource machineSrc;
+ @Shadow private long storage;
+ @Shadow private int accelerator;
+
+ /**
+ * 完全重写addBlockEntity方法,移除16线程的硬限制
+ * @author ExtendedAE_Plus
+ * @reason 移除单方块16线程的硬限制,允许更高的线程数
+ */
+ @Overwrite
+ void addBlockEntity(CraftingBlockEntity te) {
+ if (this.machineSrc == null || te.isCoreBlock()) {
+ this.machineSrc = new MachineSource(te);
+ }
- // 2) 保持统计使用原始线程值(若存在多处调用),不再返回固定 16
- @Redirect(
- method = "addBlockEntity(Lappeng/blockentity/crafting/CraftingBlockEntity;)V",
- at = @At(
- value = "INVOKE",
- target = "Lappeng/blockentity/crafting/CraftingBlockEntity;getAcceleratorThreads()I",
- ordinal = 1
- )
- )
- private int extendedae_plus$onGetThreadsForLimitCheck(CraftingBlockEntity te) {
- // 返回原始线程数,确保总并行单元不被错误下限
- return te.getAcceleratorThreads();
+ te.setCoreBlock(false);
+ te.saveChanges();
+ this.blockEntities.add(0, te);
+
+ if (te instanceof CraftingMonitorBlockEntity) {
+ this.status.add((CraftingMonitorBlockEntity) te);
+ }
+ if (te.getStorageBytes() > 0) {
+ this.storage += te.getStorageBytes();
+ }
+ if (te.getAcceleratorThreads() > 0) {
+ // 移除原来的16线程限制,直接添加线程数
+ this.accelerator += te.getAcceleratorThreads();
+ }
}
}
diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/parts/AEBasePartClientSyncMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/parts/AEBasePartClientSyncMixin.java
new file mode 100644
index 0000000..2ae0175
--- /dev/null
+++ b/src/main/java/com/extendedae_plus/mixin/ae2/parts/AEBasePartClientSyncMixin.java
@@ -0,0 +1,68 @@
+package com.extendedae_plus.mixin.ae2.parts;
+
+import appeng.parts.AEBasePart;
+import com.extendedae_plus.bridge.InterfaceWirelessLinkBridge;
+import net.minecraft.network.FriendlyByteBuf;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.injection.At;
+import org.spongepowered.asm.mixin.injection.Inject;
+import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
+import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
+
+/**
+ * 为所有AE2 Part添加无线连接状态的客户端同步功能
+ */
+@Mixin(value = AEBasePart.class, remap = false)
+public class AEBasePartClientSyncMixin {
+
+ @Inject(method = "writeToStream", at = @At("TAIL"))
+ private void extendedae_plus$writeWirelessState(FriendlyByteBuf data, CallbackInfo ci) {
+ // 检查是否实现了无线链接桥接接口
+ if (this instanceof InterfaceWirelessLinkBridge) {
+ InterfaceWirelessLinkBridge bridge = (InterfaceWirelessLinkBridge) this;
+ // 同步无线连接状态到客户端
+ boolean connected = false;
+ try {
+ // 只在服务端获取真实连接状态
+ AEBasePart part = (AEBasePart)(Object)this;
+ if (!part.isClientSide()) {
+ connected = bridge.extendedae_plus$isWirelessConnected();
+ // 调试日志:记录写入的状态
+ if (part.getClass().getSimpleName().contains("IOBus")) {
+ System.out.println("[写入状态] IOBus 无线连接状态: " + connected);
+ }
+ }
+ } catch (Exception e) {
+ // 忽略异常,默认为false
+ System.err.println("获取无线连接状态失败: " + e.getMessage());
+ }
+ data.writeBoolean(connected);
+ } else {
+ // 不是无线链接Part,写入false
+ data.writeBoolean(false);
+ }
+ }
+
+ @Inject(method = "readFromStream", at = @At("TAIL"))
+ private void extendedae_plus$readWirelessState(FriendlyByteBuf data, CallbackInfoReturnable cir) {
+ // 读取无线连接状态
+ boolean connected = data.readBoolean();
+
+ // 检查是否实现了无线链接桥接接口
+ if (this instanceof InterfaceWirelessLinkBridge) {
+ InterfaceWirelessLinkBridge bridge = (InterfaceWirelessLinkBridge) this;
+ try {
+ // 调试日志:记录读取的状态
+ AEBasePart part = (AEBasePart)(Object)this;
+ if (part.getClass().getSimpleName().contains("IOBus")) {
+ System.out.println("[读取状态] IOBus 无线连接状态: " + connected);
+ }
+ // 更新客户端状态
+ bridge.extendedae_plus$setClientWirelessState(connected);
+ } catch (Exception e) {
+ // 忽略异常
+ System.err.println("设置客户端无线连接状态失败: " + e.getMessage());
+ }
+ }
+ }
+}
diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/parts/automation/IOBusPartChannelCardMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/parts/automation/IOBusPartChannelCardMixin.java
index c556c8f..cf21f1d 100644
--- a/src/main/java/com/extendedae_plus/mixin/ae2/parts/automation/IOBusPartChannelCardMixin.java
+++ b/src/main/java/com/extendedae_plus/mixin/ae2/parts/automation/IOBusPartChannelCardMixin.java
@@ -5,11 +5,14 @@ import appeng.api.upgrades.IUpgradeInventory;
import appeng.api.upgrades.IUpgradeableObject;
import appeng.helpers.InterfaceLogicHost;
import appeng.parts.automation.IOBusPart;
+import net.minecraft.nbt.CompoundTag;
+import com.extendedae_plus.util.ExtendedAELogger;
import com.extendedae_plus.ae.items.ChannelCardItem;
import com.extendedae_plus.bridge.InterfaceWirelessLinkBridge;
import com.extendedae_plus.init.ModItems;
import com.extendedae_plus.wireless.WirelessSlaveLink;
import com.extendedae_plus.wireless.endpoint.GenericNodeEndpointImpl;
+import net.minecraft.network.FriendlyByteBuf;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
@@ -24,36 +27,105 @@ public abstract class IOBusPartChannelCardMixin implements InterfaceWirelessLink
@Unique
private WirelessSlaveLink extendedae_plus$link;
+
+ @Unique
+ private long extendedae_plus$lastChannel = -1;
+
+ @Unique
+ private boolean extendedae_plus$clientConnected = false;
+
+ @Unique
+ private boolean extendedae_plus$hasTickInitialized = false;
@Inject(method = "upgradesChanged", at = @At("TAIL"))
private void extendedae_plus$onUpgradesChanged(CallbackInfo ci) {
- IUpgradeInventory inv = this.getUpgrades();
- long channel = 0L;
- boolean found = false;
- for (var stack : inv) {
- if (!stack.isEmpty() && stack.getItem() == ModItems.CHANNEL_CARD.get()) {
- channel = ChannelCardItem.getChannel(stack);
- found = true;
- break;
- }
+ // 只在服务端初始化频道链接
+ if (!((appeng.parts.AEBasePart)(Object)this).isClientSide()) {
+ extendedae_plus$initializeChannelLink();
}
- if (!found) {
- // 无频道卡则断开
- if (extendedae_plus$link != null) {
- extendedae_plus$link.setFrequency(0L);
- extendedae_plus$link.updateStatus();
- }
+ }
+
+ @Inject(method = "tickingRequest", at = @At("HEAD"))
+ private void extendedae_plus$beforeTick(appeng.api.networking.IGridNode node, int ticksSinceLastCall, org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable cir) {
+ // 在第一次tick时初始化频道链接(此时网格节点已经在线)
+ if (!extendedae_plus$hasTickInitialized && !((appeng.parts.AEBasePart)(Object)this).isClientSide()) {
+ extendedae_plus$hasTickInitialized = true;
+ extendedae_plus$initializeChannelLink();
+ }
+ }
+
+ @Inject(method = "readFromNBT", at = @At("TAIL"))
+ private void extendedae_plus$afterReadFromNBT(CompoundTag extra, CallbackInfo ci) {
+ // 从NBT加载时重置频道缓存和tick初始化标志
+ if (!((appeng.parts.AEBasePart)(Object)this).isClientSide()) {
+ extendedae_plus$lastChannel = -1;
+ extendedae_plus$hasTickInitialized = false; // 重置标志,允许再次初始化
+ }
+ }
+
+ @Unique
+ private void extendedae_plus$initializeChannelLink() {
+ // 防止重复调用
+ if (((appeng.parts.AEBasePart)(Object)this).isClientSide()) {
return;
}
- if (extendedae_plus$link == null) {
- var endpoint = new GenericNodeEndpointImpl(
- () -> ((appeng.parts.AEBasePart)(Object)this).getHost().getBlockEntity(),
- () -> ((IActionHost)(Object)this).getActionableNode()
- );
- extendedae_plus$link = new WirelessSlaveLink(endpoint);
+
+ try {
+ IUpgradeInventory inv = this.getUpgrades();
+ long channel = 0L;
+ boolean found = false;
+ for (var stack : inv) {
+ if (!stack.isEmpty() && stack.getItem() == ModItems.CHANNEL_CARD.get()) {
+ channel = ChannelCardItem.getChannel(stack);
+ found = true;
+ break;
+ }
+ }
+
+ // 频道没有变化则跳过
+ if (extendedae_plus$lastChannel == channel) {
+ return;
+ }
+ extendedae_plus$lastChannel = channel;
+
+ ExtendedAELogger.LOGGER.debug("[服务端] IOBus 初始化频道链接: found={}, channel={}", found, channel);
+
+ if (!found) {
+ // 无频道卡则断开
+ if (extendedae_plus$link != null) {
+ extendedae_plus$link.setFrequency(0L);
+ extendedae_plus$link.updateStatus();
+ ExtendedAELogger.LOGGER.debug("[服务端] IOBus 断开频道链接");
+ // 立即通知客户端状态变化(断开连接无需延迟)
+ ((appeng.parts.AEBasePart)(Object)this).getHost().markForUpdate();
+ }
+ return;
+ }
+
+ if (extendedae_plus$link == null) {
+ var endpoint = new GenericNodeEndpointImpl(
+ () -> ((appeng.parts.AEBasePart)(Object)this).getHost().getBlockEntity(),
+ () -> ((IActionHost)(Object)this).getActionableNode()
+ );
+ extendedae_plus$link = new WirelessSlaveLink(endpoint);
+ ExtendedAELogger.LOGGER.debug("[服务端] IOBus 创建新的无线链接");
+ }
+
+ extendedae_plus$link.setFrequency(channel);
+ extendedae_plus$link.updateStatus();
+
+ // 调试信息:检查网格节点状态
+ var gridNode = ((IActionHost)(Object)this).getActionableNode();
+ ExtendedAELogger.LOGGER.debug("[服务端] IOBus 设置频道: {}, 连接状态: {}, 网格节点: {}, 在线: {}",
+ channel, extendedae_plus$link.isConnected(),
+ gridNode != null ? "exists" : "null",
+ gridNode != null ? gridNode.isOnline() : "N/A");
+
+ // 通知客户端状态变化
+ ((appeng.parts.AEBasePart)(Object)this).getHost().markForUpdate();
+ } catch (Exception e) {
+ ExtendedAELogger.LOGGER.error("[服务端] IOBus 初始化频道链接失败", e);
}
- extendedae_plus$link.setFrequency(channel);
- extendedae_plus$link.updateStatus();
}
@Override
@@ -62,4 +134,18 @@ public abstract class IOBusPartChannelCardMixin implements InterfaceWirelessLink
extendedae_plus$link.updateStatus();
}
}
+
+ @Override
+ public boolean extendedae_plus$isWirelessConnected() {
+ if (((appeng.parts.AEBasePart)(Object)this).isClientSide()) {
+ return extendedae_plus$clientConnected;
+ } else {
+ return extendedae_plus$link != null && extendedae_plus$link.isConnected();
+ }
+ }
+
+ @Override
+ public void extendedae_plus$setClientWirelessState(boolean connected) {
+ extendedae_plus$clientConnected = connected;
+ }
}
diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/parts/storagebus/StorageBusPartChannelCardMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/parts/storagebus/StorageBusPartChannelCardMixin.java
index 2abb221..e4cc287 100644
--- a/src/main/java/com/extendedae_plus/mixin/ae2/parts/storagebus/StorageBusPartChannelCardMixin.java
+++ b/src/main/java/com/extendedae_plus/mixin/ae2/parts/storagebus/StorageBusPartChannelCardMixin.java
@@ -1,9 +1,13 @@
package com.extendedae_plus.mixin.ae2.parts.storagebus;
+import appeng.api.networking.IGridNodeListener;
import appeng.api.networking.security.IActionHost;
import appeng.api.upgrades.IUpgradeInventory;
import appeng.api.upgrades.IUpgradeableObject;
import appeng.parts.storagebus.StorageBusPart;
+import net.minecraft.nbt.CompoundTag;
+import net.minecraft.network.FriendlyByteBuf;
+import com.extendedae_plus.util.ExtendedAELogger;
import com.extendedae_plus.ae.items.ChannelCardItem;
import com.extendedae_plus.bridge.InterfaceWirelessLinkBridge;
import com.extendedae_plus.init.ModItems;
@@ -23,35 +27,101 @@ public abstract class StorageBusPartChannelCardMixin implements InterfaceWireles
@Unique
private WirelessSlaveLink extendedae_plus$link;
+
+ @Unique
+ private long extendedae_plus$lastChannel = -1;
+
+ @Unique
+ private boolean extendedae_plus$clientConnected = false;
@Inject(method = "upgradesChanged", at = @At("TAIL"))
private void extendedae_plus$onUpgradesChanged(CallbackInfo ci) {
- IUpgradeInventory inv = this.getUpgrades();
- long channel = 0L;
- boolean found = false;
- for (var stack : inv) {
- if (!stack.isEmpty() && stack.getItem() == ModItems.CHANNEL_CARD.get()) {
- channel = ChannelCardItem.getChannel(stack);
- found = true;
- break;
- }
+ // 只在服务端初始化频道链接
+ if (!((appeng.parts.AEBasePart)(Object)this).isClientSide()) {
+ extendedae_plus$initializeChannelLink();
}
- if (!found) {
- if (extendedae_plus$link != null) {
- extendedae_plus$link.setFrequency(0L);
- extendedae_plus$link.updateStatus();
- }
+ }
+
+ @Inject(method = "onMainNodeStateChanged", at = @At("TAIL"))
+ private void extendedae_plus$onMainNodeStateChanged(IGridNodeListener.State reason, CallbackInfo ci) {
+ // 在节点状态变化时(包括加载后的GRID_BOOT)重新初始化频道链接
+ if (reason == IGridNodeListener.State.GRID_BOOT && !((appeng.parts.AEBasePart)(Object)this).isClientSide()) {
+ extendedae_plus$initializeChannelLink();
+ }
+ }
+
+ @Inject(method = "readFromNBT", at = @At("TAIL"))
+ private void extendedae_plus$afterReadFromNBT(CompoundTag extra, CallbackInfo ci) {
+ // 从NBT加载后也重新初始化频道链接(只在服务端)
+ if (!((appeng.parts.AEBasePart)(Object)this).isClientSide()) {
+ // 从NBT加载时重置频道缓存,强制重新初始化
+ extendedae_plus$lastChannel = -1;
+ extendedae_plus$initializeChannelLink();
+ }
+ }
+
+ @Unique
+ private void extendedae_plus$initializeChannelLink() {
+ // 防止重复调用
+ if (((appeng.parts.AEBasePart)(Object)this).isClientSide()) {
return;
}
- if (extendedae_plus$link == null) {
- var endpoint = new GenericNodeEndpointImpl(
- () -> ((appeng.parts.AEBasePart)(Object)this).getHost().getBlockEntity(),
- () -> ((IActionHost)(Object)this).getActionableNode()
- );
- extendedae_plus$link = new WirelessSlaveLink(endpoint);
+
+ try {
+ IUpgradeInventory inv = this.getUpgrades();
+ long channel = 0L;
+ boolean found = false;
+ for (var stack : inv) {
+ if (!stack.isEmpty() && stack.getItem() == ModItems.CHANNEL_CARD.get()) {
+ channel = ChannelCardItem.getChannel(stack);
+ found = true;
+ break;
+ }
+ }
+
+ // 频道没有变化则跳过
+ if (extendedae_plus$lastChannel == channel) {
+ return;
+ }
+ extendedae_plus$lastChannel = channel;
+
+ ExtendedAELogger.LOGGER.debug("[服务端] StorageBus 初始化频道链接: found={}, channel={}", found, channel);
+
+ if (!found) {
+ if (extendedae_plus$link != null) {
+ extendedae_plus$link.setFrequency(0L);
+ extendedae_plus$link.updateStatus();
+ ExtendedAELogger.LOGGER.debug("[服务端] StorageBus 断开频道链接");
+ // 通知客户端状态变化
+ ((appeng.parts.AEBasePart)(Object)this).getHost().markForUpdate();
+ }
+ return;
+ }
+
+ if (extendedae_plus$link == null) {
+ var endpoint = new GenericNodeEndpointImpl(
+ () -> ((appeng.parts.AEBasePart)(Object)this).getHost().getBlockEntity(),
+ () -> ((IActionHost)(Object)this).getActionableNode()
+ );
+ extendedae_plus$link = new WirelessSlaveLink(endpoint);
+ ExtendedAELogger.LOGGER.debug("[服务端] StorageBus 创建新的无线链接");
+ }
+
+ extendedae_plus$link.setFrequency(channel);
+ extendedae_plus$link.updateStatus();
+
+ // 调试信息:检查网格节点状态
+ var gridNode = ((IActionHost)(Object)this).getActionableNode();
+ ExtendedAELogger.LOGGER.debug("[服务端] StorageBus 设置频道: {}, 连接状态: {}, 网格节点: {}, 在线: {}",
+ channel, extendedae_plus$link.isConnected(),
+ gridNode != null ? "exists" : "null",
+ gridNode != null ? gridNode.isOnline() : "N/A");
+
+ // 通知客户端状态变化
+ ((appeng.parts.AEBasePart)(Object)this).getHost().markForUpdate();
+ } catch (Exception e) {
+ ExtendedAELogger.LOGGER.error("[服务端] StorageBus 初始化频道链接失败", e);
}
- extendedae_plus$link.setFrequency(channel);
- extendedae_plus$link.updateStatus();
}
@Override
@@ -60,4 +130,18 @@ public abstract class StorageBusPartChannelCardMixin implements InterfaceWireles
extendedae_plus$link.updateStatus();
}
}
+
+ @Override
+ public boolean extendedae_plus$isWirelessConnected() {
+ if (((appeng.parts.AEBasePart)(Object)this).isClientSide()) {
+ return extendedae_plus$clientConnected;
+ } else {
+ return extendedae_plus$link != null && extendedae_plus$link.isConnected();
+ }
+ }
+
+ @Override
+ public void extendedae_plus$setClientWirelessState(boolean connected) {
+ extendedae_plus$clientConnected = connected;
+ }
}
diff --git a/src/main/resources/extendedae_plus.mixins.json b/src/main/resources/extendedae_plus.mixins.json
index 48e0c2e..ab3d360 100644
--- a/src/main/resources/extendedae_plus.mixins.json
+++ b/src/main/resources/extendedae_plus.mixins.json
@@ -67,6 +67,7 @@
"ae2.helpers.patternprovider.PatternProviderLogicHostUpgradesMixin",
"ae2.menu.PatternProviderMenuUpgradesMixin",
"ae2.helpers.patternprovider.PatternProviderLogicTickerMixin",
+ "ae2.parts.AEBasePartClientSyncMixin",
"ae2.parts.automation.IOBusPartChannelCardMixin",
"ae2.parts.automation.IOBusPartTickerChannelCardMixin",
"ae2.parts.storagebus.StorageBusPartChannelCardMixin",
From 7f704e2a9c16cf767ad6829f2419a16396ecfe51 Mon Sep 17 00:00:00 2001
From: GaLicn <133291877+GaLicn@users.noreply.github.com>
Date: Tue, 23 Sep 2025 10:48:44 +0800
Subject: [PATCH 18/34] =?UTF-8?q?=E4=BF=AE=E5=A4=8Diobus=E5=88=9D=E5=A7=8B?=
=?UTF-8?q?=E5=8C=96?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../bridge/InterfaceWirelessLinkBridge.java | 6 +-
.../PatternProviderScreenUpgradesMixin.java | 6 +-
.../InterfaceLogicChannelCardMixin.java | 24 +++----
.../InterfaceLogicChannelLinkBridge.java | 2 +-
.../helpers/InterfaceLogicTickerMixin.java | 4 +-
.../PatternProviderLogicTickerMixin.java | 4 +-
.../PatternProviderLogicUpgradesMixin.java | 44 ++++++------
.../PatternProviderMenuUpgradesMixin.java | 8 +--
.../ae2/parts/AEBasePartClientSyncMixin.java | 19 ++---
.../automation/IOBusPartChannelCardMixin.java | 70 +++++++++----------
.../IOBusPartTickerChannelCardMixin.java | 4 +-
.../StorageBusPartChannelCardMixin.java | 64 ++++++++---------
.../StorageBusPartTickerChannelCardMixin.java | 4 +-
.../mixin/hooks/ModelBakeryMixin.java | 2 +-
.../EncodePatternTransferHandlerMixin.java | 2 +-
15 files changed, 120 insertions(+), 143 deletions(-)
diff --git a/src/main/java/com/extendedae_plus/bridge/InterfaceWirelessLinkBridge.java b/src/main/java/com/extendedae_plus/bridge/InterfaceWirelessLinkBridge.java
index 11b9013..023ee39 100644
--- a/src/main/java/com/extendedae_plus/bridge/InterfaceWirelessLinkBridge.java
+++ b/src/main/java/com/extendedae_plus/bridge/InterfaceWirelessLinkBridge.java
@@ -4,19 +4,19 @@ package com.extendedae_plus.bridge;
* 非 mixin 包下的桥接接口,供 mixin 进行 instanceof 检测和回调。
*/
public interface InterfaceWirelessLinkBridge {
- void extendedae_plus$updateWirelessLink();
+ void eap$updateWirelessLink();
/**
* 获取无线连接状态(服务端返回真实状态,客户端返回同步状态)
*/
- default boolean extendedae_plus$isWirelessConnected() {
+ default boolean eap$isWirelessConnected() {
return false;
}
/**
* 设置客户端的无线连接状态(仅在客户端使用)
*/
- default void extendedae_plus$setClientWirelessState(boolean connected) {
+ default void eap$setClientWirelessState(boolean connected) {
// 默认实现为空
}
}
diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/client/gui/PatternProviderScreenUpgradesMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/client/gui/PatternProviderScreenUpgradesMixin.java
index 5e94289..9e84b7f 100644
--- a/src/main/java/com/extendedae_plus/mixin/ae2/client/gui/PatternProviderScreenUpgradesMixin.java
+++ b/src/main/java/com/extendedae_plus/mixin/ae2/client/gui/PatternProviderScreenUpgradesMixin.java
@@ -25,17 +25,17 @@ import java.util.List;
public abstract class PatternProviderScreenUpgradesMixin extends AEBaseScreen {
@Inject(method = "", at = @At("TAIL"))
- private void extendedae_plus$initUpgrades(PatternProviderMenu menu, Inventory playerInventory, Component title, ScreenStyle style, CallbackInfo ci) {
+ private void eap$initUpgrades(PatternProviderMenu menu, Inventory playerInventory, Component title, ScreenStyle style, CallbackInfo ci) {
this.widgets.add("upgrades", new UpgradesPanel(
menu.getSlots(SlotSemantics.UPGRADE),
- this::extendedae_plus$getCompatibleUpgrades));
+ this::eap$getCompatibleUpgrades));
if (((IUpgradableMenu) menu).getToolbox().isPresent()) {
this.widgets.add("toolbox", new ToolboxPanel(style, ((IUpgradableMenu) menu).getToolbox().getName()));
}
}
@Unique
- private List extendedae_plus$getCompatibleUpgrades() {
+ private List eap$getCompatibleUpgrades() {
var list = new ArrayList();
list.add(GuiText.CompatibleUpgrades.text());
list.addAll(Upgrades.getTooltipLinesForMachine(((IUpgradableMenu) menu).getUpgrades().getUpgradableItem()));
diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/helpers/InterfaceLogicChannelCardMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/helpers/InterfaceLogicChannelCardMixin.java
index 659b120..b8e7401 100644
--- a/src/main/java/com/extendedae_plus/mixin/ae2/helpers/InterfaceLogicChannelCardMixin.java
+++ b/src/main/java/com/extendedae_plus/mixin/ae2/helpers/InterfaceLogicChannelCardMixin.java
@@ -28,23 +28,23 @@ public abstract class InterfaceLogicChannelCardMixin implements InterfaceWireles
@Shadow(remap = false) protected InterfaceLogicHost host;
@Unique
- private WirelessSlaveLink extendedae_plus$link;
+ private WirelessSlaveLink eap$link;
@Inject(method = "onUpgradesChanged", at = @At("TAIL"), remap = false)
- private void extendedae_plus$onUpgradesChangedTail(CallbackInfo ci) {
+ private void eap$onUpgradesChangedTail(CallbackInfo ci) {
handleChannelCardChange();
}
@Inject(method = "readFromNBT", at = @At("TAIL"), remap = false)
- private void extendedae_plus$afterReadNBT(net.minecraft.nbt.CompoundTag tag, CallbackInfo ci) {
+ private void eap$afterReadNBT(net.minecraft.nbt.CompoundTag tag, CallbackInfo ci) {
// 重载后根据卡状态恢复连接
handleChannelCardChange();
}
@Inject(method = "clearContent", at = @At("HEAD"), remap = false)
- private void extendedae_plus$onClearContent(CallbackInfo ci) {
- if (extendedae_plus$link != null) {
- extendedae_plus$link.onUnloadOrRemove();
+ private void eap$onClearContent(CallbackInfo ci) {
+ if (eap$link != null) {
+ eap$link.onUnloadOrRemove();
}
}
@@ -58,17 +58,17 @@ public abstract class InterfaceLogicChannelCardMixin implements InterfaceWireles
break;
}
}
- if (extendedae_plus$link == null) {
+ if (eap$link == null) {
IWirelessEndpoint endpoint = new InterfaceNodeEndpointImpl(host, this::getActionableNode);
- extendedae_plus$link = new WirelessSlaveLink(endpoint);
+ eap$link = new WirelessSlaveLink(endpoint);
}
- extendedae_plus$link.setFrequency(channel);
+ eap$link.setFrequency(channel);
}
@Override
- public void extendedae_plus$updateWirelessLink() {
- if (extendedae_plus$link != null) {
- extendedae_plus$link.updateStatus();
+ public void eap$updateWirelessLink() {
+ if (eap$link != null) {
+ eap$link.updateStatus();
}
}
}
diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/helpers/InterfaceLogicChannelLinkBridge.java b/src/main/java/com/extendedae_plus/mixin/ae2/helpers/InterfaceLogicChannelLinkBridge.java
index 498d023..7dd6ad0 100644
--- a/src/main/java/com/extendedae_plus/mixin/ae2/helpers/InterfaceLogicChannelLinkBridge.java
+++ b/src/main/java/com/extendedae_plus/mixin/ae2/helpers/InterfaceLogicChannelLinkBridge.java
@@ -4,5 +4,5 @@ package com.extendedae_plus.bridge;
* 旧名兼容:已迁移到非 mixin 包,避免 Mixin 处理器禁止直接引用。
*/
public interface InterfaceLogicChannelLinkBridge {
- void extendedae_plus$updateWirelessLink();
+ void eap$updateWirelessLink();
}
diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/helpers/InterfaceLogicTickerMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/helpers/InterfaceLogicTickerMixin.java
index 77f9cbc..50ac299 100644
--- a/src/main/java/com/extendedae_plus/mixin/ae2/helpers/InterfaceLogicTickerMixin.java
+++ b/src/main/java/com/extendedae_plus/mixin/ae2/helpers/InterfaceLogicTickerMixin.java
@@ -21,10 +21,10 @@ public abstract class InterfaceLogicTickerMixin {
private InterfaceLogic this$0;
@Inject(method = "tickingRequest", at = @At("TAIL"), remap = false)
- private void extendedae_plus$tickTail(appeng.api.networking.IGridNode node, int ticksSinceLastCall,
+ private void eap$tickTail(appeng.api.networking.IGridNode node, int ticksSinceLastCall,
CallbackInfoReturnable cir) {
if (this$0 instanceof InterfaceWirelessLinkBridge bridge) {
- bridge.extendedae_plus$updateWirelessLink();
+ bridge.eap$updateWirelessLink();
}
}
}
diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/helpers/patternprovider/PatternProviderLogicTickerMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/helpers/patternprovider/PatternProviderLogicTickerMixin.java
index 8baebff..8753adb 100644
--- a/src/main/java/com/extendedae_plus/mixin/ae2/helpers/patternprovider/PatternProviderLogicTickerMixin.java
+++ b/src/main/java/com/extendedae_plus/mixin/ae2/helpers/patternprovider/PatternProviderLogicTickerMixin.java
@@ -21,10 +21,10 @@ public abstract class PatternProviderLogicTickerMixin {
private PatternProviderLogic this$0;
@Inject(method = "tickingRequest", at = @At("TAIL"))
- private void extendedae_plus$tickTail(appeng.api.networking.IGridNode node, int ticksSinceLastCall,
+ private void eap$tickTail(appeng.api.networking.IGridNode node, int ticksSinceLastCall,
CallbackInfoReturnable cir) {
if (this$0 instanceof InterfaceWirelessLinkBridge bridge) {
- bridge.extendedae_plus$updateWirelessLink();
+ bridge.eap$updateWirelessLink();
}
}
}
diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/helpers/patternprovider/PatternProviderLogicUpgradesMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/helpers/patternprovider/PatternProviderLogicUpgradesMixin.java
index 82e5bb0..7b56bae 100644
--- a/src/main/java/com/extendedae_plus/mixin/ae2/helpers/patternprovider/PatternProviderLogicUpgradesMixin.java
+++ b/src/main/java/com/extendedae_plus/mixin/ae2/helpers/patternprovider/PatternProviderLogicUpgradesMixin.java
@@ -32,10 +32,10 @@ import java.util.List;
@Mixin(value = PatternProviderLogic.class, remap = false)
public abstract class PatternProviderLogicUpgradesMixin implements IUpgradeableObject, InterfaceWirelessLinkBridge {
@Unique
- private IUpgradeInventory extendedae_plus$upgrades = UpgradeInventories.empty();
+ private IUpgradeInventory eap$upgrades = UpgradeInventories.empty();
@Unique
- private WirelessSlaveLink extendedae_plus$link;
+ private WirelessSlaveLink eap$link;
@Final
@Shadow
@@ -50,48 +50,48 @@ public abstract class PatternProviderLogicUpgradesMixin implements IUpgradeableO
private IActionSource actionSource;
@Unique
- private void extendedae_plus$onUpgradesChanged() {
+ private void eap$onUpgradesChanged() {
this.host.saveChanges();
// 读取频道卡,更新无线链接频率
long channel = 0L;
- for (var stack : this.extendedae_plus$upgrades) {
+ for (var stack : this.eap$upgrades) {
if (!stack.isEmpty() && stack.getItem() == ModItems.CHANNEL_CARD.get()) {
channel = ChannelCardItem.getChannel(stack);
break;
}
}
- if (extendedae_plus$link == null) {
+ if (eap$link == null) {
var endpoint = new GenericNodeEndpointImpl(() -> host.getBlockEntity(), () -> this.mainNode.getNode());
- extendedae_plus$link = new WirelessSlaveLink(endpoint);
+ eap$link = new WirelessSlaveLink(endpoint);
}
- extendedae_plus$link.setFrequency(channel);
- extendedae_plus$link.updateStatus();
+ eap$link.setFrequency(channel);
+ eap$link.updateStatus();
}
@Override
public IUpgradeInventory getUpgrades() {
- return this.extendedae_plus$upgrades;
+ return this.eap$upgrades;
}
@Inject(method = "(Lappeng/api/networking/IManagedGridNode;Lappeng/helpers/patternprovider/PatternProviderLogicHost;I)V",
at = @At("TAIL"))
- private void extendedae_plus$initUpgrades(IManagedGridNode mainNode, PatternProviderLogicHost host, int patternInventorySize, CallbackInfo ci) {
- this.extendedae_plus$upgrades = UpgradeInventories.forMachine(host.getTerminalIcon().getItem(), 1, this::extendedae_plus$onUpgradesChanged);
+ private void eap$initUpgrades(IManagedGridNode mainNode, PatternProviderLogicHost host, int patternInventorySize, CallbackInfo ci) {
+ this.eap$upgrades = UpgradeInventories.forMachine(host.getTerminalIcon().getItem(), 1, this::eap$onUpgradesChanged);
}
@Inject(method = "writeToNBT", at = @At("TAIL"))
- private void extendedae_plus$saveUpgrades(CompoundTag tag, CallbackInfo ci) {
- this.extendedae_plus$upgrades.writeToNBT(tag, "upgrades");
+ private void eap$saveUpgrades(CompoundTag tag, CallbackInfo ci) {
+ this.eap$upgrades.writeToNBT(tag, "upgrades");
}
@Inject(method = "readFromNBT", at = @At("TAIL"))
- private void extendedae_plus$loadUpgrades(CompoundTag tag, CallbackInfo ci) {
- this.extendedae_plus$upgrades.readFromNBT(tag, "upgrades");
+ private void eap$loadUpgrades(CompoundTag tag, CallbackInfo ci) {
+ this.eap$upgrades.readFromNBT(tag, "upgrades");
}
@Inject(method = "addDrops", at = @At("TAIL"))
- private void extendedae_plus$dropUpgrades(List drops, CallbackInfo ci) {
- for (var stack : this.extendedae_plus$upgrades) {
+ private void eap$dropUpgrades(List drops, CallbackInfo ci) {
+ for (var stack : this.eap$upgrades) {
if (!stack.isEmpty()) {
drops.add(stack);
}
@@ -99,14 +99,14 @@ public abstract class PatternProviderLogicUpgradesMixin implements IUpgradeableO
}
@Inject(method = "clearContent", at = @At("TAIL"))
- private void extendedae_plus$clearUpgrades(CallbackInfo ci) {
- this.extendedae_plus$upgrades.clear();
+ private void eap$clearUpgrades(CallbackInfo ci) {
+ this.eap$upgrades.clear();
}
@Override
- public void extendedae_plus$updateWirelessLink() {
- if (extendedae_plus$link != null) {
- extendedae_plus$link.updateStatus();
+ public void eap$updateWirelessLink() {
+ if (eap$link != null) {
+ eap$link.updateStatus();
}
}
}
diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/menu/PatternProviderMenuUpgradesMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/menu/PatternProviderMenuUpgradesMixin.java
index 514b523..0cda3ae 100644
--- a/src/main/java/com/extendedae_plus/mixin/ae2/menu/PatternProviderMenuUpgradesMixin.java
+++ b/src/main/java/com/extendedae_plus/mixin/ae2/menu/PatternProviderMenuUpgradesMixin.java
@@ -24,18 +24,18 @@ public abstract class PatternProviderMenuUpgradesMixin extends AEBaseMenu implem
@Shadow protected PatternProviderLogic logic;
@Unique
- private ToolboxMenu extendedae_plus$toolbox;
+ private ToolboxMenu eap$toolbox;
@Inject(method = "(Lnet/minecraft/world/inventory/MenuType;ILnet/minecraft/world/entity/player/Inventory;Lappeng/helpers/patternprovider/PatternProviderLogicHost;)V",
at = @At("TAIL"))
- private void extendedae_plus$initUpgrades(MenuType> menuType, int id, Inventory playerInventory, PatternProviderLogicHost host, CallbackInfo ci) {
- this.extendedae_plus$toolbox = new ToolboxMenu(this);
+ private void eap$initUpgrades(MenuType> menuType, int id, Inventory playerInventory, PatternProviderLogicHost host, CallbackInfo ci) {
+ this.eap$toolbox = new ToolboxMenu(this);
this.setupUpgrades(((IUpgradeableObject) host).getUpgrades());
}
@Override
public ToolboxMenu getToolbox() {
- return this.extendedae_plus$toolbox;
+ return this.eap$toolbox;
}
@Override
diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/parts/AEBasePartClientSyncMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/parts/AEBasePartClientSyncMixin.java
index 2ae0175..b180e30 100644
--- a/src/main/java/com/extendedae_plus/mixin/ae2/parts/AEBasePartClientSyncMixin.java
+++ b/src/main/java/com/extendedae_plus/mixin/ae2/parts/AEBasePartClientSyncMixin.java
@@ -16,7 +16,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
public class AEBasePartClientSyncMixin {
@Inject(method = "writeToStream", at = @At("TAIL"))
- private void extendedae_plus$writeWirelessState(FriendlyByteBuf data, CallbackInfo ci) {
+ private void eap$writeWirelessState(FriendlyByteBuf data, CallbackInfo ci) {
// 检查是否实现了无线链接桥接接口
if (this instanceof InterfaceWirelessLinkBridge) {
InterfaceWirelessLinkBridge bridge = (InterfaceWirelessLinkBridge) this;
@@ -26,15 +26,10 @@ public class AEBasePartClientSyncMixin {
// 只在服务端获取真实连接状态
AEBasePart part = (AEBasePart)(Object)this;
if (!part.isClientSide()) {
- connected = bridge.extendedae_plus$isWirelessConnected();
- // 调试日志:记录写入的状态
- if (part.getClass().getSimpleName().contains("IOBus")) {
- System.out.println("[写入状态] IOBus 无线连接状态: " + connected);
- }
+ connected = bridge.eap$isWirelessConnected();
}
} catch (Exception e) {
// 忽略异常,默认为false
- System.err.println("获取无线连接状态失败: " + e.getMessage());
}
data.writeBoolean(connected);
} else {
@@ -44,7 +39,7 @@ public class AEBasePartClientSyncMixin {
}
@Inject(method = "readFromStream", at = @At("TAIL"))
- private void extendedae_plus$readWirelessState(FriendlyByteBuf data, CallbackInfoReturnable cir) {
+ private void eap$readWirelessState(FriendlyByteBuf data, CallbackInfoReturnable cir) {
// 读取无线连接状态
boolean connected = data.readBoolean();
@@ -52,16 +47,10 @@ public class AEBasePartClientSyncMixin {
if (this instanceof InterfaceWirelessLinkBridge) {
InterfaceWirelessLinkBridge bridge = (InterfaceWirelessLinkBridge) this;
try {
- // 调试日志:记录读取的状态
- AEBasePart part = (AEBasePart)(Object)this;
- if (part.getClass().getSimpleName().contains("IOBus")) {
- System.out.println("[读取状态] IOBus 无线连接状态: " + connected);
- }
// 更新客户端状态
- bridge.extendedae_plus$setClientWirelessState(connected);
+ bridge.eap$setClientWirelessState(connected);
} catch (Exception e) {
// 忽略异常
- System.err.println("设置客户端无线连接状态失败: " + e.getMessage());
}
}
}
diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/parts/automation/IOBusPartChannelCardMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/parts/automation/IOBusPartChannelCardMixin.java
index cf21f1d..b09b40d 100644
--- a/src/main/java/com/extendedae_plus/mixin/ae2/parts/automation/IOBusPartChannelCardMixin.java
+++ b/src/main/java/com/extendedae_plus/mixin/ae2/parts/automation/IOBusPartChannelCardMixin.java
@@ -26,45 +26,45 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
public abstract class IOBusPartChannelCardMixin implements InterfaceWirelessLinkBridge, IUpgradeableObject {
@Unique
- private WirelessSlaveLink extendedae_plus$link;
+ private WirelessSlaveLink eap$link;
@Unique
- private long extendedae_plus$lastChannel = -1;
+ private long eap$lastChannel = -1;
@Unique
- private boolean extendedae_plus$clientConnected = false;
+ private boolean eap$clientConnected = false;
@Unique
- private boolean extendedae_plus$hasTickInitialized = false;
+ private boolean eap$hasTickInitialized = false;
@Inject(method = "upgradesChanged", at = @At("TAIL"))
- private void extendedae_plus$onUpgradesChanged(CallbackInfo ci) {
+ private void eap$onUpgradesChanged(CallbackInfo ci) {
// 只在服务端初始化频道链接
if (!((appeng.parts.AEBasePart)(Object)this).isClientSide()) {
- extendedae_plus$initializeChannelLink();
+ eap$initializeChannelLink();
}
}
@Inject(method = "tickingRequest", at = @At("HEAD"))
- private void extendedae_plus$beforeTick(appeng.api.networking.IGridNode node, int ticksSinceLastCall, org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable cir) {
+ private void eap$beforeTick(appeng.api.networking.IGridNode node, int ticksSinceLastCall, org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable cir) {
// 在第一次tick时初始化频道链接(此时网格节点已经在线)
- if (!extendedae_plus$hasTickInitialized && !((appeng.parts.AEBasePart)(Object)this).isClientSide()) {
- extendedae_plus$hasTickInitialized = true;
- extendedae_plus$initializeChannelLink();
+ if (!eap$hasTickInitialized && !((appeng.parts.AEBasePart)(Object)this).isClientSide()) {
+ eap$hasTickInitialized = true;
+ eap$initializeChannelLink();
}
}
@Inject(method = "readFromNBT", at = @At("TAIL"))
- private void extendedae_plus$afterReadFromNBT(CompoundTag extra, CallbackInfo ci) {
+ private void eap$afterReadFromNBT(CompoundTag extra, CallbackInfo ci) {
// 从NBT加载时重置频道缓存和tick初始化标志
if (!((appeng.parts.AEBasePart)(Object)this).isClientSide()) {
- extendedae_plus$lastChannel = -1;
- extendedae_plus$hasTickInitialized = false; // 重置标志,允许再次初始化
+ eap$lastChannel = -1;
+ eap$hasTickInitialized = false; // 重置标志,允许再次初始化
}
}
@Unique
- private void extendedae_plus$initializeChannelLink() {
+ private void eap$initializeChannelLink() {
// 防止重复调用
if (((appeng.parts.AEBasePart)(Object)this).isClientSide()) {
return;
@@ -83,18 +83,18 @@ public abstract class IOBusPartChannelCardMixin implements InterfaceWirelessLink
}
// 频道没有变化则跳过
- if (extendedae_plus$lastChannel == channel) {
+ if (eap$lastChannel == channel) {
return;
}
- extendedae_plus$lastChannel = channel;
+ eap$lastChannel = channel;
ExtendedAELogger.LOGGER.debug("[服务端] IOBus 初始化频道链接: found={}, channel={}", found, channel);
if (!found) {
// 无频道卡则断开
- if (extendedae_plus$link != null) {
- extendedae_plus$link.setFrequency(0L);
- extendedae_plus$link.updateStatus();
+ if (eap$link != null) {
+ eap$link.setFrequency(0L);
+ eap$link.updateStatus();
ExtendedAELogger.LOGGER.debug("[服务端] IOBus 断开频道链接");
// 立即通知客户端状态变化(断开连接无需延迟)
((appeng.parts.AEBasePart)(Object)this).getHost().markForUpdate();
@@ -102,24 +102,18 @@ public abstract class IOBusPartChannelCardMixin implements InterfaceWirelessLink
return;
}
- if (extendedae_plus$link == null) {
+ if (eap$link == null) {
var endpoint = new GenericNodeEndpointImpl(
() -> ((appeng.parts.AEBasePart)(Object)this).getHost().getBlockEntity(),
() -> ((IActionHost)(Object)this).getActionableNode()
);
- extendedae_plus$link = new WirelessSlaveLink(endpoint);
+ eap$link = new WirelessSlaveLink(endpoint);
ExtendedAELogger.LOGGER.debug("[服务端] IOBus 创建新的无线链接");
}
- extendedae_plus$link.setFrequency(channel);
- extendedae_plus$link.updateStatus();
-
- // 调试信息:检查网格节点状态
- var gridNode = ((IActionHost)(Object)this).getActionableNode();
- ExtendedAELogger.LOGGER.debug("[服务端] IOBus 设置频道: {}, 连接状态: {}, 网格节点: {}, 在线: {}",
- channel, extendedae_plus$link.isConnected(),
- gridNode != null ? "exists" : "null",
- gridNode != null ? gridNode.isOnline() : "N/A");
+ eap$link.setFrequency(channel);
+ eap$link.updateStatus();
+ ExtendedAELogger.LOGGER.debug("[服务端] IOBus 设置频道: {}, 连接状态: {}", channel, eap$link.isConnected());
// 通知客户端状态变化
((appeng.parts.AEBasePart)(Object)this).getHost().markForUpdate();
@@ -129,23 +123,23 @@ public abstract class IOBusPartChannelCardMixin implements InterfaceWirelessLink
}
@Override
- public void extendedae_plus$updateWirelessLink() {
- if (extendedae_plus$link != null) {
- extendedae_plus$link.updateStatus();
+ public void eap$updateWirelessLink() {
+ if (eap$link != null) {
+ eap$link.updateStatus();
}
}
@Override
- public boolean extendedae_plus$isWirelessConnected() {
+ public boolean eap$isWirelessConnected() {
if (((appeng.parts.AEBasePart)(Object)this).isClientSide()) {
- return extendedae_plus$clientConnected;
+ return eap$clientConnected;
} else {
- return extendedae_plus$link != null && extendedae_plus$link.isConnected();
+ return eap$link != null && eap$link.isConnected();
}
}
@Override
- public void extendedae_plus$setClientWirelessState(boolean connected) {
- extendedae_plus$clientConnected = connected;
+ public void eap$setClientWirelessState(boolean connected) {
+ eap$clientConnected = connected;
}
}
diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/parts/automation/IOBusPartTickerChannelCardMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/parts/automation/IOBusPartTickerChannelCardMixin.java
index c5bd28f..d9ac8ba 100644
--- a/src/main/java/com/extendedae_plus/mixin/ae2/parts/automation/IOBusPartTickerChannelCardMixin.java
+++ b/src/main/java/com/extendedae_plus/mixin/ae2/parts/automation/IOBusPartTickerChannelCardMixin.java
@@ -16,9 +16,9 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
public abstract class IOBusPartTickerChannelCardMixin {
@Inject(method = "tickingRequest", at = @At("TAIL"))
- private void extendedae_plus$tickTail(IGridNode node, int ticksSinceLastCall, CallbackInfoReturnable cir) {
+ private void eap$tickTail(IGridNode node, int ticksSinceLastCall, CallbackInfoReturnable cir) {
if (this instanceof InterfaceWirelessLinkBridge bridge) {
- bridge.extendedae_plus$updateWirelessLink();
+ bridge.eap$updateWirelessLink();
}
}
}
diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/parts/storagebus/StorageBusPartChannelCardMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/parts/storagebus/StorageBusPartChannelCardMixin.java
index e4cc287..00b6626 100644
--- a/src/main/java/com/extendedae_plus/mixin/ae2/parts/storagebus/StorageBusPartChannelCardMixin.java
+++ b/src/main/java/com/extendedae_plus/mixin/ae2/parts/storagebus/StorageBusPartChannelCardMixin.java
@@ -26,42 +26,42 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
public abstract class StorageBusPartChannelCardMixin implements InterfaceWirelessLinkBridge, IUpgradeableObject {
@Unique
- private WirelessSlaveLink extendedae_plus$link;
+ private WirelessSlaveLink eap$link;
@Unique
- private long extendedae_plus$lastChannel = -1;
+ private long eap$lastChannel = -1;
@Unique
- private boolean extendedae_plus$clientConnected = false;
+ private boolean eap$clientConnected = false;
@Inject(method = "upgradesChanged", at = @At("TAIL"))
- private void extendedae_plus$onUpgradesChanged(CallbackInfo ci) {
+ private void eap$onUpgradesChanged(CallbackInfo ci) {
// 只在服务端初始化频道链接
if (!((appeng.parts.AEBasePart)(Object)this).isClientSide()) {
- extendedae_plus$initializeChannelLink();
+ eap$initializeChannelLink();
}
}
@Inject(method = "onMainNodeStateChanged", at = @At("TAIL"))
- private void extendedae_plus$onMainNodeStateChanged(IGridNodeListener.State reason, CallbackInfo ci) {
+ private void eap$onMainNodeStateChanged(IGridNodeListener.State reason, CallbackInfo ci) {
// 在节点状态变化时(包括加载后的GRID_BOOT)重新初始化频道链接
if (reason == IGridNodeListener.State.GRID_BOOT && !((appeng.parts.AEBasePart)(Object)this).isClientSide()) {
- extendedae_plus$initializeChannelLink();
+ eap$initializeChannelLink();
}
}
@Inject(method = "readFromNBT", at = @At("TAIL"))
- private void extendedae_plus$afterReadFromNBT(CompoundTag extra, CallbackInfo ci) {
+ private void eap$afterReadFromNBT(CompoundTag extra, CallbackInfo ci) {
// 从NBT加载后也重新初始化频道链接(只在服务端)
if (!((appeng.parts.AEBasePart)(Object)this).isClientSide()) {
// 从NBT加载时重置频道缓存,强制重新初始化
- extendedae_plus$lastChannel = -1;
- extendedae_plus$initializeChannelLink();
+ eap$lastChannel = -1;
+ eap$initializeChannelLink();
}
}
@Unique
- private void extendedae_plus$initializeChannelLink() {
+ private void eap$initializeChannelLink() {
// 防止重复调用
if (((appeng.parts.AEBasePart)(Object)this).isClientSide()) {
return;
@@ -80,17 +80,17 @@ public abstract class StorageBusPartChannelCardMixin implements InterfaceWireles
}
// 频道没有变化则跳过
- if (extendedae_plus$lastChannel == channel) {
+ if (eap$lastChannel == channel) {
return;
}
- extendedae_plus$lastChannel = channel;
+ eap$lastChannel = channel;
ExtendedAELogger.LOGGER.debug("[服务端] StorageBus 初始化频道链接: found={}, channel={}", found, channel);
if (!found) {
- if (extendedae_plus$link != null) {
- extendedae_plus$link.setFrequency(0L);
- extendedae_plus$link.updateStatus();
+ if (eap$link != null) {
+ eap$link.setFrequency(0L);
+ eap$link.updateStatus();
ExtendedAELogger.LOGGER.debug("[服务端] StorageBus 断开频道链接");
// 通知客户端状态变化
((appeng.parts.AEBasePart)(Object)this).getHost().markForUpdate();
@@ -98,24 +98,18 @@ public abstract class StorageBusPartChannelCardMixin implements InterfaceWireles
return;
}
- if (extendedae_plus$link == null) {
+ if (eap$link == null) {
var endpoint = new GenericNodeEndpointImpl(
() -> ((appeng.parts.AEBasePart)(Object)this).getHost().getBlockEntity(),
() -> ((IActionHost)(Object)this).getActionableNode()
);
- extendedae_plus$link = new WirelessSlaveLink(endpoint);
+ eap$link = new WirelessSlaveLink(endpoint);
ExtendedAELogger.LOGGER.debug("[服务端] StorageBus 创建新的无线链接");
}
- extendedae_plus$link.setFrequency(channel);
- extendedae_plus$link.updateStatus();
-
- // 调试信息:检查网格节点状态
- var gridNode = ((IActionHost)(Object)this).getActionableNode();
- ExtendedAELogger.LOGGER.debug("[服务端] StorageBus 设置频道: {}, 连接状态: {}, 网格节点: {}, 在线: {}",
- channel, extendedae_plus$link.isConnected(),
- gridNode != null ? "exists" : "null",
- gridNode != null ? gridNode.isOnline() : "N/A");
+ eap$link.setFrequency(channel);
+ eap$link.updateStatus();
+ ExtendedAELogger.LOGGER.debug("[服务端] StorageBus 设置频道: {}, 连接状态: {}", channel, eap$link.isConnected());
// 通知客户端状态变化
((appeng.parts.AEBasePart)(Object)this).getHost().markForUpdate();
@@ -125,23 +119,23 @@ public abstract class StorageBusPartChannelCardMixin implements InterfaceWireles
}
@Override
- public void extendedae_plus$updateWirelessLink() {
- if (extendedae_plus$link != null) {
- extendedae_plus$link.updateStatus();
+ public void eap$updateWirelessLink() {
+ if (eap$link != null) {
+ eap$link.updateStatus();
}
}
@Override
- public boolean extendedae_plus$isWirelessConnected() {
+ public boolean eap$isWirelessConnected() {
if (((appeng.parts.AEBasePart)(Object)this).isClientSide()) {
- return extendedae_plus$clientConnected;
+ return eap$clientConnected;
} else {
- return extendedae_plus$link != null && extendedae_plus$link.isConnected();
+ return eap$link != null && eap$link.isConnected();
}
}
@Override
- public void extendedae_plus$setClientWirelessState(boolean connected) {
- extendedae_plus$clientConnected = connected;
+ public void eap$setClientWirelessState(boolean connected) {
+ eap$clientConnected = connected;
}
}
diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/parts/storagebus/StorageBusPartTickerChannelCardMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/parts/storagebus/StorageBusPartTickerChannelCardMixin.java
index 636b4e4..202bc80 100644
--- a/src/main/java/com/extendedae_plus/mixin/ae2/parts/storagebus/StorageBusPartTickerChannelCardMixin.java
+++ b/src/main/java/com/extendedae_plus/mixin/ae2/parts/storagebus/StorageBusPartTickerChannelCardMixin.java
@@ -16,9 +16,9 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
public abstract class StorageBusPartTickerChannelCardMixin {
@Inject(method = "tickingRequest", at = @At("TAIL"))
- private void extendedae_plus$tickTail(IGridNode node, int ticksSinceLastCall, CallbackInfoReturnable cir) {
+ private void eap$tickTail(IGridNode node, int ticksSinceLastCall, CallbackInfoReturnable cir) {
if (this instanceof InterfaceWirelessLinkBridge bridge) {
- bridge.extendedae_plus$updateWirelessLink();
+ bridge.eap$updateWirelessLink();
}
}
}
diff --git a/src/main/java/com/extendedae_plus/mixin/hooks/ModelBakeryMixin.java b/src/main/java/com/extendedae_plus/mixin/hooks/ModelBakeryMixin.java
index efc1b57..fa39a01 100644
--- a/src/main/java/com/extendedae_plus/mixin/hooks/ModelBakeryMixin.java
+++ b/src/main/java/com/extendedae_plus/mixin/hooks/ModelBakeryMixin.java
@@ -19,7 +19,7 @@ import com.extendedae_plus.hooks.BuiltInModelHooks;
@Mixin(ModelBakery.class)
public class ModelBakeryMixin {
@Inject(method = "loadModel", at = @At("HEAD"), cancellable = true)
- private void extendedae_plus$loadModelHook(ResourceLocation id, CallbackInfo ci) {
+ private void eap$loadModelHook(ResourceLocation id, CallbackInfo ci) {
var model = BuiltInModelHooks.getBuiltInModel(id);
if (model != null) {
cacheAndQueueDependencies(id, model);
diff --git a/src/main/java/com/extendedae_plus/mixin/jei/EncodePatternTransferHandlerMixin.java b/src/main/java/com/extendedae_plus/mixin/jei/EncodePatternTransferHandlerMixin.java
index 820b3ce..48e4b3f 100644
--- a/src/main/java/com/extendedae_plus/mixin/jei/EncodePatternTransferHandlerMixin.java
+++ b/src/main/java/com/extendedae_plus/mixin/jei/EncodePatternTransferHandlerMixin.java
@@ -20,7 +20,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
public abstract class EncodePatternTransferHandlerMixin {
@Inject(method = "transferRecipe", at = @At("HEAD"), require = 0)
- private void extendedae_plus$captureProcessingName(PatternEncodingTermMenu menu,
+ private void eap$captureProcessingName(PatternEncodingTermMenu menu,
Object recipeBase,
IRecipeSlotsView slotsView,
Player player,
From b2b2740864d2d323bd09feedf75e83c0b252f4bd Mon Sep 17 00:00:00 2001
From: GaLicn <133291877+GaLicn@users.noreply.github.com>
Date: Tue, 23 Sep 2025 12:17:13 +0800
Subject: [PATCH 19/34] =?UTF-8?q?=E4=BF=AE=E5=A4=8Dinterface=E5=88=9D?=
=?UTF-8?q?=E5=A7=8B=E5=8C=96=E9=97=AE=E9=A2=98?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../bridge/InterfaceWirelessLinkBridge.java | 28 +++
.../InterfaceLogicChannelCardMixin.java | 215 ++++++++++++++++--
.../helpers/InterfaceLogicTickerMixin.java | 15 ++
.../automation/IOBusPartChannelCardMixin.java | 2 +-
.../StorageBusPartChannelCardMixin.java | 2 +-
5 files changed, 245 insertions(+), 17 deletions(-)
diff --git a/src/main/java/com/extendedae_plus/bridge/InterfaceWirelessLinkBridge.java b/src/main/java/com/extendedae_plus/bridge/InterfaceWirelessLinkBridge.java
index 023ee39..d200f77 100644
--- a/src/main/java/com/extendedae_plus/bridge/InterfaceWirelessLinkBridge.java
+++ b/src/main/java/com/extendedae_plus/bridge/InterfaceWirelessLinkBridge.java
@@ -19,4 +19,32 @@ public interface InterfaceWirelessLinkBridge {
default void eap$setClientWirelessState(boolean connected) {
// 默认实现为空
}
+
+ /**
+ * 检查是否已经进行过tick初始化
+ */
+ default boolean eap$hasTickInitialized() {
+ return true; // 默认认为已初始化
+ }
+
+ /**
+ * 设置tick初始化状态
+ */
+ default void eap$setTickInitialized(boolean initialized) {
+ // 默认实现为空
+ }
+
+ /**
+ * 执行频道链接初始化
+ */
+ default void eap$initializeChannelLink() {
+ // 默认实现为空
+ }
+
+ /**
+ * 检查并处理延迟初始化
+ */
+ default void eap$handleDelayedInit() {
+ // 默认实现为空
+ }
}
diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/helpers/InterfaceLogicChannelCardMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/helpers/InterfaceLogicChannelCardMixin.java
index b8e7401..39cabad 100644
--- a/src/main/java/com/extendedae_plus/mixin/ae2/helpers/InterfaceLogicChannelCardMixin.java
+++ b/src/main/java/com/extendedae_plus/mixin/ae2/helpers/InterfaceLogicChannelCardMixin.java
@@ -6,6 +6,7 @@ import appeng.helpers.InterfaceLogicHost;
import com.extendedae_plus.ae.items.ChannelCardItem;
import com.extendedae_plus.bridge.InterfaceWirelessLinkBridge;
import com.extendedae_plus.init.ModItems;
+import com.extendedae_plus.util.ExtendedAELogger;
import com.extendedae_plus.wireless.IWirelessEndpoint;
import com.extendedae_plus.wireless.WirelessSlaveLink;
import com.extendedae_plus.wireless.endpoint.InterfaceNodeEndpointImpl;
@@ -26,19 +27,63 @@ public abstract class InterfaceLogicChannelCardMixin implements InterfaceWireles
@Shadow(remap = false) public abstract appeng.api.networking.IGridNode getActionableNode();
@Shadow(remap = false) protected InterfaceLogicHost host;
+
+ @Shadow(remap = false) protected appeng.api.networking.IManagedGridNode mainNode;
@Unique
private WirelessSlaveLink eap$link;
+
+ @Unique
+ private long eap$lastChannel = -1;
+
+ @Unique
+ private boolean eap$clientConnected = false;
+
+ @Unique
+ private boolean eap$hasInitialized = false;
+
+ @Unique
+ private int eap$delayedInitTicks = 0;
+
+ static {
+ ExtendedAELogger.LOGGER.info("[服务端] InterfaceLogicChannelCardMixin 已加载");
+ }
@Inject(method = "onUpgradesChanged", at = @At("TAIL"), remap = false)
private void eap$onUpgradesChangedTail(CallbackInfo ci) {
- handleChannelCardChange();
+ // 升级变更时重置标志并尝试初始化
+ eap$lastChannel = -1;
+ eap$hasInitialized = false;
+ eap$initializeChannelLink();
+ }
+
+ @Inject(method = "gridChanged", at = @At("TAIL"), remap = false)
+ private void eap$afterGridChanged(CallbackInfo ci) {
+ // 网格状态变化时重置标志并设置延迟初始化
+ ExtendedAELogger.LOGGER.debug("[服务端] Interface gridChanged 触发,设置延迟初始化");
+ eap$lastChannel = -1;
+ eap$hasInitialized = false;
+ eap$delayedInitTicks = 10; // 适当增加延迟tick,等待网格完成引导
+ // 尝试唤醒设备,确保后续还能继续tick
+ if (mainNode != null) {
+ mainNode.ifPresent((grid, node) -> {
+ try {
+ grid.getTickManager().wakeDevice(node);
+ } catch (Throwable t) {
+ // 防御性日志,避免因这里的异常影响主流程
+ ExtendedAELogger.LOGGER.debug("[服务端] Interface 唤醒设备失败: {}", t.toString());
+ }
+ });
+ }
}
@Inject(method = "readFromNBT", at = @At("TAIL"), remap = false)
private void eap$afterReadNBT(net.minecraft.nbt.CompoundTag tag, CallbackInfo ci) {
- // 重载后根据卡状态恢复连接
- handleChannelCardChange();
+ // 从 NBT加载时重置标志
+ eap$lastChannel = -1;
+ eap$hasInitialized = false;
+ // 直接尝试初始化
+ eap$initializeChannelLink();
}
@Inject(method = "clearContent", at = @At("HEAD"), remap = false)
@@ -49,20 +94,95 @@ public abstract class InterfaceLogicChannelCardMixin implements InterfaceWireles
}
@Unique
- private void handleChannelCardChange() {
- var inv = getUpgrades();
- long channel = 0L;
- for (ItemStack stack : inv) {
- if (!stack.isEmpty() && stack.getItem() == ModItems.CHANNEL_CARD.get()) {
- channel = ChannelCardItem.getChannel(stack);
- break;
+ public void eap$initializeChannelLink() {
+ ExtendedAELogger.LOGGER.debug("[服务端] Interface eap$initializeChannelLink 被调用");
+
+ // 防止在客户端执行
+ if (host.getBlockEntity() != null && host.getBlockEntity().getLevel() != null && host.getBlockEntity().getLevel().isClientSide) {
+ ExtendedAELogger.LOGGER.debug("[服务端] Interface 在客户端,跳过初始化");
+ return;
+ }
+
+ // 检查是否已经初始化过
+ if (eap$hasInitialized) {
+ ExtendedAELogger.LOGGER.debug("[服务端] Interface 已经初始化过,跳过");
+ return;
+ }
+
+ // 优先等待网格完成引导(比仅检查 isActive 更可靠)
+ if (!mainNode.hasGridBooted()) {
+ ExtendedAELogger.LOGGER.debug("[服务端] Interface 网格未完成引导(boot),等待后再初始化: ready={}, active={}, online={}",
+ mainNode.isReady(), mainNode.isActive(), mainNode.isOnline());
+ return;
+ }
+
+ try {
+ var inv = getUpgrades();
+ long channel = 0L;
+ boolean found = false;
+ for (ItemStack stack : inv) {
+ if (!stack.isEmpty() && stack.getItem() == ModItems.CHANNEL_CARD.get()) {
+ channel = ChannelCardItem.getChannel(stack);
+ found = true;
+ break;
+ }
}
+
+ ExtendedAELogger.LOGGER.debug("[服务端] Interface 初始化频道链接: found={}, channel={}", found, channel);
+
+ if (!found) {
+ // 无频道卡则断开
+ if (eap$link != null) {
+ eap$link.setFrequency(0L);
+ eap$link.updateStatus();
+ ExtendedAELogger.LOGGER.debug("[服务端] Interface 断开频道链接");
+ }
+ eap$hasInitialized = true; // 无频道卡也算初始化完成
+ return;
+ }
+
+ if (eap$link == null) {
+ // 使用mainNode而不是getActionableNode,因为后者可能返回null
+ IWirelessEndpoint endpoint = new InterfaceNodeEndpointImpl(host, () -> mainNode.getNode());
+ eap$link = new WirelessSlaveLink(endpoint);
+ ExtendedAELogger.LOGGER.debug("[服务端] Interface 创建新的无线链接");
+ }
+
+ eap$link.setFrequency(channel);
+ eap$link.updateStatus();
+
+ // 调试信息:检查网格节点状态
+ var gridNode = mainNode.getNode();
+ var isActive = mainNode.isActive();
+ ExtendedAELogger.LOGGER.debug("[服务端] Interface 设置频道: {}, 连接状态: {}, 网格节点: {}, 激活: {}, 在线: {}",
+ channel, eap$link.isConnected(),
+ gridNode != null ? "exists" : "null",
+ isActive,
+ gridNode != null ? gridNode.isOnline() : "N/A");
+
+ if (eap$link.isConnected()) {
+ eap$hasInitialized = true; // 设置初始化完成标志
+ ExtendedAELogger.LOGGER.debug("[服务端] Interface 无线链接建立成功");
+ } else {
+ ExtendedAELogger.LOGGER.warn("[服务端] Interface 无线链接建立失败,将继续重试");
+ // 不标记为完成,允许后续tick重试
+ eap$hasInitialized = false;
+ // 设置一个短延迟窗口,避免每tick刷屏
+ eap$delayedInitTicks = Math.max(eap$delayedInitTicks, 5);
+ try {
+ mainNode.ifPresent((grid, node) -> {
+ try {
+ grid.getTickManager().wakeDevice(node);
+ } catch (Throwable t) {
+ ExtendedAELogger.LOGGER.debug("[服务端] Interface 初始化失败后唤醒设备失败: {}", t.toString());
+ }
+ });
+ } catch (Throwable ignored) {
+ }
+ }
+ } catch (Exception e) {
+ ExtendedAELogger.LOGGER.error("[服务端] Interface 初始化频道链接失败", e);
}
- if (eap$link == null) {
- IWirelessEndpoint endpoint = new InterfaceNodeEndpointImpl(host, this::getActionableNode);
- eap$link = new WirelessSlaveLink(endpoint);
- }
- eap$link.setFrequency(channel);
}
@Override
@@ -71,4 +191,69 @@ public abstract class InterfaceLogicChannelCardMixin implements InterfaceWireles
eap$link.updateStatus();
}
}
+
+ @Override
+ public boolean eap$isWirelessConnected() {
+ // InterfaceLogic没有isClientSide方法,需要通过host判断
+ if (host.getBlockEntity() != null && host.getBlockEntity().getLevel() != null && host.getBlockEntity().getLevel().isClientSide) {
+ return eap$clientConnected;
+ } else {
+ return eap$link != null && eap$link.isConnected();
+ }
+ }
+
+ @Override
+ public void eap$setClientWirelessState(boolean connected) {
+ eap$clientConnected = connected;
+ }
+
+ @Override
+ public boolean eap$hasTickInitialized() {
+ return eap$hasInitialized;
+ }
+
+ @Override
+ public void eap$setTickInitialized(boolean initialized) {
+ eap$hasInitialized = initialized;
+ }
+
+ @Override
+ public void eap$handleDelayedInit() {
+ // 仅在服务端执行延迟初始化,避免在渲染线程/客户端触发任何初始化路径
+ if (host.getBlockEntity() != null && host.getBlockEntity().getLevel() != null && host.getBlockEntity().getLevel().isClientSide) {
+ return;
+ }
+
+ // 若尚未初始化,则持续尝试,直到网格完成引导
+ if (!eap$hasInitialized) {
+ if (!mainNode.hasGridBooted()) {
+ // 仍在引导,消耗计时器
+ if (eap$delayedInitTicks > 0) {
+ eap$delayedInitTicks--;
+ }
+ if (eap$delayedInitTicks == 0) {
+ // 重新设定一个短延迟窗口,并唤醒设备,以保证后续还能继续 tick
+ eap$delayedInitTicks = 5;
+ try {
+ mainNode.ifPresent((grid, node) -> {
+ try {
+ grid.getTickManager().wakeDevice(node);
+ } catch (Throwable t) {
+ ExtendedAELogger.LOGGER.debug("[服务端] Interface 延迟等待期间唤醒设备失败: {}", t.toString());
+ }
+ });
+ } catch (Throwable ignored) {
+ }
+ ExtendedAELogger.LOGGER.debug("[服务端] Interface 网格仍在引导,继续等待: ready={}, active={}, online={}",
+ mainNode.isReady(), mainNode.isActive(), mainNode.isOnline());
+ }
+ } else {
+ // 网格已引导完成,执行初始化
+ ExtendedAELogger.LOGGER.debug("[服务端] Interface 延迟初始化触发(网格已完成引导)");
+ eap$initializeChannelLink();
+ }
+ }
+ }
+
+ // eap$initializeChannelLink方法已在上面实现
}
diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/helpers/InterfaceLogicTickerMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/helpers/InterfaceLogicTickerMixin.java
index 50ac299..a2063d5 100644
--- a/src/main/java/com/extendedae_plus/mixin/ae2/helpers/InterfaceLogicTickerMixin.java
+++ b/src/main/java/com/extendedae_plus/mixin/ae2/helpers/InterfaceLogicTickerMixin.java
@@ -2,6 +2,7 @@ package com.extendedae_plus.mixin.ae2.helpers;
import appeng.helpers.InterfaceLogic;
import com.extendedae_plus.bridge.InterfaceWirelessLinkBridge;
+import com.extendedae_plus.util.ExtendedAELogger;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
@@ -20,6 +21,20 @@ public abstract class InterfaceLogicTickerMixin {
@Final
private InterfaceLogic this$0;
+ @Inject(method = "tickingRequest", at = @At("HEAD"), remap = false)
+ private void eap$tickHead(appeng.api.networking.IGridNode node, int ticksSinceLastCall,
+ CallbackInfoReturnable cir) {
+ // 仅在服务端处理延迟初始化,避免客户端干扰
+ if (node != null && node.getLevel() != null && node.getLevel().isClientSide) {
+ return;
+ }
+
+ if (this$0 instanceof InterfaceWirelessLinkBridge bridge) {
+ // 处理延迟初始化
+ bridge.eap$handleDelayedInit();
+ }
+ }
+
@Inject(method = "tickingRequest", at = @At("TAIL"), remap = false)
private void eap$tickTail(appeng.api.networking.IGridNode node, int ticksSinceLastCall,
CallbackInfoReturnable cir) {
diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/parts/automation/IOBusPartChannelCardMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/parts/automation/IOBusPartChannelCardMixin.java
index b09b40d..b00c3e1 100644
--- a/src/main/java/com/extendedae_plus/mixin/ae2/parts/automation/IOBusPartChannelCardMixin.java
+++ b/src/main/java/com/extendedae_plus/mixin/ae2/parts/automation/IOBusPartChannelCardMixin.java
@@ -64,7 +64,7 @@ public abstract class IOBusPartChannelCardMixin implements InterfaceWirelessLink
}
@Unique
- private void eap$initializeChannelLink() {
+ public void eap$initializeChannelLink() {
// 防止重复调用
if (((appeng.parts.AEBasePart)(Object)this).isClientSide()) {
return;
diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/parts/storagebus/StorageBusPartChannelCardMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/parts/storagebus/StorageBusPartChannelCardMixin.java
index 00b6626..50e4a71 100644
--- a/src/main/java/com/extendedae_plus/mixin/ae2/parts/storagebus/StorageBusPartChannelCardMixin.java
+++ b/src/main/java/com/extendedae_plus/mixin/ae2/parts/storagebus/StorageBusPartChannelCardMixin.java
@@ -61,7 +61,7 @@ public abstract class StorageBusPartChannelCardMixin implements InterfaceWireles
}
@Unique
- private void eap$initializeChannelLink() {
+ public void eap$initializeChannelLink() {
// 防止重复调用
if (((appeng.parts.AEBasePart)(Object)this).isClientSide()) {
return;
From 773d1bb26378ac4b218bb49fd24131cbc99adaa2 Mon Sep 17 00:00:00 2001
From: GaLicn <133291877+GaLicn@users.noreply.github.com>
Date: Tue, 23 Sep 2025 12:48:50 +0800
Subject: [PATCH 20/34] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E6=A0=B7=E6=9D=BF?=
=?UTF-8?q?=E4=BE=9B=E5=BA=94=E5=99=A8=E5=88=9D=E5=A7=8B=E5=8C=96=E9=97=AE?=
=?UTF-8?q?=E9=A2=98?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../InterfaceLogicChannelCardMixin.java | 68 ++-----
.../PatternProviderLogicTickerMixin.java | 12 ++
.../PatternProviderLogicUpgradesMixin.java | 175 ++++++++++++++++--
3 files changed, 186 insertions(+), 69 deletions(-)
diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/helpers/InterfaceLogicChannelCardMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/helpers/InterfaceLogicChannelCardMixin.java
index 39cabad..bb4b6cf 100644
--- a/src/main/java/com/extendedae_plus/mixin/ae2/helpers/InterfaceLogicChannelCardMixin.java
+++ b/src/main/java/com/extendedae_plus/mixin/ae2/helpers/InterfaceLogicChannelCardMixin.java
@@ -6,8 +6,6 @@ import appeng.helpers.InterfaceLogicHost;
import com.extendedae_plus.ae.items.ChannelCardItem;
import com.extendedae_plus.bridge.InterfaceWirelessLinkBridge;
import com.extendedae_plus.init.ModItems;
-import com.extendedae_plus.util.ExtendedAELogger;
-import com.extendedae_plus.wireless.IWirelessEndpoint;
import com.extendedae_plus.wireless.WirelessSlaveLink;
import com.extendedae_plus.wireless.endpoint.InterfaceNodeEndpointImpl;
import net.minecraft.server.level.ServerLevel;
@@ -46,7 +44,7 @@ public abstract class InterfaceLogicChannelCardMixin implements InterfaceWireles
private int eap$delayedInitTicks = 0;
static {
- ExtendedAELogger.LOGGER.info("[服务端] InterfaceLogicChannelCardMixin 已加载");
+ // InterfaceLogicChannelCardMixin 已加载
}
@Inject(method = "onUpgradesChanged", at = @At("TAIL"), remap = false)
@@ -60,7 +58,6 @@ public abstract class InterfaceLogicChannelCardMixin implements InterfaceWireles
@Inject(method = "gridChanged", at = @At("TAIL"), remap = false)
private void eap$afterGridChanged(CallbackInfo ci) {
// 网格状态变化时重置标志并设置延迟初始化
- ExtendedAELogger.LOGGER.debug("[服务端] Interface gridChanged 触发,设置延迟初始化");
eap$lastChannel = -1;
eap$hasInitialized = false;
eap$delayedInitTicks = 10; // 适当增加延迟tick,等待网格完成引导
@@ -69,9 +66,7 @@ public abstract class InterfaceLogicChannelCardMixin implements InterfaceWireles
mainNode.ifPresent((grid, node) -> {
try {
grid.getTickManager().wakeDevice(node);
- } catch (Throwable t) {
- // 防御性日志,避免因这里的异常影响主流程
- ExtendedAELogger.LOGGER.debug("[服务端] Interface 唤醒设备失败: {}", t.toString());
+ } catch (Throwable ignored) {
}
});
}
@@ -95,76 +90,53 @@ public abstract class InterfaceLogicChannelCardMixin implements InterfaceWireles
@Unique
public void eap$initializeChannelLink() {
- ExtendedAELogger.LOGGER.debug("[服务端] Interface eap$initializeChannelLink 被调用");
-
- // 防止在客户端执行
+ // 仅在服务端执行,避免在渲染线程/客户端触发任何初始化路径
if (host.getBlockEntity() != null && host.getBlockEntity().getLevel() != null && host.getBlockEntity().getLevel().isClientSide) {
- ExtendedAELogger.LOGGER.debug("[服务端] Interface 在客户端,跳过初始化");
return;
}
-
- // 检查是否已经初始化过
+
+ // 避免重复初始化
if (eap$hasInitialized) {
- ExtendedAELogger.LOGGER.debug("[服务端] Interface 已经初始化过,跳过");
return;
}
-
+
// 优先等待网格完成引导(比仅检查 isActive 更可靠)
if (!mainNode.hasGridBooted()) {
- ExtendedAELogger.LOGGER.debug("[服务端] Interface 网格未完成引导(boot),等待后再初始化: ready={}, active={}, online={}",
- mainNode.isReady(), mainNode.isActive(), mainNode.isOnline());
return;
}
-
+
try {
- var inv = getUpgrades();
long channel = 0L;
boolean found = false;
- for (ItemStack stack : inv) {
+ for (ItemStack stack : getUpgrades()) {
if (!stack.isEmpty() && stack.getItem() == ModItems.CHANNEL_CARD.get()) {
channel = ChannelCardItem.getChannel(stack);
found = true;
break;
}
}
-
- ExtendedAELogger.LOGGER.debug("[服务端] Interface 初始化频道链接: found={}, channel={}", found, channel);
-
+
if (!found) {
- // 无频道卡则断开
+ // 无频道卡:断开并视为初始化完成
if (eap$link != null) {
eap$link.setFrequency(0L);
eap$link.updateStatus();
- ExtendedAELogger.LOGGER.debug("[服务端] Interface 断开频道链接");
}
- eap$hasInitialized = true; // 无频道卡也算初始化完成
+ eap$hasInitialized = true;
return;
}
-
+
if (eap$link == null) {
- // 使用mainNode而不是getActionableNode,因为后者可能返回null
- IWirelessEndpoint endpoint = new InterfaceNodeEndpointImpl(host, () -> mainNode.getNode());
+ var endpoint = new InterfaceNodeEndpointImpl(host, () -> this.mainNode.getNode());
eap$link = new WirelessSlaveLink(endpoint);
- ExtendedAELogger.LOGGER.debug("[服务端] Interface 创建新的无线链接");
}
-
+
eap$link.setFrequency(channel);
eap$link.updateStatus();
- // 调试信息:检查网格节点状态
- var gridNode = mainNode.getNode();
- var isActive = mainNode.isActive();
- ExtendedAELogger.LOGGER.debug("[服务端] Interface 设置频道: {}, 连接状态: {}, 网格节点: {}, 激活: {}, 在线: {}",
- channel, eap$link.isConnected(),
- gridNode != null ? "exists" : "null",
- isActive,
- gridNode != null ? gridNode.isOnline() : "N/A");
-
if (eap$link.isConnected()) {
eap$hasInitialized = true; // 设置初始化完成标志
- ExtendedAELogger.LOGGER.debug("[服务端] Interface 无线链接建立成功");
} else {
- ExtendedAELogger.LOGGER.warn("[服务端] Interface 无线链接建立失败,将继续重试");
// 不标记为完成,允许后续tick重试
eap$hasInitialized = false;
// 设置一个短延迟窗口,避免每tick刷屏
@@ -173,15 +145,13 @@ public abstract class InterfaceLogicChannelCardMixin implements InterfaceWireles
mainNode.ifPresent((grid, node) -> {
try {
grid.getTickManager().wakeDevice(node);
- } catch (Throwable t) {
- ExtendedAELogger.LOGGER.debug("[服务端] Interface 初始化失败后唤醒设备失败: {}", t.toString());
+ } catch (Throwable ignored) {
}
});
} catch (Throwable ignored) {
}
}
- } catch (Exception e) {
- ExtendedAELogger.LOGGER.error("[服务端] Interface 初始化频道链接失败", e);
+ } catch (Exception ignored) {
}
}
@@ -238,18 +208,14 @@ public abstract class InterfaceLogicChannelCardMixin implements InterfaceWireles
mainNode.ifPresent((grid, node) -> {
try {
grid.getTickManager().wakeDevice(node);
- } catch (Throwable t) {
- ExtendedAELogger.LOGGER.debug("[服务端] Interface 延迟等待期间唤醒设备失败: {}", t.toString());
+ } catch (Throwable ignored) {
}
});
} catch (Throwable ignored) {
}
- ExtendedAELogger.LOGGER.debug("[服务端] Interface 网格仍在引导,继续等待: ready={}, active={}, online={}",
- mainNode.isReady(), mainNode.isActive(), mainNode.isOnline());
}
} else {
// 网格已引导完成,执行初始化
- ExtendedAELogger.LOGGER.debug("[服务端] Interface 延迟初始化触发(网格已完成引导)");
eap$initializeChannelLink();
}
}
diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/helpers/patternprovider/PatternProviderLogicTickerMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/helpers/patternprovider/PatternProviderLogicTickerMixin.java
index 8753adb..e799173 100644
--- a/src/main/java/com/extendedae_plus/mixin/ae2/helpers/patternprovider/PatternProviderLogicTickerMixin.java
+++ b/src/main/java/com/extendedae_plus/mixin/ae2/helpers/patternprovider/PatternProviderLogicTickerMixin.java
@@ -20,6 +20,18 @@ public abstract class PatternProviderLogicTickerMixin {
@Final
private PatternProviderLogic this$0;
+ @Inject(method = "tickingRequest", at = @At("HEAD"))
+ private void eap$tickHead(appeng.api.networking.IGridNode node, int ticksSinceLastCall,
+ CallbackInfoReturnable cir) {
+ // 仅在服务端处理延迟初始化
+ if (node != null && node.getLevel() != null && node.getLevel().isClientSide) {
+ return;
+ }
+ if (this$0 instanceof InterfaceWirelessLinkBridge bridge) {
+ bridge.eap$handleDelayedInit();
+ }
+ }
+
@Inject(method = "tickingRequest", at = @At("TAIL"))
private void eap$tickTail(appeng.api.networking.IGridNode node, int ticksSinceLastCall,
CallbackInfoReturnable cir) {
diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/helpers/patternprovider/PatternProviderLogicUpgradesMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/helpers/patternprovider/PatternProviderLogicUpgradesMixin.java
index 7b56bae..1a802cd 100644
--- a/src/main/java/com/extendedae_plus/mixin/ae2/helpers/patternprovider/PatternProviderLogicUpgradesMixin.java
+++ b/src/main/java/com/extendedae_plus/mixin/ae2/helpers/patternprovider/PatternProviderLogicUpgradesMixin.java
@@ -37,6 +37,18 @@ public abstract class PatternProviderLogicUpgradesMixin implements IUpgradeableO
@Unique
private WirelessSlaveLink eap$link;
+ @Unique
+ private long eap$lastChannel = -1;
+
+ @Unique
+ private boolean eap$clientConnected = false;
+
+ @Unique
+ private boolean eap$hasInitialized = false;
+
+ @Unique
+ private int eap$delayedInitTicks = 0;
+
@Final
@Shadow
private PatternProviderLogicHost host;
@@ -52,26 +64,13 @@ public abstract class PatternProviderLogicUpgradesMixin implements IUpgradeableO
@Unique
private void eap$onUpgradesChanged() {
this.host.saveChanges();
- // 读取频道卡,更新无线链接频率
- long channel = 0L;
- for (var stack : this.eap$upgrades) {
- if (!stack.isEmpty() && stack.getItem() == ModItems.CHANNEL_CARD.get()) {
- channel = ChannelCardItem.getChannel(stack);
- break;
- }
- }
- if (eap$link == null) {
- var endpoint = new GenericNodeEndpointImpl(() -> host.getBlockEntity(), () -> this.mainNode.getNode());
- eap$link = new WirelessSlaveLink(endpoint);
- }
- eap$link.setFrequency(channel);
- eap$link.updateStatus();
+ // 升级变更,重置并尝试初始化
+ eap$lastChannel = -1;
+ eap$hasInitialized = false;
+ eap$initializeChannelLink();
}
- @Override
- public IUpgradeInventory getUpgrades() {
- return this.eap$upgrades;
- }
+
@Inject(method = "(Lappeng/api/networking/IManagedGridNode;Lappeng/helpers/patternprovider/PatternProviderLogicHost;I)V",
at = @At("TAIL"))
@@ -87,6 +86,10 @@ public abstract class PatternProviderLogicUpgradesMixin implements IUpgradeableO
@Inject(method = "readFromNBT", at = @At("TAIL"))
private void eap$loadUpgrades(CompoundTag tag, CallbackInfo ci) {
this.eap$upgrades.readFromNBT(tag, "upgrades");
+ // 从 NBT 加载后,重置并尝试初始化(可能刚进入世界)
+ eap$lastChannel = -1;
+ eap$hasInitialized = false;
+ eap$initializeChannelLink();
}
@Inject(method = "addDrops", at = @At("TAIL"))
@@ -109,4 +112,140 @@ public abstract class PatternProviderLogicUpgradesMixin implements IUpgradeableO
eap$link.updateStatus();
}
}
+
+ @Override
+ public IUpgradeInventory getUpgrades() {
+ return this.eap$upgrades;
+ }
+
+ // ===== 频道卡初始化与延迟重试(服务端) =====
+ @Unique
+ public void eap$initializeChannelLink() {
+ // 客户端早退
+ if (host.getBlockEntity() != null && host.getBlockEntity().getLevel() != null && host.getBlockEntity().getLevel().isClientSide) {
+ return;
+ }
+
+ // 避免重复初始化
+ if (eap$hasInitialized) {
+ return;
+ }
+
+ // 等待网格完成引导
+ if (!mainNode.hasGridBooted()) {
+ // 安排短延迟,等待后续 tick 再试
+ eap$delayedInitTicks = Math.max(eap$delayedInitTicks, 5);
+ try {
+ mainNode.ifPresent((grid, node) -> {
+ try { grid.getTickManager().wakeDevice(node); } catch (Throwable ignored) {}
+ });
+ } catch (Throwable ignored) {}
+ return;
+ }
+
+ try {
+ long channel = 0L;
+ boolean found = false;
+ for (ItemStack stack : this.eap$upgrades) {
+ if (!stack.isEmpty() && stack.getItem() == ModItems.CHANNEL_CARD.get()) {
+ channel = ChannelCardItem.getChannel(stack);
+ found = true;
+ break;
+ }
+ }
+
+ if (!found) {
+ // 无频道卡:断开并视为初始化完成
+ if (eap$link != null) {
+ eap$link.setFrequency(0L);
+ eap$link.updateStatus();
+ }
+ eap$hasInitialized = true;
+ return;
+ }
+
+ if (eap$link == null) {
+ var endpoint = new GenericNodeEndpointImpl(() -> host.getBlockEntity(), () -> this.mainNode.getNode());
+ eap$link = new WirelessSlaveLink(endpoint);
+ }
+
+ eap$link.setFrequency(channel);
+ eap$link.updateStatus();
+
+ if (eap$link.isConnected()) {
+ eap$hasInitialized = true;
+ } else {
+ eap$hasInitialized = false;
+ eap$delayedInitTicks = Math.max(eap$delayedInitTicks, 5);
+ try {
+ mainNode.ifPresent((grid, node) -> {
+ try { grid.getTickManager().wakeDevice(node); } catch (Throwable ignored) {}
+ });
+ } catch (Throwable ignored) {}
+ }
+ } catch (Exception ignored) {
+ }
+ }
+
+ @Override
+ public void eap$setClientWirelessState(boolean connected) {
+ eap$clientConnected = connected;
+ }
+
+ @Override
+ public boolean eap$isWirelessConnected() {
+ if (host.getBlockEntity() != null && host.getBlockEntity().getLevel() != null && host.getBlockEntity().getLevel().isClientSide) {
+ return eap$clientConnected;
+ } else {
+ return eap$link != null && eap$link.isConnected();
+ }
+ }
+
+ @Override
+ public boolean eap$hasTickInitialized() {
+ return eap$hasInitialized;
+ }
+
+ @Override
+ public void eap$setTickInitialized(boolean initialized) {
+ eap$hasInitialized = initialized;
+ }
+
+ @Override
+ public void eap$handleDelayedInit() {
+ // 仅服务端
+ if (host.getBlockEntity() != null && host.getBlockEntity().getLevel() != null && host.getBlockEntity().getLevel().isClientSide) {
+ return;
+ }
+ if (!eap$hasInitialized) {
+ if (!mainNode.hasGridBooted()) {
+ if (eap$delayedInitTicks > 0) {
+ eap$delayedInitTicks--;
+ }
+ if (eap$delayedInitTicks == 0) {
+ eap$delayedInitTicks = 5;
+ try {
+ mainNode.ifPresent((grid, node) -> {
+ try { grid.getTickManager().wakeDevice(node); } catch (Throwable ignored) {}
+ });
+ } catch (Throwable ignored) {}
+ }
+ } else {
+ eap$initializeChannelLink();
+ }
+ }
+ }
+
+ // 当主节点状态变化(例如 GRID_BOOT 完成)时,安排一次延迟初始化并唤醒设备
+ @Inject(method = "onMainNodeStateChanged", at = @At("TAIL"))
+ private void eap$onMainNodeStateChangedTail(CallbackInfo ci) {
+ eap$lastChannel = -1;
+ eap$hasInitialized = false;
+ eap$delayedInitTicks = 10;
+ try {
+ mainNode.ifPresent((grid, node) -> {
+ try { grid.getTickManager().wakeDevice(node); } catch (Throwable ignored) {}
+ });
+ } catch (Throwable ignored) {}
+ }
}
From a8b9b8e7490edae86cb9263321d837061ac2569c Mon Sep 17 00:00:00 2001
From: GaLicn <133291877+GaLicn@users.noreply.github.com>
Date: Tue, 23 Sep 2025 16:22:18 +0800
Subject: [PATCH 21/34] =?UTF-8?q?=E4=BE=9D=E8=B5=96?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
build.gradle | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/build.gradle b/build.gradle
index 9c945b4..25fe6a4 100644
--- a/build.gradle
+++ b/build.gradle
@@ -81,7 +81,7 @@ dependencies {
annotationProcessor "org.spongepowered:mixin:${mixin_version}:processor"
- modCompileOnly "curse.maven:applied-flux-965012:${applied_flux_version}"
+ modImplementation "curse.maven:applied-flux-965012:6755986"
modCompileOnly "curse.maven:mega-cells-622112:${mega_cells_version}"
modCompileOnly "curse.maven:jade-324717:${jade_version}"
From 428f4520b2a8d7c23e13c40417ca23e1a8658c19 Mon Sep 17 00:00:00 2001
From: GaLicn <133291877+GaLicn@users.noreply.github.com>
Date: Tue, 23 Sep 2025 17:43:38 +0800
Subject: [PATCH 22/34] =?UTF-8?q?=E5=85=BC=E5=AE=B9applied=20flux?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
build.gradle | 2 +-
.../com/extendedae_plus/ExtendedAEPlus.java | 4 +
.../compat/CompatibilityTest.java | 62 ++++
.../compat/UpgradeSlotCompat.java | 202 +++++++++++
.../mixin/MixinConditions.java | 79 ++++
.../PatternProviderScreenUpgradesMixin.java | 10 +-
.../compat/PatternProviderCompatMixin.java | 69 ++++
.../PatternProviderLogicCompatMixin.java | 337 ++++++++++++++++++
.../PatternProviderLogicHostCompatMixin.java | 27 ++
.../PatternProviderScreenCompatMixin.java | 88 +++++
...PatternProviderLogicHostUpgradesMixin.java | 5 +-
.../PatternProviderLogicUpgradesMixin.java | 23 +-
.../PatternProviderMenuUpgradesMixin.java | 14 +-
.../resources/extendedae_plus.mixins.json | 9 +-
14 files changed, 921 insertions(+), 10 deletions(-)
create mode 100644 src/main/java/com/extendedae_plus/compat/CompatibilityTest.java
create mode 100644 src/main/java/com/extendedae_plus/compat/UpgradeSlotCompat.java
create mode 100644 src/main/java/com/extendedae_plus/mixin/MixinConditions.java
create mode 100644 src/main/java/com/extendedae_plus/mixin/ae2/compat/PatternProviderCompatMixin.java
create mode 100644 src/main/java/com/extendedae_plus/mixin/ae2/compat/PatternProviderLogicCompatMixin.java
create mode 100644 src/main/java/com/extendedae_plus/mixin/ae2/compat/PatternProviderLogicHostCompatMixin.java
create mode 100644 src/main/java/com/extendedae_plus/mixin/ae2/compat/PatternProviderScreenCompatMixin.java
diff --git a/build.gradle b/build.gradle
index 25fe6a4..f22d7c4 100644
--- a/build.gradle
+++ b/build.gradle
@@ -81,7 +81,7 @@ dependencies {
annotationProcessor "org.spongepowered:mixin:${mixin_version}:processor"
- modImplementation "curse.maven:applied-flux-965012:6755986"
+ // modImplementation "curse.maven:applied-flux-965012:6755986"
modCompileOnly "curse.maven:mega-cells-622112:${mega_cells_version}"
modCompileOnly "curse.maven:jade-324717:${jade_version}"
diff --git a/src/main/java/com/extendedae_plus/ExtendedAEPlus.java b/src/main/java/com/extendedae_plus/ExtendedAEPlus.java
index 9d993f8..efc4a7a 100644
--- a/src/main/java/com/extendedae_plus/ExtendedAEPlus.java
+++ b/src/main/java/com/extendedae_plus/ExtendedAEPlus.java
@@ -4,6 +4,7 @@ import appeng.api.storage.StorageCells;
import appeng.menu.locator.MenuLocators;
import com.extendedae_plus.ae.api.storage.InfinityBigIntegerCellHandler;
import com.extendedae_plus.client.ClientRegistrar;
+import com.extendedae_plus.compat.CompatibilityTest;
import com.extendedae_plus.config.ModConfig;
import com.extendedae_plus.init.*;
import com.extendedae_plus.menu.locator.CuriosItemLocator;
@@ -62,6 +63,9 @@ public class ExtendedAEPlus {
private void commonSetup(final FMLCommonSetupEvent event) {
StorageCells.addCellHandler(InfinityBigIntegerCellHandler.INSTANCE);
+ // 运行兼容性测试
+ CompatibilityTest.testCompatibility();
+
// 注册本模组网络通道与数据包
event.enqueueWork(() -> {
// 注册升级卡
diff --git a/src/main/java/com/extendedae_plus/compat/CompatibilityTest.java b/src/main/java/com/extendedae_plus/compat/CompatibilityTest.java
new file mode 100644
index 0000000..50b7687
--- /dev/null
+++ b/src/main/java/com/extendedae_plus/compat/CompatibilityTest.java
@@ -0,0 +1,62 @@
+package com.extendedae_plus.compat;
+
+import com.extendedae_plus.util.ExtendedAELogger;
+import net.minecraftforge.fml.ModList;
+
+/**
+ * 兼容性测试类
+ * 用于验证模组兼容性检测是否正常工作
+ */
+public class CompatibilityTest {
+
+ /**
+ * 测试模组兼容性检测
+ */
+ public static void testCompatibility() {
+ ExtendedAELogger.LOGGER.info("=== ExtendedAE_Plus 兼容性测试开始 ===");
+
+ // 测试appflux模组检测
+ boolean appfluxExists = ModList.get().isLoaded("appflux");
+ ExtendedAELogger.LOGGER.info("ExtendedAE-appflux模组检测结果: {}", appfluxExists ? "存在" : "不存在");
+
+ // 测试升级卡槽功能启用状态
+ boolean shouldEnableUpgrades = UpgradeSlotCompat.shouldEnableUpgradeSlots();
+ ExtendedAELogger.LOGGER.info("升级卡槽功能启用状态: {}", shouldEnableUpgrades ? "启用" : "禁用");
+
+ // 测试Screen升级面板添加状态
+ boolean shouldAddPanel = UpgradeSlotCompat.shouldAddUpgradePanelToScreen();
+ ExtendedAELogger.LOGGER.info("Screen升级面板添加状态: {}", shouldAddPanel ? "启用" : "禁用");
+
+ // 输出兼容性策略
+ if (appfluxExists) {
+ ExtendedAELogger.LOGGER.info("兼容性策略: 检测到ExtendedAE-appflux模组,将使用其升级卡槽功能");
+ } else {
+ ExtendedAELogger.LOGGER.info("兼容性策略: 未检测到ExtendedAE-appflux模组,将使用我们自己的升级卡槽功能");
+ }
+
+ ExtendedAELogger.LOGGER.info("=== ExtendedAE_Plus 兼容性测试完成 ===");
+ }
+
+ /**
+ * 获取兼容性状态报告
+ */
+ public static String getCompatibilityReport() {
+ boolean appfluxExists = ModList.get().isLoaded("appflux");
+ boolean upgradesEnabled = UpgradeSlotCompat.shouldEnableUpgradeSlots();
+
+ StringBuilder report = new StringBuilder();
+ report.append("ExtendedAE_Plus 兼容性报告:\n");
+ report.append("- ExtendedAE-appflux模组: ").append(appfluxExists ? "已安装" : "未安装").append("\n");
+ report.append("- 升级卡槽功能: ").append(upgradesEnabled ? "启用中" : "已禁用").append("\n");
+
+ if (appfluxExists && !upgradesEnabled) {
+ report.append("- 兼容性状态: 正常 (使用appflux的升级功能)\n");
+ } else if (!appfluxExists && upgradesEnabled) {
+ report.append("- 兼容性状态: 正常 (使用我们的升级功能)\n");
+ } else {
+ report.append("- 兼容性状态: 异常 (配置不一致)\n");
+ }
+
+ return report.toString();
+ }
+}
diff --git a/src/main/java/com/extendedae_plus/compat/UpgradeSlotCompat.java b/src/main/java/com/extendedae_plus/compat/UpgradeSlotCompat.java
new file mode 100644
index 0000000..6c39295
--- /dev/null
+++ b/src/main/java/com/extendedae_plus/compat/UpgradeSlotCompat.java
@@ -0,0 +1,202 @@
+package com.extendedae_plus.compat;
+
+import appeng.api.upgrades.IUpgradeInventory;
+import appeng.api.upgrades.IUpgradeableObject;
+import appeng.api.upgrades.UpgradeInventories;
+import appeng.client.gui.style.ScreenStyle;
+import appeng.client.gui.widgets.ToolboxPanel;
+import appeng.client.gui.widgets.UpgradesPanel;
+import appeng.core.localization.GuiText;
+import appeng.helpers.patternprovider.PatternProviderLogicHost;
+import appeng.menu.AEBaseMenu;
+import appeng.menu.SlotSemantics;
+import appeng.menu.ToolboxMenu;
+import com.extendedae_plus.util.ExtendedAELogger;
+import net.minecraft.network.chat.Component;
+import net.minecraft.world.inventory.Slot;
+import net.minecraftforge.fml.ModList;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 升级卡槽兼容性管理类
+ * 检测ExtendedAE-appflux模组是否存在,如果存在则使用其升级卡槽功能
+ * 否则使用我们自己的实现
+ */
+public class UpgradeSlotCompat {
+ private static final String APPFLUX_MOD_ID = "appflux";
+
+ /**
+ * 检测是否应该启用我们的升级卡槽功能
+ * @return true如果应该启用,false如果检测到appflux模组存在
+ */
+ public static boolean shouldEnableUpgradeSlots() {
+ boolean appfluxExists = ModList.get().isLoaded(APPFLUX_MOD_ID);
+ ExtendedAELogger.LOGGER.info("ExtendedAE-appflux模组检测: {}", appfluxExists ? "存在" : "不存在");
+
+ if (appfluxExists) {
+ ExtendedAELogger.LOGGER.info("检测到ExtendedAE-appflux模组,跳过我们的升级卡槽功能");
+ return false;
+ } else {
+ ExtendedAELogger.LOGGER.info("未检测到ExtendedAE-appflux模组,启用我们的升级卡槽功能");
+ return true;
+ }
+ }
+
+ /**
+ * 检测是否应该在Screen中添加升级面板
+ * @return true如果应该添加,false如果检测到appflux模组存在
+ */
+ public static boolean shouldAddUpgradePanelToScreen() {
+ return shouldEnableUpgradeSlots();
+ }
+
+ /**
+ * 初始化菜单升级功能(如果需要的话)
+ * @param menu 目标菜单
+ * @param host 样板供应器逻辑主机
+ * @return 是否成功初始化
+ */
+ public static boolean initMenuUpgrades(AEBaseMenu menu, PatternProviderLogicHost host) {
+ if (!shouldEnableUpgradeSlots()) {
+ return false;
+ }
+
+ try {
+ // 创建工具箱菜单
+ ToolboxMenu toolbox = new ToolboxMenu(menu);
+
+ // 设置升级槽
+ if (host instanceof IUpgradeableObject upgradeableHost) {
+ // 使用反射调用protected的setupUpgrades方法
+ try {
+ var setupUpgradesMethod = AEBaseMenu.class.getDeclaredMethod("setupUpgrades", IUpgradeInventory.class);
+ setupUpgradesMethod.setAccessible(true);
+ setupUpgradesMethod.invoke(menu, upgradeableHost.getUpgrades());
+ } catch (Exception e) {
+ ExtendedAELogger.LOGGER.error("反射调用setupUpgrades失败", e);
+ return false;
+ }
+
+ // 使用反射或接口设置工具箱
+ if (menu instanceof IUpgradeableMenuCompat compatMenu) {
+ compatMenu.setCompatToolbox(toolbox);
+ }
+
+ ExtendedAELogger.LOGGER.debug("成功为PatternProviderMenu初始化升级功能");
+ return true;
+ }
+ } catch (Exception e) {
+ ExtendedAELogger.LOGGER.error("初始化PatternProviderMenu升级功能时出错", e);
+ }
+
+ return false;
+ }
+
+ /**
+ * 为Screen添加升级面板(如果需要的话)
+ * @param widgets 小部件映射
+ * @param menu 菜单实例
+ * @param style 屏幕样式
+ * @return 是否成功添加
+ */
+ public static boolean addUpgradePanelToScreen(Object widgets, Object menu, ScreenStyle style) {
+ if (!shouldAddUpgradePanelToScreen()) {
+ return false;
+ }
+
+ try {
+ if (menu instanceof IUpgradeableMenuCompat compatMenu) {
+ try {
+ // 使用反射获取widgets的add方法 - 尝试不同的方法签名
+ var widgetsClass = widgets.getClass();
+ var addMethod = widgetsClass.getDeclaredMethod("add", String.class, Object.class);
+ addMethod.setAccessible(true);
+
+ // 获取升级槽位
+ var menuClass = menu.getClass();
+ var getSlotsMethod = menuClass.getMethod("getSlots", SlotSemantics.class);
+ @SuppressWarnings("unchecked")
+ List upgradeSlots = (List) getSlotsMethod.invoke(menu, SlotSemantics.UPGRADE);
+
+ // 添加升级面板
+ UpgradesPanel upgradesPanel = new UpgradesPanel(upgradeSlots, () -> getCompatibleUpgrades(compatMenu));
+ addMethod.invoke(widgets, "upgrades", upgradesPanel);
+
+ // 添加工具箱面板(如果存在)
+ ToolboxMenu toolbox = compatMenu.getCompatToolbox();
+ if (toolbox != null && toolbox.isPresent()) {
+ ToolboxPanel toolboxPanel = new ToolboxPanel(style, toolbox.getName());
+ addMethod.invoke(widgets, "toolbox", toolboxPanel);
+ }
+
+ ExtendedAELogger.LOGGER.debug("成功为PatternProviderScreen添加升级面板");
+ return true;
+ } catch (NoSuchMethodException e) {
+ // 尝试其他可能的方法签名
+ try {
+ var widgetsClass = widgets.getClass();
+ var putMethod = widgetsClass.getDeclaredMethod("put", String.class, Object.class);
+ putMethod.setAccessible(true);
+
+ // 获取升级槽位
+ var menuClass = menu.getClass();
+ var getSlotsMethod = menuClass.getMethod("getSlots", SlotSemantics.class);
+ @SuppressWarnings("unchecked")
+ List upgradeSlots = (List) getSlotsMethod.invoke(menu, SlotSemantics.UPGRADE);
+
+ // 添加升级面板
+ UpgradesPanel upgradesPanel = new UpgradesPanel(upgradeSlots, () -> getCompatibleUpgrades(compatMenu));
+ putMethod.invoke(widgets, "upgrades", upgradesPanel);
+
+ // 添加工具箱面板(如果存在)
+ ToolboxMenu toolbox = compatMenu.getCompatToolbox();
+ if (toolbox != null && toolbox.isPresent()) {
+ ToolboxPanel toolboxPanel = new ToolboxPanel(style, toolbox.getName());
+ putMethod.invoke(widgets, "toolbox", toolboxPanel);
+ }
+
+ ExtendedAELogger.LOGGER.debug("成功为PatternProviderScreen添加升级面板(使用put方法)");
+ return true;
+ } catch (Exception e2) {
+ ExtendedAELogger.LOGGER.error("反射调用widgets方法失败", e2);
+ return false;
+ }
+ }
+ }
+ } catch (Exception e) {
+ ExtendedAELogger.LOGGER.error("为PatternProviderScreen添加升级面板时出错", e);
+ }
+
+ return false;
+ }
+
+ /**
+ * 获取兼容的升级列表
+ */
+ private static List getCompatibleUpgrades(IUpgradeableMenuCompat menu) {
+ var list = new ArrayList();
+ list.add(GuiText.CompatibleUpgrades.text());
+
+ try {
+ IUpgradeInventory upgrades = menu.getCompatUpgrades();
+ if (upgrades != null) {
+ list.addAll(appeng.api.upgrades.Upgrades.getTooltipLinesForMachine(upgrades.getUpgradableItem()));
+ }
+ } catch (Exception e) {
+ ExtendedAELogger.LOGGER.error("获取兼容升级列表时出错", e);
+ }
+
+ return list;
+ }
+
+ /**
+ * 兼容性升级菜单接口
+ */
+ public interface IUpgradeableMenuCompat {
+ ToolboxMenu getCompatToolbox();
+ void setCompatToolbox(ToolboxMenu toolbox);
+ IUpgradeInventory getCompatUpgrades();
+ }
+}
diff --git a/src/main/java/com/extendedae_plus/mixin/MixinConditions.java b/src/main/java/com/extendedae_plus/mixin/MixinConditions.java
new file mode 100644
index 0000000..ed45cbe
--- /dev/null
+++ b/src/main/java/com/extendedae_plus/mixin/MixinConditions.java
@@ -0,0 +1,79 @@
+package com.extendedae_plus.mixin;
+
+import net.minecraftforge.fml.ModList;
+import org.spongepowered.asm.mixin.extensibility.IMixinConfigPlugin;
+import org.spongepowered.asm.mixin.extensibility.IMixinInfo;
+
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Mixin条件加载插件
+ * 用于根据模组存在情况动态加载不同的Mixin
+ */
+public class MixinConditions implements IMixinConfigPlugin {
+
+ @Override
+ public void onLoad(String mixinPackage) {
+ // 初始化时调用
+ }
+
+ @Override
+ public String getRefMapperConfig() {
+ return null;
+ }
+
+ @Override
+ public boolean shouldApplyMixin(String targetClassName, String mixinClassName) {
+ // 对于升级相关的Mixin,检查appflux是否存在
+ if (mixinClassName.contains("PatternProviderMenuUpgradesMixin") ||
+ mixinClassName.contains("PatternProviderScreenUpgradesMixin") ||
+ mixinClassName.contains("PatternProviderLogicUpgradesMixin") ||
+ mixinClassName.contains("PatternProviderLogicHostUpgradesMixin")) {
+
+ try {
+ // 检查ModList是否已初始化
+ if (net.minecraftforge.fml.ModList.get() == null) {
+ System.out.println("[ExtendedAE_Plus] ModList未初始化,默认应用升级Mixin: " + mixinClassName);
+ return true; // 修改策略:未初始化时默认应用,运行时再检查
+ }
+
+ boolean appfluxExists = net.minecraftforge.fml.ModList.get().isLoaded("appflux");
+ boolean shouldApply = !appfluxExists;
+
+ System.out.println("[ExtendedAE_Plus] 升级Mixin检查: " + mixinClassName +
+ ", appflux存在: " + appfluxExists +
+ ", 应用Mixin: " + shouldApply);
+
+ return shouldApply;
+ } catch (Exception e) {
+ System.out.println("[ExtendedAE_Plus] ModList检查失败,默认应用升级Mixin: " + mixinClassName);
+ return true; // 修改策略:出错时默认应用,运行时再检查
+ }
+ }
+
+ // 其他Mixin正常应用
+ System.out.println("[ExtendedAE_Plus] 加载Mixin: " + mixinClassName);
+ return true;
+ }
+
+ @Override
+ public void acceptTargets(Set myTargets, Set otherTargets) {
+ // 接受目标类
+ }
+
+ @Override
+ public List getMixins() {
+ return null;
+ }
+
+ @Override
+ public void preApply(String targetClassName, org.objectweb.asm.tree.ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) {
+ // 应用前调用
+ }
+
+ @Override
+ public void postApply(String targetClassName, org.objectweb.asm.tree.ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) {
+ // 应用后调用
+ }
+}
diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/client/gui/PatternProviderScreenUpgradesMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/client/gui/PatternProviderScreenUpgradesMixin.java
index 9e84b7f..d02b4f9 100644
--- a/src/main/java/com/extendedae_plus/mixin/ae2/client/gui/PatternProviderScreenUpgradesMixin.java
+++ b/src/main/java/com/extendedae_plus/mixin/ae2/client/gui/PatternProviderScreenUpgradesMixin.java
@@ -10,6 +10,7 @@ import appeng.core.localization.GuiText;
import appeng.menu.SlotSemantics;
import appeng.menu.implementations.PatternProviderMenu;
import com.extendedae_plus.bridge.IUpgradableMenu;
+import com.extendedae_plus.compat.UpgradeSlotCompat;
import net.minecraft.network.chat.Component;
import net.minecraft.world.entity.player.Inventory;
import org.spongepowered.asm.mixin.Mixin;
@@ -21,15 +22,20 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import java.util.ArrayList;
import java.util.List;
-@Mixin(value = PatternProviderScreen.class, remap = false)
+@Mixin(value = PatternProviderScreen.class, priority = 2000, remap = false)
public abstract class PatternProviderScreenUpgradesMixin extends AEBaseScreen {
@Inject(method = "", at = @At("TAIL"))
private void eap$initUpgrades(PatternProviderMenu menu, Inventory playerInventory, Component title, ScreenStyle style, CallbackInfo ci) {
+ // 只有在应该启用升级卡槽时才添加升级面板
+ if (!UpgradeSlotCompat.shouldAddUpgradePanelToScreen()) {
+ return;
+ }
+
this.widgets.add("upgrades", new UpgradesPanel(
menu.getSlots(SlotSemantics.UPGRADE),
this::eap$getCompatibleUpgrades));
- if (((IUpgradableMenu) menu).getToolbox().isPresent()) {
+ if (((IUpgradableMenu) menu).getToolbox() != null && ((IUpgradableMenu) menu).getToolbox().isPresent()) {
this.widgets.add("toolbox", new ToolboxPanel(style, ((IUpgradableMenu) menu).getToolbox().getName()));
}
}
diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/compat/PatternProviderCompatMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/compat/PatternProviderCompatMixin.java
new file mode 100644
index 0000000..edba48a
--- /dev/null
+++ b/src/main/java/com/extendedae_plus/mixin/ae2/compat/PatternProviderCompatMixin.java
@@ -0,0 +1,69 @@
+package com.extendedae_plus.mixin.ae2.compat;
+
+import appeng.api.upgrades.IUpgradeInventory;
+import appeng.helpers.patternprovider.PatternProviderLogicHost;
+import appeng.menu.AEBaseMenu;
+import appeng.menu.ToolboxMenu;
+import appeng.menu.implementations.PatternProviderMenu;
+import com.extendedae_plus.compat.UpgradeSlotCompat;
+import net.minecraft.world.entity.player.Inventory;
+import net.minecraft.world.inventory.MenuType;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.Unique;
+import org.spongepowered.asm.mixin.injection.At;
+import org.spongepowered.asm.mixin.injection.Inject;
+import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
+
+/**
+ * PatternProviderMenu的兼容性Mixin
+ * 优先级设置为500,低于appflux的默认优先级,避免冲突
+ */
+@Mixin(value = PatternProviderMenu.class, priority = 500, remap = false)
+public abstract class PatternProviderCompatMixin extends AEBaseMenu implements UpgradeSlotCompat.IUpgradeableMenuCompat {
+
+ @Unique
+ private ToolboxMenu eap$compatToolbox;
+
+ @Unique
+ private IUpgradeInventory eap$compatUpgrades;
+
+ @Inject(method = "(Lnet/minecraft/world/inventory/MenuType;ILnet/minecraft/world/entity/player/Inventory;Lappeng/helpers/patternprovider/PatternProviderLogicHost;)V",
+ at = @At("TAIL"))
+ private void eap$initCompatUpgrades(MenuType> menuType, int id, Inventory playerInventory, PatternProviderLogicHost host, CallbackInfo ci) {
+ try {
+ // 检测是否应该启用升级卡槽功能
+ if (UpgradeSlotCompat.shouldEnableUpgradeSlots()) {
+ // 直接初始化升级功能
+ this.eap$compatToolbox = new ToolboxMenu(this);
+
+ if (host instanceof appeng.api.upgrades.IUpgradeableObject upgradeableHost) {
+ this.eap$compatUpgrades = upgradeableHost.getUpgrades();
+ this.setupUpgrades(this.eap$compatUpgrades);
+ }
+ }
+ } catch (Exception e) {
+ // 静默处理异常,确保不会因为升级功能导致崩溃
+ com.extendedae_plus.util.ExtendedAELogger.LOGGER.error("PatternProviderMenu兼容性升级初始化失败", e);
+ }
+ }
+
+ @Override
+ public ToolboxMenu getCompatToolbox() {
+ return this.eap$compatToolbox;
+ }
+
+ @Override
+ public void setCompatToolbox(ToolboxMenu toolbox) {
+ this.eap$compatToolbox = toolbox;
+ }
+
+ @Override
+ public IUpgradeInventory getCompatUpgrades() {
+ return this.eap$compatUpgrades;
+ }
+
+ // 构造函数,Mixin要求
+ public PatternProviderCompatMixin(MenuType> menuType, int id, Inventory playerInventory, Object host) {
+ super(menuType, id, playerInventory, host);
+ }
+}
diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/compat/PatternProviderLogicCompatMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/compat/PatternProviderLogicCompatMixin.java
new file mode 100644
index 0000000..0945b85
--- /dev/null
+++ b/src/main/java/com/extendedae_plus/mixin/ae2/compat/PatternProviderLogicCompatMixin.java
@@ -0,0 +1,337 @@
+package com.extendedae_plus.mixin.ae2.compat;
+
+import appeng.api.networking.IManagedGridNode;
+import appeng.api.networking.security.IActionSource;
+import appeng.api.upgrades.IUpgradeInventory;
+import appeng.api.upgrades.IUpgradeableObject;
+import appeng.api.upgrades.UpgradeInventories;
+import appeng.helpers.patternprovider.PatternProviderLogic;
+import appeng.helpers.patternprovider.PatternProviderLogicHost;
+import com.extendedae_plus.ae.items.ChannelCardItem;
+import com.extendedae_plus.bridge.InterfaceWirelessLinkBridge;
+import com.extendedae_plus.compat.UpgradeSlotCompat;
+import com.extendedae_plus.init.ModItems;
+import com.extendedae_plus.wireless.WirelessSlaveLink;
+import com.extendedae_plus.wireless.endpoint.GenericNodeEndpointImpl;
+import net.minecraft.nbt.CompoundTag;
+import net.minecraft.world.item.ItemStack;
+import org.spongepowered.asm.mixin.Final;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.Shadow;
+import org.spongepowered.asm.mixin.Unique;
+import org.spongepowered.asm.mixin.injection.At;
+import org.spongepowered.asm.mixin.injection.Inject;
+import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
+
+import java.util.List;
+
+/**
+ * PatternProviderLogic的兼容性Mixin
+ * 优先级设置为500,避免与appflux冲突
+ */
+@Mixin(value = PatternProviderLogic.class, priority = 500, remap = false)
+public abstract class PatternProviderLogicCompatMixin implements IUpgradeableObject, InterfaceWirelessLinkBridge {
+
+ @Unique
+ private IUpgradeInventory eap$compatUpgrades = UpgradeInventories.empty();
+
+ @Unique
+ private WirelessSlaveLink eap$compatLink;
+
+ @Unique
+ private long eap$compatLastChannel = -1;
+
+ @Unique
+ private boolean eap$compatClientConnected = false;
+
+ @Unique
+ private boolean eap$compatHasInitialized = false;
+
+ @Unique
+ private int eap$compatDelayedInitTicks = 0;
+
+ @Final
+ @Shadow
+ private PatternProviderLogicHost host;
+
+ @Final
+ @Shadow
+ private IManagedGridNode mainNode;
+
+ @Final
+ @Shadow
+ private IActionSource actionSource;
+
+ @Unique
+ private void eap$compatOnUpgradesChanged() {
+ if (!UpgradeSlotCompat.shouldEnableUpgradeSlots()) {
+ return;
+ }
+
+ try {
+ this.host.saveChanges();
+ // 升级变更,重置并尝试初始化
+ eap$compatLastChannel = -1;
+ eap$compatHasInitialized = false;
+ eap$compatInitializeChannelLink();
+ } catch (Exception e) {
+ com.extendedae_plus.util.ExtendedAELogger.LOGGER.error("兼容性升级变更处理失败", e);
+ }
+ }
+
+ @Inject(method = "(Lappeng/api/networking/IManagedGridNode;Lappeng/helpers/patternprovider/PatternProviderLogicHost;I)V",
+ at = @At("TAIL"))
+ private void eap$compatInitUpgrades(IManagedGridNode mainNode, PatternProviderLogicHost host, int patternInventorySize, CallbackInfo ci) {
+ try {
+ if (UpgradeSlotCompat.shouldEnableUpgradeSlots()) {
+ this.eap$compatUpgrades = UpgradeInventories.forMachine(
+ host.getTerminalIcon().getItem(),
+ 1,
+ this::eap$compatOnUpgradesChanged
+ );
+ }
+ } catch (Exception e) {
+ com.extendedae_plus.util.ExtendedAELogger.LOGGER.error("兼容性升级初始化失败", e);
+ }
+ }
+
+ @Inject(method = "writeToNBT", at = @At("TAIL"))
+ private void eap$compatSaveUpgrades(CompoundTag tag, CallbackInfo ci) {
+ try {
+ if (UpgradeSlotCompat.shouldEnableUpgradeSlots()) {
+ this.eap$compatUpgrades.writeToNBT(tag, "compat_upgrades");
+ }
+ } catch (Exception e) {
+ com.extendedae_plus.util.ExtendedAELogger.LOGGER.error("兼容性升级保存失败", e);
+ }
+ }
+
+ @Inject(method = "readFromNBT", at = @At("TAIL"))
+ private void eap$compatLoadUpgrades(CompoundTag tag, CallbackInfo ci) {
+ try {
+ if (UpgradeSlotCompat.shouldEnableUpgradeSlots()) {
+ this.eap$compatUpgrades.readFromNBT(tag, "compat_upgrades");
+ // 从 NBT 加载后,重置并尝试初始化
+ eap$compatLastChannel = -1;
+ eap$compatHasInitialized = false;
+ eap$compatInitializeChannelLink();
+ }
+ } catch (Exception e) {
+ com.extendedae_plus.util.ExtendedAELogger.LOGGER.error("兼容性升级加载失败", e);
+ }
+ }
+
+ @Inject(method = "addDrops", at = @At("TAIL"))
+ private void eap$compatDropUpgrades(List drops, CallbackInfo ci) {
+ try {
+ if (UpgradeSlotCompat.shouldEnableUpgradeSlots()) {
+ for (var stack : this.eap$compatUpgrades) {
+ if (!stack.isEmpty()) {
+ drops.add(stack);
+ }
+ }
+ }
+ } catch (Exception e) {
+ com.extendedae_plus.util.ExtendedAELogger.LOGGER.error("兼容性升级掉落失败", e);
+ }
+ }
+
+ @Inject(method = "clearContent", at = @At("TAIL"))
+ private void eap$compatClearUpgrades(CallbackInfo ci) {
+ try {
+ if (UpgradeSlotCompat.shouldEnableUpgradeSlots()) {
+ this.eap$compatUpgrades.clear();
+ }
+ } catch (Exception e) {
+ com.extendedae_plus.util.ExtendedAELogger.LOGGER.error("兼容性升级清理失败", e);
+ }
+ }
+
+ @Override
+ public IUpgradeInventory getUpgrades() {
+ if (UpgradeSlotCompat.shouldEnableUpgradeSlots()) {
+ return this.eap$compatUpgrades;
+ }
+ return UpgradeInventories.empty();
+ }
+
+ @Override
+ public void eap$updateWirelessLink() {
+ if (!UpgradeSlotCompat.shouldEnableUpgradeSlots()) {
+ return;
+ }
+
+ try {
+ if (eap$compatLink != null) {
+ eap$compatLink.updateStatus();
+ }
+ } catch (Exception e) {
+ com.extendedae_plus.util.ExtendedAELogger.LOGGER.error("兼容性无线链接更新失败", e);
+ }
+ }
+
+ @Unique
+ public void eap$compatInitializeChannelLink() {
+ if (!UpgradeSlotCompat.shouldEnableUpgradeSlots()) {
+ return;
+ }
+
+ try {
+ // 客户端早退
+ if (host.getBlockEntity() != null && host.getBlockEntity().getLevel() != null && host.getBlockEntity().getLevel().isClientSide) {
+ return;
+ }
+
+ // 避免重复初始化
+ if (eap$compatHasInitialized) {
+ return;
+ }
+
+ // 等待网格完成引导
+ if (!mainNode.hasGridBooted()) {
+ eap$compatDelayedInitTicks = Math.max(eap$compatDelayedInitTicks, 5);
+ try {
+ mainNode.ifPresent((grid, node) -> {
+ try { grid.getTickManager().wakeDevice(node); } catch (Throwable ignored) {}
+ });
+ } catch (Throwable ignored) {}
+ return;
+ }
+
+ long channel = 0L;
+ boolean found = false;
+ for (ItemStack stack : this.eap$compatUpgrades) {
+ if (!stack.isEmpty() && stack.getItem() == ModItems.CHANNEL_CARD.get()) {
+ channel = ChannelCardItem.getChannel(stack);
+ found = true;
+ break;
+ }
+ }
+
+ if (!found) {
+ // 无频道卡:断开并视为初始化完成
+ if (eap$compatLink != null) {
+ eap$compatLink.setFrequency(0L);
+ eap$compatLink.updateStatus();
+ }
+ eap$compatHasInitialized = true;
+ return;
+ }
+
+ if (eap$compatLink == null) {
+ var endpoint = new GenericNodeEndpointImpl(() -> host.getBlockEntity(), () -> this.mainNode.getNode());
+ eap$compatLink = new WirelessSlaveLink(endpoint);
+ }
+
+ eap$compatLink.setFrequency(channel);
+ eap$compatLink.updateStatus();
+
+ if (eap$compatLink.isConnected()) {
+ eap$compatHasInitialized = true;
+ } else {
+ eap$compatHasInitialized = false;
+ eap$compatDelayedInitTicks = Math.max(eap$compatDelayedInitTicks, 5);
+ try {
+ mainNode.ifPresent((grid, node) -> {
+ try { grid.getTickManager().wakeDevice(node); } catch (Throwable ignored) {}
+ });
+ } catch (Throwable ignored) {}
+ }
+ } catch (Exception e) {
+ com.extendedae_plus.util.ExtendedAELogger.LOGGER.error("兼容性频道链接初始化失败", e);
+ }
+ }
+
+ @Override
+ public void eap$setClientWirelessState(boolean connected) {
+ if (UpgradeSlotCompat.shouldEnableUpgradeSlots()) {
+ eap$compatClientConnected = connected;
+ }
+ }
+
+ @Override
+ public boolean eap$isWirelessConnected() {
+ if (!UpgradeSlotCompat.shouldEnableUpgradeSlots()) {
+ return false;
+ }
+
+ try {
+ if (host.getBlockEntity() != null && host.getBlockEntity().getLevel() != null && host.getBlockEntity().getLevel().isClientSide) {
+ return eap$compatClientConnected;
+ } else {
+ return eap$compatLink != null && eap$compatLink.isConnected();
+ }
+ } catch (Exception e) {
+ com.extendedae_plus.util.ExtendedAELogger.LOGGER.error("检查兼容性无线连接状态失败", e);
+ return false;
+ }
+ }
+
+ @Override
+ public boolean eap$hasTickInitialized() {
+ if (UpgradeSlotCompat.shouldEnableUpgradeSlots()) {
+ return eap$compatHasInitialized;
+ }
+ return true;
+ }
+
+ @Override
+ public void eap$setTickInitialized(boolean initialized) {
+ if (UpgradeSlotCompat.shouldEnableUpgradeSlots()) {
+ eap$compatHasInitialized = initialized;
+ }
+ }
+
+ @Override
+ public void eap$handleDelayedInit() {
+ if (!UpgradeSlotCompat.shouldEnableUpgradeSlots()) {
+ return;
+ }
+
+ try {
+ // 仅服务端
+ if (host.getBlockEntity() != null && host.getBlockEntity().getLevel() != null && host.getBlockEntity().getLevel().isClientSide) {
+ return;
+ }
+ if (!eap$compatHasInitialized) {
+ if (!mainNode.hasGridBooted()) {
+ if (eap$compatDelayedInitTicks > 0) {
+ eap$compatDelayedInitTicks--;
+ }
+ if (eap$compatDelayedInitTicks == 0) {
+ eap$compatDelayedInitTicks = 5;
+ try {
+ mainNode.ifPresent((grid, node) -> {
+ try { grid.getTickManager().wakeDevice(node); } catch (Throwable ignored) {}
+ });
+ } catch (Throwable ignored) {}
+ }
+ } else {
+ eap$compatInitializeChannelLink();
+ }
+ }
+ } catch (Exception e) {
+ com.extendedae_plus.util.ExtendedAELogger.LOGGER.error("兼容性延迟初始化失败", e);
+ }
+ }
+
+ @Inject(method = "onMainNodeStateChanged", at = @At("TAIL"))
+ private void eap$compatOnMainNodeStateChangedTail(CallbackInfo ci) {
+ if (!UpgradeSlotCompat.shouldEnableUpgradeSlots()) {
+ return;
+ }
+
+ try {
+ eap$compatLastChannel = -1;
+ eap$compatHasInitialized = false;
+ eap$compatDelayedInitTicks = 10;
+ try {
+ mainNode.ifPresent((grid, node) -> {
+ try { grid.getTickManager().wakeDevice(node); } catch (Throwable ignored) {}
+ });
+ } catch (Throwable ignored) {}
+ } catch (Exception e) {
+ com.extendedae_plus.util.ExtendedAELogger.LOGGER.error("兼容性主节点状态变更处理失败", e);
+ }
+ }
+}
diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/compat/PatternProviderLogicHostCompatMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/compat/PatternProviderLogicHostCompatMixin.java
new file mode 100644
index 0000000..00b5c01
--- /dev/null
+++ b/src/main/java/com/extendedae_plus/mixin/ae2/compat/PatternProviderLogicHostCompatMixin.java
@@ -0,0 +1,27 @@
+package com.extendedae_plus.mixin.ae2.compat;
+
+import appeng.api.upgrades.IUpgradeInventory;
+import appeng.api.upgrades.IUpgradeableObject;
+import appeng.api.upgrades.UpgradeInventories;
+import appeng.helpers.patternprovider.PatternProviderLogic;
+import appeng.helpers.patternprovider.PatternProviderLogicHost;
+import com.extendedae_plus.compat.UpgradeSlotCompat;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.Shadow;
+
+/**
+ * PatternProviderLogicHost的兼容性Mixin
+ * 优先级设置为500,避免与appflux冲突
+ */
+@Mixin(value = PatternProviderLogicHost.class, priority = 500, remap = false)
+public interface PatternProviderLogicHostCompatMixin extends IUpgradeableObject {
+ @Shadow PatternProviderLogic getLogic();
+
+ @Override
+ default IUpgradeInventory getUpgrades() {
+ if (!UpgradeSlotCompat.shouldEnableUpgradeSlots()) {
+ return UpgradeInventories.empty();
+ }
+ return ((IUpgradeableObject) this.getLogic()).getUpgrades();
+ }
+}
diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/compat/PatternProviderScreenCompatMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/compat/PatternProviderScreenCompatMixin.java
new file mode 100644
index 0000000..eaee82c
--- /dev/null
+++ b/src/main/java/com/extendedae_plus/mixin/ae2/compat/PatternProviderScreenCompatMixin.java
@@ -0,0 +1,88 @@
+package com.extendedae_plus.mixin.ae2.compat;
+
+import appeng.api.upgrades.Upgrades;
+import appeng.client.gui.AEBaseScreen;
+import appeng.client.gui.implementations.PatternProviderScreen;
+import appeng.client.gui.style.ScreenStyle;
+import appeng.client.gui.widgets.ToolboxPanel;
+import appeng.client.gui.widgets.UpgradesPanel;
+import appeng.core.localization.GuiText;
+import appeng.menu.SlotSemantics;
+import appeng.menu.implementations.PatternProviderMenu;
+import com.extendedae_plus.compat.UpgradeSlotCompat;
+import net.minecraft.network.chat.Component;
+import net.minecraft.world.entity.player.Inventory;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.Unique;
+import org.spongepowered.asm.mixin.injection.At;
+import org.spongepowered.asm.mixin.injection.Inject;
+import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * PatternProviderScreen的兼容性Mixin
+ * 优先级设置为500,避免与appflux冲突
+ */
+@Mixin(value = PatternProviderScreen.class, priority = 500, remap = false)
+public abstract class PatternProviderScreenCompatMixin extends AEBaseScreen {
+
+ @Inject(method = "", at = @At("TAIL"))
+ private void eap$initCompatUpgrades(PatternProviderMenu menu, Inventory playerInventory, Component title, ScreenStyle style, CallbackInfo ci) {
+ try {
+ // 检测是否应该添加升级面板
+ if (UpgradeSlotCompat.shouldAddUpgradePanelToScreen()) {
+ // 直接添加升级面板,不使用复杂的反射
+ this.eap$addUpgradePanelDirect(menu, style);
+ }
+ } catch (Exception e) {
+ // 静默处理异常,确保不会因为升级功能导致崩溃
+ com.extendedae_plus.util.ExtendedAELogger.LOGGER.error("PatternProviderScreen兼容性升级面板初始化失败", e);
+ }
+ }
+
+ @Unique
+ private void eap$addUpgradePanelDirect(PatternProviderMenu menu, ScreenStyle style) {
+ try {
+ // 直接添加升级面板
+ this.widgets.add("upgrades", new UpgradesPanel(
+ menu.getSlots(SlotSemantics.UPGRADE),
+ this::eap$getCompatibleUpgrades));
+
+ // 添加工具箱面板(如果菜单实现了兼容接口)
+ if (menu instanceof UpgradeSlotCompat.IUpgradeableMenuCompat compatMenu) {
+ var toolbox = compatMenu.getCompatToolbox();
+ if (toolbox != null && toolbox.isPresent()) {
+ this.widgets.add("toolbox", new ToolboxPanel(style, toolbox.getName()));
+ }
+ }
+ } catch (Exception e) {
+ com.extendedae_plus.util.ExtendedAELogger.LOGGER.error("直接添加升级面板失败", e);
+ }
+ }
+
+ @Unique
+ private List eap$getCompatibleUpgrades() {
+ var list = new ArrayList();
+ list.add(GuiText.CompatibleUpgrades.text());
+
+ try {
+ if (menu instanceof UpgradeSlotCompat.IUpgradeableMenuCompat compatMenu) {
+ var upgrades = compatMenu.getCompatUpgrades();
+ if (upgrades != null) {
+ list.addAll(Upgrades.getTooltipLinesForMachine(upgrades.getUpgradableItem()));
+ }
+ }
+ } catch (Exception e) {
+ com.extendedae_plus.util.ExtendedAELogger.LOGGER.error("获取兼容升级列表失败", e);
+ }
+
+ return list;
+ }
+
+ // 构造函数,Mixin要求
+ public PatternProviderScreenCompatMixin(C menu, Inventory playerInventory, Component title, ScreenStyle style) {
+ super(menu, playerInventory, title, style);
+ }
+}
diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/helpers/patternprovider/PatternProviderLogicHostUpgradesMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/helpers/patternprovider/PatternProviderLogicHostUpgradesMixin.java
index 9254e5d..47f27dc 100644
--- a/src/main/java/com/extendedae_plus/mixin/ae2/helpers/patternprovider/PatternProviderLogicHostUpgradesMixin.java
+++ b/src/main/java/com/extendedae_plus/mixin/ae2/helpers/patternprovider/PatternProviderLogicHostUpgradesMixin.java
@@ -10,12 +10,15 @@ import org.spongepowered.asm.mixin.Shadow;
/**
* 让 PatternProviderLogicHost 作为 IUpgradeableObject 的代理,菜单可从 host 获取升级槽。
*/
-@Mixin(value = PatternProviderLogicHost.class, remap = false)
+@Mixin(value = PatternProviderLogicHost.class, priority = 2000, remap = false)
public interface PatternProviderLogicHostUpgradesMixin extends IUpgradeableObject {
@Shadow PatternProviderLogic getLogic();
@Override
default IUpgradeInventory getUpgrades() {
+ if (!com.extendedae_plus.compat.UpgradeSlotCompat.shouldEnableUpgradeSlots()) {
+ return appeng.api.upgrades.UpgradeInventories.empty();
+ }
return ((IUpgradeableObject) this.getLogic()).getUpgrades();
}
}
diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/helpers/patternprovider/PatternProviderLogicUpgradesMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/helpers/patternprovider/PatternProviderLogicUpgradesMixin.java
index 1a802cd..59f06c9 100644
--- a/src/main/java/com/extendedae_plus/mixin/ae2/helpers/patternprovider/PatternProviderLogicUpgradesMixin.java
+++ b/src/main/java/com/extendedae_plus/mixin/ae2/helpers/patternprovider/PatternProviderLogicUpgradesMixin.java
@@ -8,6 +8,7 @@ import appeng.api.upgrades.UpgradeInventories;
import appeng.helpers.patternprovider.PatternProviderLogic;
import appeng.helpers.patternprovider.PatternProviderLogicHost;
import com.extendedae_plus.ae.items.ChannelCardItem;
+import com.extendedae_plus.compat.UpgradeSlotCompat;
import com.extendedae_plus.init.ModItems;
import com.extendedae_plus.wireless.WirelessSlaveLink;
import com.extendedae_plus.wireless.endpoint.GenericNodeEndpointImpl;
@@ -29,7 +30,7 @@ import java.util.List;
* 为 PatternProviderLogic 注入升级槽,实现 IUpgradeableObject。
* 仅负责升级槽的持久化/掉落/清空与初始化,不改变原有逻辑。
*/
-@Mixin(value = PatternProviderLogic.class, remap = false)
+@Mixin(value = PatternProviderLogic.class, priority = 2000, remap = false)
public abstract class PatternProviderLogicUpgradesMixin implements IUpgradeableObject, InterfaceWirelessLinkBridge {
@Unique
private IUpgradeInventory eap$upgrades = UpgradeInventories.empty();
@@ -75,16 +76,27 @@ public abstract class PatternProviderLogicUpgradesMixin implements IUpgradeableO
@Inject(method = "(Lappeng/api/networking/IManagedGridNode;Lappeng/helpers/patternprovider/PatternProviderLogicHost;I)V",
at = @At("TAIL"))
private void eap$initUpgrades(IManagedGridNode mainNode, PatternProviderLogicHost host, int patternInventorySize, CallbackInfo ci) {
+ // 只有在应该启用升级卡槽时才初始化
+ if (!UpgradeSlotCompat.shouldEnableUpgradeSlots()) {
+ return;
+ }
+
this.eap$upgrades = UpgradeInventories.forMachine(host.getTerminalIcon().getItem(), 1, this::eap$onUpgradesChanged);
}
@Inject(method = "writeToNBT", at = @At("TAIL"))
private void eap$saveUpgrades(CompoundTag tag, CallbackInfo ci) {
+ if (!UpgradeSlotCompat.shouldEnableUpgradeSlots()) {
+ return;
+ }
this.eap$upgrades.writeToNBT(tag, "upgrades");
}
@Inject(method = "readFromNBT", at = @At("TAIL"))
private void eap$loadUpgrades(CompoundTag tag, CallbackInfo ci) {
+ if (!UpgradeSlotCompat.shouldEnableUpgradeSlots()) {
+ return;
+ }
this.eap$upgrades.readFromNBT(tag, "upgrades");
// 从 NBT 加载后,重置并尝试初始化(可能刚进入世界)
eap$lastChannel = -1;
@@ -94,6 +106,9 @@ public abstract class PatternProviderLogicUpgradesMixin implements IUpgradeableO
@Inject(method = "addDrops", at = @At("TAIL"))
private void eap$dropUpgrades(List drops, CallbackInfo ci) {
+ if (!UpgradeSlotCompat.shouldEnableUpgradeSlots()) {
+ return;
+ }
for (var stack : this.eap$upgrades) {
if (!stack.isEmpty()) {
drops.add(stack);
@@ -103,6 +118,9 @@ public abstract class PatternProviderLogicUpgradesMixin implements IUpgradeableO
@Inject(method = "clearContent", at = @At("TAIL"))
private void eap$clearUpgrades(CallbackInfo ci) {
+ if (!UpgradeSlotCompat.shouldEnableUpgradeSlots()) {
+ return;
+ }
this.eap$upgrades.clear();
}
@@ -115,6 +133,9 @@ public abstract class PatternProviderLogicUpgradesMixin implements IUpgradeableO
@Override
public IUpgradeInventory getUpgrades() {
+ if (!UpgradeSlotCompat.shouldEnableUpgradeSlots()) {
+ return UpgradeInventories.empty();
+ }
return this.eap$upgrades;
}
diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/menu/PatternProviderMenuUpgradesMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/menu/PatternProviderMenuUpgradesMixin.java
index 0cda3ae..c5ed6d7 100644
--- a/src/main/java/com/extendedae_plus/mixin/ae2/menu/PatternProviderMenuUpgradesMixin.java
+++ b/src/main/java/com/extendedae_plus/mixin/ae2/menu/PatternProviderMenuUpgradesMixin.java
@@ -8,6 +8,7 @@ import appeng.menu.AEBaseMenu;
import appeng.menu.ToolboxMenu;
import appeng.menu.implementations.PatternProviderMenu;
import com.extendedae_plus.bridge.IUpgradableMenu;
+import com.extendedae_plus.compat.UpgradeSlotCompat;
import net.minecraft.world.entity.player.Inventory;
import net.minecraft.world.inventory.MenuType;
import org.spongepowered.asm.mixin.Final;
@@ -18,7 +19,7 @@ import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
-@Mixin(value = PatternProviderMenu.class, remap = false)
+@Mixin(value = PatternProviderMenu.class, priority = 2000, remap = false)
public abstract class PatternProviderMenuUpgradesMixin extends AEBaseMenu implements IUpgradableMenu {
@Final
@Shadow protected PatternProviderLogic logic;
@@ -29,17 +30,28 @@ public abstract class PatternProviderMenuUpgradesMixin extends AEBaseMenu implem
@Inject(method = "(Lnet/minecraft/world/inventory/MenuType;ILnet/minecraft/world/entity/player/Inventory;Lappeng/helpers/patternprovider/PatternProviderLogicHost;)V",
at = @At("TAIL"))
private void eap$initUpgrades(MenuType> menuType, int id, Inventory playerInventory, PatternProviderLogicHost host, CallbackInfo ci) {
+ // 只有在应该启用升级卡槽时才初始化
+ if (!UpgradeSlotCompat.shouldEnableUpgradeSlots()) {
+ return;
+ }
+
this.eap$toolbox = new ToolboxMenu(this);
this.setupUpgrades(((IUpgradeableObject) host).getUpgrades());
}
@Override
public ToolboxMenu getToolbox() {
+ if (!UpgradeSlotCompat.shouldEnableUpgradeSlots()) {
+ return null;
+ }
return this.eap$toolbox;
}
@Override
public IUpgradeInventory getUpgrades() {
+ if (!UpgradeSlotCompat.shouldEnableUpgradeSlots()) {
+ return appeng.api.upgrades.UpgradeInventories.empty();
+ }
return ((IUpgradeableObject) this.logic).getUpgrades();
}
diff --git a/src/main/resources/extendedae_plus.mixins.json b/src/main/resources/extendedae_plus.mixins.json
index ab3d360..5c81819 100644
--- a/src/main/resources/extendedae_plus.mixins.json
+++ b/src/main/resources/extendedae_plus.mixins.json
@@ -20,7 +20,7 @@
"ae2.client.gui.PatternEncodingTermScreenMixin",
"ae2.client.gui.PatternProviderCloseMixin",
"ae2.client.gui.PatternProviderScreenMixin",
- "ae2.client.gui.PatternProviderScreenUpgradesMixin",
+ "ae2.compat.PatternProviderScreenCompatMixin",
"ae2.client.gui.SlotGridLayoutMixin",
"ae2.menu.CraftConfirmMenuGoBackMixin",
"extendedae.accessor.GuiExPatternTerminalAccessor",
@@ -63,9 +63,9 @@
"ae2.menu.PatternEncodingTermMenuMixin",
"ae2.menu.PatternProviderMenuAdvancedMixin",
"ae2.menu.PatternProviderMenuDoublingMixin",
- "ae2.helpers.patternprovider.PatternProviderLogicUpgradesMixin",
- "ae2.helpers.patternprovider.PatternProviderLogicHostUpgradesMixin",
- "ae2.menu.PatternProviderMenuUpgradesMixin",
+ "ae2.compat.PatternProviderLogicCompatMixin",
+ "ae2.compat.PatternProviderLogicHostCompatMixin",
+ "ae2.compat.PatternProviderCompatMixin",
"ae2.helpers.patternprovider.PatternProviderLogicTickerMixin",
"ae2.parts.AEBasePartClientSyncMixin",
"ae2.parts.automation.IOBusPartChannelCardMixin",
@@ -84,5 +84,6 @@
"injectors": {
"defaultRequire": 1
},
+ "plugin": "com.extendedae_plus.mixin.MixinConditions",
"priority": 1000
}
From b8bfd1a3f663586ff46d88980f09f36cdb9a61ef Mon Sep 17 00:00:00 2001
From: GaLicn <133291877+GaLicn@users.noreply.github.com>
Date: Tue, 23 Sep 2025 18:10:27 +0800
Subject: [PATCH 23/34] =?UTF-8?q?=E5=85=BC=E5=AE=B9applied=20flux?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../compat/UpgradeSlotCompat.java | 9 ++
.../PatternProviderLogicCompatMixin.java | 125 +++++++++++++-----
.../PatternProviderLogicHostCompatMixin.java | 2 +-
3 files changed, 102 insertions(+), 34 deletions(-)
diff --git a/src/main/java/com/extendedae_plus/compat/UpgradeSlotCompat.java b/src/main/java/com/extendedae_plus/compat/UpgradeSlotCompat.java
index 6c39295..cef62a5 100644
--- a/src/main/java/com/extendedae_plus/compat/UpgradeSlotCompat.java
+++ b/src/main/java/com/extendedae_plus/compat/UpgradeSlotCompat.java
@@ -44,6 +44,15 @@ public class UpgradeSlotCompat {
}
}
+ /**
+ * 检测是否应该启用频道卡功能
+ * 频道卡是我们独有的功能,即使appflux存在也应该启用
+ * @return 总是返回true,因为频道卡功能不与appflux冲突
+ */
+ public static boolean shouldEnableChannelCard() {
+ return true; // 频道卡功能总是启用,因为appflux没有实现这个功能
+ }
+
/**
* 检测是否应该在Screen中添加升级面板
* @return true如果应该添加,false如果检测到appflux模组存在
diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/compat/PatternProviderLogicCompatMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/compat/PatternProviderLogicCompatMixin.java
index 0945b85..1b922e2 100644
--- a/src/main/java/com/extendedae_plus/mixin/ae2/compat/PatternProviderLogicCompatMixin.java
+++ b/src/main/java/com/extendedae_plus/mixin/ae2/compat/PatternProviderLogicCompatMixin.java
@@ -27,7 +27,8 @@ import java.util.List;
/**
* PatternProviderLogic的兼容性Mixin
- * 优先级设置为500,避免与appflux冲突
+ * 优先级设置为1500,在appflux之后应用
+ * 根据appflux是否存在来决定是否实现IUpgradeableObject接口
*/
@Mixin(value = PatternProviderLogic.class, priority = 500, remap = false)
public abstract class PatternProviderLogicCompatMixin implements IUpgradeableObject, InterfaceWirelessLinkBridge {
@@ -64,31 +65,60 @@ public abstract class PatternProviderLogicCompatMixin implements IUpgradeableObj
@Unique
private void eap$compatOnUpgradesChanged() {
- if (!UpgradeSlotCompat.shouldEnableUpgradeSlots()) {
- return;
- }
-
try {
this.host.saveChanges();
- // 升级变更,重置并尝试初始化
- eap$compatLastChannel = -1;
- eap$compatHasInitialized = false;
- eap$compatInitializeChannelLink();
+ // 频道卡功能独立于升级槽功能,总是处理
+ if (UpgradeSlotCompat.shouldEnableChannelCard()) {
+ // 升级变更,重置并尝试初始化频道卡
+ eap$compatLastChannel = -1;
+ eap$compatHasInitialized = false;
+ eap$compatInitializeChannelLink();
+ }
} catch (Exception e) {
com.extendedae_plus.util.ExtendedAELogger.LOGGER.error("兼容性升级变更处理失败", e);
}
}
+
+ // 监听appflux的升级变化 - 通过注入到appflux的af_$onUpgradesChanged方法
+ @Inject(method = "af_$onUpgradesChanged", at = @At("TAIL"), remap = false, require = 0)
+ private void eap$onAppfluxUpgradesChanged(CallbackInfo ci) {
+ try {
+ if (UpgradeSlotCompat.shouldEnableChannelCard()) {
+ com.extendedae_plus.util.ExtendedAELogger.LOGGER.info("监听到appflux升级变化,处理频道卡");
+ // 升级变更,重置并尝试初始化频道卡
+ eap$compatLastChannel = -1;
+ eap$compatHasInitialized = false;
+ eap$compatInitializeChannelLink();
+ }
+ } catch (Exception e) {
+ com.extendedae_plus.util.ExtendedAELogger.LOGGER.error("监听appflux升级变化失败", e);
+ }
+ }
@Inject(method = "(Lappeng/api/networking/IManagedGridNode;Lappeng/helpers/patternprovider/PatternProviderLogicHost;I)V",
at = @At("TAIL"))
private void eap$compatInitUpgrades(IManagedGridNode mainNode, PatternProviderLogicHost host, int patternInventorySize, CallbackInfo ci) {
try {
- if (UpgradeSlotCompat.shouldEnableUpgradeSlots()) {
+ com.extendedae_plus.util.ExtendedAELogger.LOGGER.info("兼容性PatternProviderLogic初始化被调用");
+
+ boolean upgradeSlots = UpgradeSlotCompat.shouldEnableUpgradeSlots();
+ boolean channelCard = UpgradeSlotCompat.shouldEnableChannelCard();
+
+ com.extendedae_plus.util.ExtendedAELogger.LOGGER.info("升级槽功能: {}, 频道卡功能: {}", upgradeSlots, channelCard);
+
+ if (upgradeSlots) {
+ // 只有在升级槽功能启用时才创建升级槽
this.eap$compatUpgrades = UpgradeInventories.forMachine(
host.getTerminalIcon().getItem(),
1,
this::eap$compatOnUpgradesChanged
);
+ com.extendedae_plus.util.ExtendedAELogger.LOGGER.info("创建了完整的升级槽");
+ } else if (channelCard) {
+ // 如果装了appflux,我们不创建自己的升级槽,而是监听appflux的升级槽
+ com.extendedae_plus.util.ExtendedAELogger.LOGGER.info("装了appflux,将监听其升级槽来处理频道卡");
+ } else {
+ com.extendedae_plus.util.ExtendedAELogger.LOGGER.info("跳过升级槽创建");
}
} catch (Exception e) {
com.extendedae_plus.util.ExtendedAELogger.LOGGER.error("兼容性升级初始化失败", e);
@@ -98,7 +128,7 @@ public abstract class PatternProviderLogicCompatMixin implements IUpgradeableObj
@Inject(method = "writeToNBT", at = @At("TAIL"))
private void eap$compatSaveUpgrades(CompoundTag tag, CallbackInfo ci) {
try {
- if (UpgradeSlotCompat.shouldEnableUpgradeSlots()) {
+ if (UpgradeSlotCompat.shouldEnableUpgradeSlots() || UpgradeSlotCompat.shouldEnableChannelCard()) {
this.eap$compatUpgrades.writeToNBT(tag, "compat_upgrades");
}
} catch (Exception e) {
@@ -109,12 +139,14 @@ public abstract class PatternProviderLogicCompatMixin implements IUpgradeableObj
@Inject(method = "readFromNBT", at = @At("TAIL"))
private void eap$compatLoadUpgrades(CompoundTag tag, CallbackInfo ci) {
try {
- if (UpgradeSlotCompat.shouldEnableUpgradeSlots()) {
+ if (UpgradeSlotCompat.shouldEnableUpgradeSlots() || UpgradeSlotCompat.shouldEnableChannelCard()) {
this.eap$compatUpgrades.readFromNBT(tag, "compat_upgrades");
- // 从 NBT 加载后,重置并尝试初始化
- eap$compatLastChannel = -1;
- eap$compatHasInitialized = false;
- eap$compatInitializeChannelLink();
+ // 从 NBT 加载后,重置并尝试初始化频道卡
+ if (UpgradeSlotCompat.shouldEnableChannelCard()) {
+ eap$compatLastChannel = -1;
+ eap$compatHasInitialized = false;
+ eap$compatInitializeChannelLink();
+ }
}
} catch (Exception e) {
com.extendedae_plus.util.ExtendedAELogger.LOGGER.error("兼容性升级加载失败", e);
@@ -124,7 +156,7 @@ public abstract class PatternProviderLogicCompatMixin implements IUpgradeableObj
@Inject(method = "addDrops", at = @At("TAIL"))
private void eap$compatDropUpgrades(List drops, CallbackInfo ci) {
try {
- if (UpgradeSlotCompat.shouldEnableUpgradeSlots()) {
+ if (UpgradeSlotCompat.shouldEnableUpgradeSlots() || UpgradeSlotCompat.shouldEnableChannelCard()) {
for (var stack : this.eap$compatUpgrades) {
if (!stack.isEmpty()) {
drops.add(stack);
@@ -139,7 +171,7 @@ public abstract class PatternProviderLogicCompatMixin implements IUpgradeableObj
@Inject(method = "clearContent", at = @At("TAIL"))
private void eap$compatClearUpgrades(CallbackInfo ci) {
try {
- if (UpgradeSlotCompat.shouldEnableUpgradeSlots()) {
+ if (UpgradeSlotCompat.shouldEnableUpgradeSlots() || UpgradeSlotCompat.shouldEnableChannelCard()) {
this.eap$compatUpgrades.clear();
}
} catch (Exception e) {
@@ -150,14 +182,19 @@ public abstract class PatternProviderLogicCompatMixin implements IUpgradeableObj
@Override
public IUpgradeInventory getUpgrades() {
if (UpgradeSlotCompat.shouldEnableUpgradeSlots()) {
- return this.eap$compatUpgrades;
+ // 不装appflux时,返回我们自己的升级槽
+ return this.eap$compatUpgrades != null ? this.eap$compatUpgrades : UpgradeInventories.empty();
+ } else {
+ // 装了appflux时,这个方法不应该被调用,因为appflux的Mixin会覆盖它
+ // 但是为了安全起见,返回空的升级槽
+ com.extendedae_plus.util.ExtendedAELogger.LOGGER.debug("装了appflux时getUpgrades被调用,这不应该发生");
+ return UpgradeInventories.empty();
}
- return UpgradeInventories.empty();
}
@Override
public void eap$updateWirelessLink() {
- if (!UpgradeSlotCompat.shouldEnableUpgradeSlots()) {
+ if (!UpgradeSlotCompat.shouldEnableChannelCard()) {
return;
}
@@ -172,7 +209,7 @@ public abstract class PatternProviderLogicCompatMixin implements IUpgradeableObj
@Unique
public void eap$compatInitializeChannelLink() {
- if (!UpgradeSlotCompat.shouldEnableUpgradeSlots()) {
+ if (!UpgradeSlotCompat.shouldEnableChannelCard()) {
return;
}
@@ -200,11 +237,33 @@ public abstract class PatternProviderLogicCompatMixin implements IUpgradeableObj
long channel = 0L;
boolean found = false;
- for (ItemStack stack : this.eap$compatUpgrades) {
- if (!stack.isEmpty() && stack.getItem() == ModItems.CHANNEL_CARD.get()) {
- channel = ChannelCardItem.getChannel(stack);
- found = true;
- break;
+
+ // 获取升级槽 - 如果装了appflux则从appflux获取,否则从我们自己的获取
+ IUpgradeInventory upgrades = null;
+ if (UpgradeSlotCompat.shouldEnableUpgradeSlots()) {
+ // 不装appflux时使用我们自己的升级槽
+ upgrades = this.eap$compatUpgrades;
+ } else if (UpgradeSlotCompat.shouldEnableChannelCard()) {
+ // 装了appflux时,尝试从PatternProviderLogic获取升级槽
+ try {
+ if (this instanceof IUpgradeableObject) {
+ IUpgradeableObject upgradeableThis = (IUpgradeableObject) this;
+ upgrades = upgradeableThis.getUpgrades();
+ com.extendedae_plus.util.ExtendedAELogger.LOGGER.debug("从appflux获取到升级槽: {}", upgrades != null);
+ }
+ } catch (Exception e) {
+ com.extendedae_plus.util.ExtendedAELogger.LOGGER.error("获取appflux升级槽失败", e);
+ }
+ }
+
+ if (upgrades != null) {
+ for (ItemStack stack : upgrades) {
+ if (!stack.isEmpty() && stack.getItem() == ModItems.CHANNEL_CARD.get()) {
+ channel = ChannelCardItem.getChannel(stack);
+ found = true;
+ com.extendedae_plus.util.ExtendedAELogger.LOGGER.info("找到频道卡,频道: {}", channel);
+ break;
+ }
}
}
@@ -244,14 +303,14 @@ public abstract class PatternProviderLogicCompatMixin implements IUpgradeableObj
@Override
public void eap$setClientWirelessState(boolean connected) {
- if (UpgradeSlotCompat.shouldEnableUpgradeSlots()) {
+ if (UpgradeSlotCompat.shouldEnableChannelCard()) {
eap$compatClientConnected = connected;
}
}
@Override
public boolean eap$isWirelessConnected() {
- if (!UpgradeSlotCompat.shouldEnableUpgradeSlots()) {
+ if (!UpgradeSlotCompat.shouldEnableChannelCard()) {
return false;
}
@@ -269,7 +328,7 @@ public abstract class PatternProviderLogicCompatMixin implements IUpgradeableObj
@Override
public boolean eap$hasTickInitialized() {
- if (UpgradeSlotCompat.shouldEnableUpgradeSlots()) {
+ if (UpgradeSlotCompat.shouldEnableChannelCard()) {
return eap$compatHasInitialized;
}
return true;
@@ -277,14 +336,14 @@ public abstract class PatternProviderLogicCompatMixin implements IUpgradeableObj
@Override
public void eap$setTickInitialized(boolean initialized) {
- if (UpgradeSlotCompat.shouldEnableUpgradeSlots()) {
+ if (UpgradeSlotCompat.shouldEnableChannelCard()) {
eap$compatHasInitialized = initialized;
}
}
@Override
public void eap$handleDelayedInit() {
- if (!UpgradeSlotCompat.shouldEnableUpgradeSlots()) {
+ if (!UpgradeSlotCompat.shouldEnableChannelCard()) {
return;
}
@@ -317,7 +376,7 @@ public abstract class PatternProviderLogicCompatMixin implements IUpgradeableObj
@Inject(method = "onMainNodeStateChanged", at = @At("TAIL"))
private void eap$compatOnMainNodeStateChangedTail(CallbackInfo ci) {
- if (!UpgradeSlotCompat.shouldEnableUpgradeSlots()) {
+ if (!UpgradeSlotCompat.shouldEnableChannelCard()) {
return;
}
diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/compat/PatternProviderLogicHostCompatMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/compat/PatternProviderLogicHostCompatMixin.java
index 00b5c01..f6cdee4 100644
--- a/src/main/java/com/extendedae_plus/mixin/ae2/compat/PatternProviderLogicHostCompatMixin.java
+++ b/src/main/java/com/extendedae_plus/mixin/ae2/compat/PatternProviderLogicHostCompatMixin.java
@@ -19,7 +19,7 @@ public interface PatternProviderLogicHostCompatMixin extends IUpgradeableObject
@Override
default IUpgradeInventory getUpgrades() {
- if (!UpgradeSlotCompat.shouldEnableUpgradeSlots()) {
+ if (!UpgradeSlotCompat.shouldEnableUpgradeSlots() && !UpgradeSlotCompat.shouldEnableChannelCard()) {
return UpgradeInventories.empty();
}
return ((IUpgradeableObject) this.getLogic()).getUpgrades();
From 7a8b8ccd6588b17d342e2fcba11efff648417331 Mon Sep 17 00:00:00 2001
From: C-H716 <1536152356@qq.com>
Date: Tue, 23 Sep 2025 18:13:01 +0800
Subject: [PATCH 24/34] =?UTF-8?q?fix:=20=E6=9C=80=E7=BB=88=E4=BF=AE?=
=?UTF-8?q?=E5=A4=8D=E7=89=88=E6=97=A0=E9=99=90=E5=AD=98=E5=82=A8=E7=A3=81?=
=?UTF-8?q?=E7=9B=98?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
README.md | 2 +-
gradle.properties | 2 +-
.../com/extendedae_plus/ExtendedAEPlus.java | 24 +-
.../InfinityBigIntegerCellHandler.java | 15 -
.../InfinityBigIntegerCellInventory.java | 556 +++++++-----------
.../ae/items/InfinityBigIntegerCellItem.java | 74 +--
.../command/InfinityDiskGiveCommand.java | 5 +-
.../util/storage/InfinityConstants.java | 30 +
.../util/storage/InfinityDataStorage.java | 57 +-
.../util/storage/InfinityStorageManager.java | 269 +++------
10 files changed, 419 insertions(+), 615 deletions(-)
create mode 100644 src/main/java/com/extendedae_plus/util/storage/InfinityConstants.java
diff --git a/README.md b/README.md
index ca05466..af20501 100644
--- a/README.md
+++ b/README.md
@@ -42,6 +42,6 @@ ExtendedAE Plus 是一个面向 Applied Energistics 2 与 ExtendedAE 的功能
- Applied Energistics 2(AE2):MIT License
- SpongePowered Mixin:MIT License
- Configuration(by Toma):MIT License
-- AE2Things(by ProjectET):MIT License
+- AE2Things-Forge(by Technici4n):MIT License
请查阅各上游项目以获取完整与最新的许可证信息。第三方组件的许可证与版权归其各自作者所有。
diff --git a/gradle.properties b/gradle.properties
index b86c90f..b06db34 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-fix2
+mod_version = 1.4.2-fix3
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 3e02a54..00cd4c1 100644
--- a/src/main/java/com/extendedae_plus/ExtendedAEPlus.java
+++ b/src/main/java/com/extendedae_plus/ExtendedAEPlus.java
@@ -3,22 +3,20 @@ 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.command.InfinityDiskGiveCommand;
import com.extendedae_plus.config.ModConfig;
import com.extendedae_plus.init.*;
import com.extendedae_plus.menu.locator.CuriosItemLocator;
import com.extendedae_plus.util.storage.InfinityStorageManager;
import net.minecraft.resources.ResourceLocation;
-import net.minecraft.server.level.ServerLevel;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.client.event.ModelEvent;
import net.minecraftforge.common.MinecraftForge;
-import net.minecraftforge.event.level.LevelEvent;
+import net.minecraftforge.event.RegisterCommandsEvent;
+import net.minecraftforge.event.TickEvent;
import net.minecraftforge.eventbus.api.IEventBus;
import net.minecraftforge.eventbus.api.SubscribeEvent;
-import net.minecraftforge.event.RegisterCommandsEvent;
-import com.extendedae_plus.command.InfinityDiskGiveCommand;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent;
import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent;
@@ -54,15 +52,11 @@ public class ExtendedAEPlus {
// 注册到Forge事件总线
MinecraftForge.EVENT_BUS.register(this);
- MinecraftForge.EVENT_BUS.addListener(ExtendedAEPlus::onLevelLoad);
// 注册命令注册监听
MinecraftForge.EVENT_BUS.addListener(this::onRegisterCommands);
// 注册通用配置
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);
+ MinecraftForge.EVENT_BUS.addListener(ExtendedAEPlus::worldTick);
}
/**
@@ -121,10 +115,12 @@ public class ExtendedAEPlus {
}
}
- // 在世界加载时注册/加载 SavedData
- private static void onLevelLoad(LevelEvent.Load event) {
- if (event.getLevel() instanceof ServerLevel serverLevel) {
- InfinityStorageManager.getForLevel(serverLevel);
+
+ public static InfinityStorageManager STORAGE_INSTANCE = new InfinityStorageManager();
+
+ public static void worldTick(TickEvent.LevelTickEvent event) {
+ if (event.phase == TickEvent.Phase.START && event.side.isServer()) {
+ STORAGE_INSTANCE = InfinityStorageManager.getInstance(event.level.getServer());
}
}
diff --git a/src/main/java/com/extendedae_plus/ae/api/storage/InfinityBigIntegerCellHandler.java b/src/main/java/com/extendedae_plus/ae/api/storage/InfinityBigIntegerCellHandler.java
index bc7c29c..0609b6c 100644
--- a/src/main/java/com/extendedae_plus/ae/api/storage/InfinityBigIntegerCellHandler.java
+++ b/src/main/java/com/extendedae_plus/ae/api/storage/InfinityBigIntegerCellHandler.java
@@ -5,30 +5,15 @@ import appeng.api.storage.cells.ISaveProvider;
import com.extendedae_plus.ae.items.InfinityBigIntegerCellItem;
import net.minecraft.world.item.ItemStack;
-/**
- * InfinityBigIntegerCellHandler
- *
- * 该类实现 AE2 的 ICellHandler,用于:
- * - 判定某个 ItemStack 是否为本 mod 的 Infinity 存储单元
- * - 在 AE2 请求访问或创建存储单元时,创建并返回对应的 StorageCell 实例
- */
public class InfinityBigIntegerCellHandler implements ICellHandler {
- /** Handler 单例,供注册与调用使用 */
public static final InfinityBigIntegerCellHandler INSTANCE = new InfinityBigIntegerCellHandler();
- /**
- * 判断给定的 ItemStack 是否为 InfinityBigIntegerCell
- */
@Override
public boolean isCell(ItemStack is) {
return is.getItem() instanceof InfinityBigIntegerCellItem;
}
- /**
- * 在 AE2 需要访问或创建存储单元时返回对应的 InfinityBigIntegerCellInventory(StorageCell 实现)。
- * 参数 container 为 AE2 提供的保存回调(ISaveProvider),当 cell 需要持久化时会调用它。
- */
@Override
public InfinityBigIntegerCellInventory getCellInventory(ItemStack is, ISaveProvider container) {
return InfinityBigIntegerCellInventory.createInventory(is, container);
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 efbf16b..efcce92 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
@@ -8,7 +8,10 @@ import appeng.api.stacks.KeyCounter;
import appeng.api.storage.cells.CellState;
import appeng.api.storage.cells.ISaveProvider;
import appeng.api.storage.cells.StorageCell;
+import appeng.core.AELog;
+import com.extendedae_plus.ExtendedAEPlus;
import com.extendedae_plus.ae.items.InfinityBigIntegerCellItem;
+import com.extendedae_plus.util.storage.InfinityConstants;
import com.extendedae_plus.util.storage.InfinityDataStorage;
import com.extendedae_plus.util.storage.InfinityStorageManager;
import it.unimi.dsi.fastutil.objects.Object2ObjectMap;
@@ -16,77 +19,45 @@ import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.ListTag;
import net.minecraft.network.chat.Component;
-import net.minecraft.server.MinecraftServer;
-import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.item.ItemStack;
-import net.minecraftforge.event.TickEvent;
-import net.minecraftforge.event.server.ServerStoppingEvent;
-import net.minecraftforge.eventbus.api.SubscribeEvent;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.RoundingMode;
+import java.util.Objects;
import java.util.UUID;
-import java.util.concurrent.ConcurrentLinkedQueue;
-import static com.extendedae_plus.util.ExtendedAELogger.LOGGER;
/**
- * InfinityBigIntegerCellInventory
- *
- * 本类实现 AE2 的 StorageCell,表示单个 Infinity 存储单元的运行时数据与行为。
- * 主要职责:
- * - 在内存中维护条目映射 (AEKey -> BigInteger 数量)
- * - 提供插入/提取/列举/持久化等操作的实现
- * - 通过 UUID 将 ItemStack 与世界级的 SavedData 关联以实现持久化
- *
- * 重要字段:
- * - stack: 关联的 ItemStack,NB T 中保存 UUID 与缓存信息
- * - container: AE2 提供的保存回调 (ISaveProvider),用于合并与触发持久化
- * - storedMap: 延迟初始化的内存映射,减少未使用时内存占用
- * - totalStored: 缓存的总数量 (BigInteger),避免频繁全表扫描
- * - isPersisted: 标记内存状态是否已同步到持久层
+ * This code is inspired by AE2Things[](https://github.com/Technici4n/AE2Things-Forge), licensed under the MIT License.
+ * Original copyright (c) Technici4n
*/
public class InfinityBigIntegerCellInventory implements StorageCell {
-
- // 待持久化队列(用于 debounce:在服务器 tick 中合并持久化)
- private static final ConcurrentLinkedQueue PENDING_PERSIST = new ConcurrentLinkedQueue<>();
- // 数字格式化对象:改为方法局部以避免静态非线程安全问题
-
- // 关联的 ItemStack(含可能的 uuid NBT)
- private final ItemStack stack;
+ private final InfinityBigIntegerCellItem cell;
+ // 磁盘本身
+ private final ItemStack self;
// AE2 提供的保存提供者,用于在容器中批量保存时触发回调
private final ISaveProvider container;
- // 内存中的键-数量映射(使用 BigInteger 支持超长数量,延迟初始化)
- private Object2ObjectMap storedMap = null;
+ // 存储物品键和数量的映射
+ private Object2ObjectMap AEKey2AmountsMap;
+ // 存储的物品种类数量
+ private int totalAEKeyType;
+ // 存储的物品总数
+ private BigInteger totalAEKey2Amounts = BigInteger.ZERO;
// 标记是否已持久化到 SavedData
private boolean isPersisted = true;
- // 缓存的总存储量,避免每次调用进行全表扫描
- private BigInteger totalStored = BigInteger.ZERO;
- /**
- * 私有构造器:通过 createInventory 工厂方法调用
- *
- * @param stack 关联的物品堆
- * @param saveProvider AE2 的保存回调(可为 null)
- */
- private InfinityBigIntegerCellInventory(ItemStack stack, ISaveProvider saveProvider) {
- this.stack = stack;
- container = saveProvider;
- // 不在构造时创建 storedMap,推迟到实际访问或首次写入时初始化
- 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;
+ public InfinityBigIntegerCellInventory(InfinityBigIntegerCellItem cell, ItemStack stack, ISaveProvider saveProvider) {
+ // 保存存储单元类型(InfinityBigIntegerCellItem 实例),用于访问磁盘属性
+ this.cell = cell;
+ // 保存物品堆栈,表示磁盘本身,包含运行时的 NBT 数据
+ this.self = stack;
+ // 保存提供者,用于触发数据保存
+ this.container = saveProvider;
+ // 初始化 storedAmounts 为 null,延迟加载物品数据
+ this.AEKey2AmountsMap = null;
+ // 初始化磁盘数据
+ initData();
}
// 将 BigInteger 格式化为带单位的字符串,保留两位小数
@@ -107,29 +78,43 @@ public class InfinityBigIntegerCellInventory implements StorageCell {
return df.format(bd.doubleValue()) + units[idx];
}
- // 获取当前存储单元的数据存储对象
+ // 获取磁盘的 InfinityDataStorage 数据
private InfinityDataStorage getCellStorage() {
- if (this.getUUID() == null) {
- // 如果没有UUID,返回空存储(返回新实例以避免共享可变状态)
- return InfinityDataStorage.empty();
+ // 如果磁盘有 UUID,返回对应的 InfinityDataStorage
+ if (getUUID() != null) {
+ return getStorageManagerInstance().getOrCreateCell(getUUID());
} else {
- // 在访问全局 SavedData 之前做防御性检查,避免在客户端或尚未初始化的情况下 NPE
- InfinityStorageManager mgr = getStorageInstance();
- if (mgr == null) {
- // SavedData 尚未就绪,视为空存储(返回新实例以避免共享可变状态)
- return InfinityDataStorage.empty();
- }
- // 否则获取或创建对应UUID的存储
- return mgr.getOrCreateCell(getUUID());
+ // 否则返回空的 InfinityDataStorage
+ return InfinityDataStorage.EMPTY;
}
}
- // 获取存储单元状态(空/非空)
+ // 初始化磁盘数据
+ private void initData() {
+ // 如果磁盘有 UUID,加载存储的物品数据
+ if (hasUUID()) {
+ this.totalAEKeyType = getCellStorage().amounts.size();
+ this.totalAEKey2Amounts = getCellStorage().itemCount.equals(BigInteger.ZERO) ?
+ BigInteger.ZERO :
+ getCellStorage().itemCount;
+
+ } else {
+ // 否则初始化为空
+ this.totalAEKeyType = 0;
+ this.totalAEKey2Amounts = BigInteger.ZERO;
+ // 加载物品数据
+ getCellStoredMap();
+ }
+ }
+
+ // 获取存储单元的状态(空、部分填充)
@Override
public CellState getStatus() {
- if (this.getCellStoredMap().isEmpty()) {
+ // 如果没有存储任何物品,返回空状态
+ if (this.getTotalAEKey2Amounts().equals(BigInteger.ZERO)) {
return CellState.EMPTY;
}
+ // 否则返回满状态
return CellState.NOT_EMPTY;
}
@@ -139,225 +124,226 @@ public class InfinityBigIntegerCellInventory implements StorageCell {
return 512;
}
+ // 持久化存储单元数据到全局存储
+ @Override
+ public void persist() {
+ if (this.isPersisted)
+ return;
+
+ if (totalAEKey2Amounts.equals(BigInteger.ZERO)) {
+ if (hasUUID()) {
+ getStorageManagerInstance().removeCell(getUUID());
+ if (self.hasTag()) {
+ var tag = self.getTag();
+ // remove persisted identifiers and cached summary fields from the ItemStack
+ tag.remove(InfinityConstants.INFINITY_CELL_UUID);
+ tag.remove(InfinityConstants.INFINITY_ITEM_TOTAL);
+ tag.remove(InfinityConstants.INFINITY_ITEM_TYPES);
+ // backward compat: also remove internal cell item count key if present
+ tag.remove(InfinityConstants.INFINITY_CELL_ITEM_COUNT);
+ }
+ initData();
+ }
+ return;
+ }
+
+ // 创建物品键列表
+ ListTag keys = new ListTag();
+ // 创建物品数量列表
+ ListTag amounts = new ListTag();
+ // 初始化物品总数
+ BigInteger itemCount = BigInteger.ZERO;
+
+ for (var entry : this.AEKey2AmountsMap.object2ObjectEntrySet()) {
+ BigInteger amount = entry.getValue();
+ // 如果数量大于 0,添加到键和数量列表
+ if (amount.compareTo(BigInteger.ZERO) > 0) {
+ keys.add(entry.getKey().toTagGeneric());
+ CompoundTag amountTag = new CompoundTag();
+ amountTag.putByteArray("value", amount.toByteArray());
+ amounts.add(amountTag);
+
+ itemCount = itemCount.add(amount);
+ }
+ }
+
+ if (keys.isEmpty()) {
+ getStorageManagerInstance().updateCell(getUUID(), new InfinityDataStorage());
+ } else {
+ getStorageManagerInstance().modifyDisk(getUUID(), keys, amounts, itemCount);
+ }
+
+ // 更新存储的物品种类数量
+ this.totalAEKeyType = this.AEKey2AmountsMap.size();
+ // 更新存储的物品总数
+ this.totalAEKey2Amounts = itemCount;
+ // 将物品总数与种类数量存入物品堆栈的 NBT(用于快捷查看/tooltip),同时保留旧字段以兼容历史版本
+ var tag = self.getOrCreateTag();
+ tag.putByteArray(InfinityConstants.INFINITY_ITEM_TOTAL, itemCount.toByteArray());
+ tag.putInt(InfinityConstants.INFINITY_ITEM_TYPES, this.totalAEKeyType);
+ // backward compat storage field (kept for legacy readers)
+ tag.putByteArray(InfinityConstants.INFINITY_CELL_ITEM_COUNT, itemCount.toByteArray());
+
+ // 标记数据已持久化
+ this.isPersisted = true;
+ }
+
// 获取存储单元的描述(此处返回null,可自定义)
@Override
public Component getDescription() {
return null;
}
+ // 静态方法,创建存储单元库存
+ public static InfinityBigIntegerCellInventory createInventory(ItemStack stack, ISaveProvider saveProvider) {
+ // 检查物品堆栈是否为空
+ Objects.requireNonNull(stack, "Cannot create cell inventory for null itemstack");
+ // 检查物品是否为 IDISKCellItem 类型
+ if (!(stack.getItem() instanceof InfinityBigIntegerCellItem cell)) {
+ return null;
+ }
+ // 创建并返回新的 DISKCellInventory 实例
+ return new InfinityBigIntegerCellInventory(cell, stack, saveProvider);
+ }
+
+ // 获取存储的物品总数
+ public BigInteger getTotalAEKey2Amounts() {
+ return this.totalAEKey2Amounts;
+ }
+
+ // 获取存储的物品种类数量
+ public int getTotalAEKeyType() {
+ return this.totalAEKeyType;
+ }
+
// 判断物品堆栈是否有UUID
public boolean hasUUID() {
- return stack.hasTag() && stack.getOrCreateTag().contains("uuid");
+ return self.hasTag() && self.getOrCreateTag().contains(InfinityConstants.INFINITY_CELL_UUID);
}
// 获取物品堆栈的UUID
public UUID getUUID() {
- if (this.hasUUID())
- return stack.getOrCreateTag().getUUID("uuid");
- else
+ if (this.hasUUID()) {
+ return self.getOrCreateTag().getUUID(InfinityConstants.INFINITY_CELL_UUID);
+ } else {
return null;
+ }
}
// 获取或初始化存储映射
private Object2ObjectMap getCellStoredMap() {
- if (storedMap == null) {
- storedMap = new Object2ObjectOpenHashMap<>();
+ if (AEKey2AmountsMap == null) {
+ AEKey2AmountsMap = new Object2ObjectOpenHashMap<>();
this.loadCellStoredMap();
}
- return storedMap;
- }
-
- // 从存储中加载物品映射
- private void loadCellStoredMap() {
- boolean corruptedTag = false; // 标记数据是否损坏
- if (!stack.hasTag()) return;
- InfinityDataStorage storage = this.getCellStorage();
- ListTag keys = storage.keys;
- ListTag amounts = storage.amounts;
- int len = Math.min(keys.size(), amounts.size());
- for (int i = 0; i < len; i++) {
- AEKey key = AEKey.fromTagGeneric(keys.getCompound(i));
- CompoundTag amtTag = amounts.getCompound(i);
- try {
- BigInteger amount;
- if (amtTag.contains("l")) {
- long v = amtTag.getLong("l");
- amount = BigInteger.valueOf(v);
- } else if (amtTag.contains("s")) {
- amount = new BigInteger(amtTag.getString("s"));
- } else {
- corruptedTag = true;
- continue;
- }
- if (amount.compareTo(BigInteger.ZERO) <= 0 || key == null) {
- corruptedTag = true;
- } else {
- // storedMap 已在 getCellStoredMap() 中初始化,直接使用字段以避免额外方法开销
- storedMap.put(key, amount);
- // 更新缓存的总数
- totalStored = totalStored.add(amount);
- }
- } catch (NumberFormatException ex) {
- corruptedTag = true;
- }
- }
- // 如果有损坏,保存修正后的数据
- if (corruptedTag) {
- this.saveChanges();
- }
- }
-
- // 标记数据需要保存,并通知容器或直接持久化
- private void saveChanges() {
- // 标记为未持久化,交由容器或延迟任务合并写入以减少 I/O
- isPersisted = false;
- if (container != null) {
- // 当存在容器时,优先让容器统一处理持久化
- container.saveChanges();
- } else {
- // 如果没有容器,入队等待服务器 tick 在主线程统一持久化,避免频繁 I/O
- if (!PENDING_PERSIST.contains(this)) {
- PENDING_PERSIST.offer(this);
- }
- }
+ return AEKey2AmountsMap;
}
// 获取所有可用的物品堆栈及其数量
@Override
public void getAvailableStacks(KeyCounter out) {
BigInteger maxLong = BigInteger.valueOf(Long.MAX_VALUE);
- Object2ObjectMap map = getCellStoredMap();
- for (Object2ObjectMap.Entry entry : map.object2ObjectEntrySet()) {
+ if(this.getCellStoredMap() == null) return;
+ for (var entry : this.getCellStoredMap().object2ObjectEntrySet()) {
AEKey key = entry.getKey();
BigInteger value = entry.getValue();
- // 当前 KeyCounter 中已有的值(long)
+ // 获取 KeyCounter 中已有的值
long existing = out.get(key);
- // 将 existing 与当前 value 做 BigInteger 累加并饱和到 Long.MAX_VALUE
+ // 计算总和并限制到 Long.MAX_VALUE
BigInteger sum = BigInteger.valueOf(existing).add(value);
long toSet = sum.compareTo(maxLong) > 0 ? Long.MAX_VALUE : sum.longValue();
-
- // KeyCounter 没有 set(key,long) 的统一接口暴露(只有 add/remove),所以先移除已存在的值再设置。
- // 为避免读取-写入竞争,我们计算出要新增的 delta 并调用 add(key, delta)
+ // 更新 KeyCounter
if (existing == Long.MAX_VALUE) {
- // 已经饱和,无需再添加
continue;
}
- long delta;
- if (toSet == Long.MAX_VALUE) {
- delta = Long.MAX_VALUE - existing;
- } else {
- delta = toSet - existing;
- }
+ long delta = toSet - existing;
if (delta != 0) {
out.add(key, delta);
}
}
}
- // 持久化存储单元数据到全局存储
- @Override
- public void persist() {
- if (this.isPersisted)
- return;
- // 若全局存储管理器尚未就绪,重新入队并在未来 tick 重试
- InfinityStorageManager mgr = getStorageInstance();
- if (mgr == null) {
- try {
- var id = this.getUUID();
- LOGGER.info("Persist deferred for cell {}: storage manager not ready", id);
- } catch (Throwable ignored) {
- }
- if (!PENDING_PERSIST.contains(this)) {
- PENDING_PERSIST.offer(this);
- }
- return;
- }
- Object2ObjectMap map = this.getCellStoredMap();
- if (map.isEmpty()) {
- // 如果存储为空,移除UUID和全局存储中的数据
- if (this.hasUUID()) {
- mgr.removeCell(getUUID());
- if (stack.getTag() != null) {
- stack.getTag().remove("uuid");
- // 移除缓存的 total 字段
- stack.getTag().remove("total");
- }
- // 标记为已持久化,避免重复尝试
- isPersisted = true;
- }
- return;
+ // 从存储中加载物品映射
+ private void loadCellStoredMap() {
+ boolean dataCorruption = false;
+ if (!self.hasTag()) return;
+
+ var keys = getCellStorage().keys;
+ var amounts = getCellStorage().amounts;
+ // 数据损坏
+ if (keys.size() != amounts.size()) {
+ AELog.warn("Loading storage cell with mismatched amounts/tags: %d != %d", amounts.size(), keys.size());
}
- // 构建要保存的Key和数量列表(混合表示:long 或 string)
- ListTag amountTags = new ListTag();
- ListTag keys = new ListTag();
- for (Object2ObjectMap.Entry entry : map.object2ObjectEntrySet()) {
- BigInteger amount = entry.getValue();
- if (amount.compareTo(BigInteger.ZERO) > 0) {
- keys.add(entry.getKey().toTagGeneric());
- CompoundTag amt = new CompoundTag();
- if (amount.compareTo(BigInteger.valueOf(Long.MAX_VALUE)) <= 0) {
- amt.putLong("l", amount.longValue());
- } else {
- amt.putString("s", amount.toString());
- }
- amountTags.add(amt);
- }
- }
- // 如果没有Key,更新为空存储,否则保存数据
- 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) {
- if (totalStored.compareTo(BigInteger.valueOf(Long.MAX_VALUE)) <= 0) {
- stack.getOrCreateTag().putLong("total", totalStored.longValue());
+ // 遍历数量和键,加载到 AEKey2AmountsMap
+ for (int i = 0; i < amounts.size(); i++) {
+ AEKey key = AEKey.fromTagGeneric(keys.getCompound(i));
+ BigInteger amount = new BigInteger(amounts.getCompound(i).getByteArray("value"));
+ // 检查数据是否损坏
+ if (amount.compareTo(BigInteger.ZERO) <= 0 || key == null) {
+ dataCorruption = true;
} else {
- stack.getOrCreateTag().putString("total", totalStored.toString());
+ AEKey2AmountsMap.put(key, amount);
}
- // 将当前已存储的不同物品种类数缓存到 NBT(键名: "types"),用于客户端 tooltip 显示
- int typesCount = this.getCellStoredMap().size();
- stack.getOrCreateTag().putInt("types", typesCount);
}
- isPersisted = true;
+ if (dataCorruption) {
+ this.saveChanges();
+ }
+ }
+
+ // 获取全局存储实例
+ private static InfinityStorageManager getStorageManagerInstance() {
+ return ExtendedAEPlus.STORAGE_INSTANCE;
+ }
+
+ // 标记数据需要保存,并通知容器或直接持久化
+ private void saveChanges() {
+ // 更新存储的物品种类数量
+ this.totalAEKeyType = this.AEKey2AmountsMap.size();
+ // 重置物品总数
+ this.totalAEKey2Amounts = BigInteger.ZERO;
+ // 计算物品总数
+ for (BigInteger AEKey2Amounts : this.AEKey2AmountsMap.values()) {
+ this.totalAEKey2Amounts = this.totalAEKey2Amounts.add(AEKey2Amounts);
+ }
+ // 标记数据未持久化
+ this.isPersisted = false;
+ // 如果有保存提供者,通知保存
+ if (this.container != null) {
+ this.container.saveChanges();
+ } else {
+ // 否则立即持久化
+ this.persist();
+ }
}
// 插入物品到存储单元
@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并初始化存储
if (!this.hasUUID()) {
- InfinityStorageManager mgr = getStorageInstance();
- // 仅在 SavedData 已就绪(通常在服务器端)时创建 UUID 并注册持久化存储
- if (mgr != null) {
- stack.getOrCreateTag().putUUID("uuid", UUID.randomUUID());
- mgr.getOrCreateCell(getUUID());
- // 确保 storedMap 初始化并从持久层加载数据
- this.getCellStoredMap();
- } else {
- // SavedData 未就绪:把插入作为本地内存操作(不生成 UUID,不持久化),确保 storedMap 初始化以容纳临时数据
- this.getCellStoredMap();
- }
+ self.getOrCreateTag().putUUID(InfinityConstants.INFINITY_CELL_UUID, UUID.randomUUID());
+ getStorageManagerInstance().getOrCreateCell(getUUID());
+ loadCellStoredMap();
}
- Object2ObjectMap map = this.getCellStoredMap();
- BigInteger currentAmount = map.getOrDefault(what, BigInteger.ZERO);
+ // 获取当前物品数量
+ BigInteger currentAmount = this.getCellStoredMap().getOrDefault(what, BigInteger.ZERO);
+
if (mode == Actionable.MODULATE) {
// 实际插入,更新数量并保存
BigInteger newAmount = currentAmount.add(BigInteger.valueOf(amount));
- map.put(what, newAmount);
- // 更新 cached total
- totalStored = totalStored.add(BigInteger.valueOf(amount));
+ getCellStoredMap().put(what, newAmount);
this.saveChanges();
}
return amount;
@@ -366,35 +352,26 @@ public class InfinityBigIntegerCellInventory implements StorageCell {
// 从存储单元提取物品
@Override
public long extract(AEKey what, long amount, Actionable mode, IActionSource source) {
- Object2ObjectMap map = this.getCellStoredMap();
- BigInteger currentAmount = map.getOrDefault(what, BigInteger.ZERO);
+ BigInteger currentAmount = this.getCellStoredMap().getOrDefault(what, BigInteger.ZERO);
+ // 如果有物品可提取
if (currentAmount.compareTo(BigInteger.ZERO) > 0) {
+
BigInteger requested = BigInteger.valueOf(amount);
- if (currentAmount.compareTo(requested) <= 0) {
- // 提取全部
- long ret;
- if (currentAmount.compareTo(BigInteger.valueOf(Long.MAX_VALUE)) > 0) {
- ret = Long.MAX_VALUE;
- } else {
- ret = currentAmount.longValue();
- }
+
+ // 如果提取数量大于等于当前数量
+ if (requested.compareTo(currentAmount) >= 0) {
if (mode == Actionable.MODULATE) {
- map.remove(what);
- // 更新 cached total
- // 如果 currentAmount 大于 Long.MAX_VALUE,totalStored 减去 currentAmount 会保留大整数
- totalStored = totalStored.subtract(currentAmount);
+ getCellStoredMap().remove(what);
this.saveChanges();
}
- return ret;
+ return currentAmount.compareTo(BigInteger.valueOf(Long.MAX_VALUE)) > 0 ? Long.MAX_VALUE : currentAmount.longValue();
} else {
- // 提取部分
+ // 提取部分数量
if (mode == Actionable.MODULATE) {
- map.put(what, currentAmount.subtract(requested));
- // 更新 cached total
- totalStored = totalStored.subtract(requested);
+ getCellStoredMap().put(what, currentAmount.subtract(requested));
this.saveChanges();
}
- return amount;
+ return requested.longValue();
}
}
return 0;
@@ -403,89 +380,6 @@ public class InfinityBigIntegerCellInventory implements StorageCell {
// 获取存储单元内所有物品的总数量(格式化字符串)
public String getTotalStorage() {
// 使用缓存的 totalStored,避免每次全表扫描
- return formatBigInteger(totalStored);
- }
-
- /** 定时 tick 保存计数器 */
- private static int SAVE_TICK_COUNTER = 0;
-
- /** tick 间隔,单位为服务器 tick(20 tick ≈ 1 秒) */
- private static final int SAVE_TICK_INTERVAL = 20; // 约30秒
-
- /** 服务器 tick 事件:处理队列并定时保存 */
- @SubscribeEvent
- public static void onServerTick(TickEvent.ServerTickEvent event) {
- if (event.phase != TickEvent.Phase.END) return;
-
- // 处理待持久化队列
- InfinityBigIntegerCellInventory inv;
- while ((inv = PENDING_PERSIST.poll()) != null) {
- try {
- if (!inv.isPersisted) {
- inv.persist();
- }
- } catch (Throwable ex) {
- LOGGER.info("InfinityBigIntegerCellInventory onServerTick error: {}", ex.getMessage());
- }
- }
-
- // 定时标记全局存储为脏
- try {
- if (++SAVE_TICK_COUNTER >= SAVE_TICK_INTERVAL) {
- SAVE_TICK_COUNTER = 0;
- if (InfinityStorageManager.INSTANCE != null) {
- InfinityStorageManager.INSTANCE.setDirty();
- }
- }
- } catch (Throwable ex) {
- LOGGER.info("InfinityBigIntegerCellInventory tick dirty set error: {}", ex.getMessage());
- }
- }
-
- // 在服务器停止时被调用,立即强制持久化队列中的所有实例
- @SubscribeEvent
- public static void onServerStopping(ServerStoppingEvent event) {
- // 尝试在服务端停止流程开始时初始化 SavedData(确保 INSTANCE 可用以便后续持久化)
- try {
- MinecraftServer server = event.getServer();
- if (server != null) {
- for (ServerLevel level : server.getAllLevels()) {
- InfinityStorageManager.getForLevel(level);
- }
- }
- } catch (Throwable ignored) {
- LOGGER.info("InfinityBigIntegerCellInventory onServerStopping init error: {}", ignored.getMessage());
- }
-
- InfinityBigIntegerCellInventory inv;
- while ((inv = PENDING_PERSIST.poll()) != null) {
- try {
- if (!inv.isPersisted) {
- inv.persist();
- }
- } catch (Throwable ex) {
- LOGGER.info("InfinityBigIntegerCellInventory onServerStopping persist error: {}", ex.getMessage()); }
- }
-
- // 强制写回磁盘,确保直接关闭游戏也能保存
- try {
- InfinityStorageManager mgr = InfinityStorageManager.INSTANCE;
- if (mgr != null) {
- MinecraftServer server = event.getServer();
- if (server != null) {
- for (ServerLevel level : server.getAllLevels()) {
- try {
- mgr.forceSaveAll(level);
- LOGGER.info("Stop forceSaveAll for level {}", level.dimension().location());
- } catch (Throwable ex) {
- LOGGER.info("InfinityBigIntegerCellInventory forceSaveAll error for level {}: {}",
- level.dimension().location(), ex.getMessage());
- }
- }
- }
- }
- } catch (Throwable ex) {
- LOGGER.info("InfinityBigIntegerCellInventory onServerStopping forceSaveAll error: {}", ex.getMessage());
- }
+ return formatBigInteger(totalAEKey2Amounts);
}
}
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 75baebf..2b4af58 100644
--- a/src/main/java/com/extendedae_plus/ae/items/InfinityBigIntegerCellItem.java
+++ b/src/main/java/com/extendedae_plus/ae/items/InfinityBigIntegerCellItem.java
@@ -1,11 +1,12 @@
package com.extendedae_plus.ae.items;
+import appeng.api.config.FuzzyMode;
+import appeng.api.storage.cells.ICellWorkbenchItem;
import com.extendedae_plus.ae.api.storage.InfinityBigIntegerCellInventory;
+import com.extendedae_plus.util.storage.InfinityConstants;
import com.google.common.base.Preconditions;
import net.minecraft.ChatFormatting;
import net.minecraft.nbt.CompoundTag;
-import net.minecraft.nbt.LongTag;
-import net.minecraft.nbt.Tag;
import net.minecraft.network.chat.Component;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.Item;
@@ -16,24 +17,15 @@ import net.minecraftforge.registries.ForgeRegistries;
import org.jetbrains.annotations.NotNull;
import javax.annotation.Nullable;
-import java.math.BigInteger;
import java.util.List;
import java.util.Objects;
-public class InfinityBigIntegerCellItem extends Item {
+public class InfinityBigIntegerCellItem extends Item implements ICellWorkbenchItem {
public InfinityBigIntegerCellItem() {
super(new Properties().stacksTo(1).fireResistant());
}
- /**
- * 在物品悬停提示中展示额外信息。
- * 功能:
- * - 若 ItemStack 的 NBT 含有 UUID,则显示该 UUID(不会触发服务器加载或持久化行为)
- * - 若 NBT 同步了 total 字段,则读取并格式化显示总存储量(使用 Inventory 的 formatBigInteger)
- *
- * 设计说明:客户端 tooltip 不主动访问服务端 SavedData,以避免不必要的 I/O 与状态变更。
- */
@Override
public void appendHoverText(ItemStack stack,
@Nullable Level world,
@@ -45,15 +37,16 @@ public class InfinityBigIntegerCellItem extends Item {
Preconditions.checkArgument(stack.getItem() == this);
// 仅在 ItemStack 自身存在 UUID 时显示 UUID,避免触发持久化或加载逻辑
CompoundTag tag = stack.getTag();
- if (tag != null && tag.contains("uuid")) {
- String uuidStr = tag.getUUID("uuid").toString();
+ if (tag != null && tag.contains(InfinityConstants.INFINITY_CELL_UUID)) {
+ String uuidStr = tag.getUUID(InfinityConstants.INFINITY_CELL_UUID).toString();
tooltip.add(
Component.literal("UUID: ").withStyle(ChatFormatting.GRAY).append(Component.literal(uuidStr).withStyle(ChatFormatting.YELLOW))
);
- // 读取并显示已缓存的种类数量(types),表示当前存储了多少种不同的 AEKey
- if (tag.contains("types")) {
+
+ // 显示已缓存的种类数量(types)——优先使用 ItemStack 缓存字段
+ if (tag.contains(InfinityConstants.INFINITY_ITEM_TYPES)) {
try {
- int types = tag.getInt("types");
+ int types = tag.getInt(InfinityConstants.INFINITY_ITEM_TYPES);
tooltip.add(
Component.literal("Types: ").withStyle(ChatFormatting.GRAY).append(Component.literal(String.valueOf(types)).withStyle(ChatFormatting.GREEN))
);
@@ -61,24 +54,30 @@ public class InfinityBigIntegerCellItem extends Item {
// ignore malformed value
}
}
- // 读取并显示已缓存的 total(支持 long 或 string),使用格式化函数展示友好单位
- if (tag.contains("total")) {
- BigInteger total = BigInteger.ZERO;
- Tag t = tag.get("total");
+
+ // 显示物品总数(formatted)。优先使用缓存的 INFINITY_ITEM_TOTAL 字段(byte[]),否则回退为 legacy 字段或不显示
+ if (tag.contains(InfinityConstants.INFINITY_ITEM_TOTAL)) {
try {
- if (t instanceof LongTag) {
- total = BigInteger.valueOf(tag.getLong("total"));
- } else {
- String s = tag.getString("total");
- total = new BigInteger(s);
- }
+ byte[] bytes = tag.getByteArray(InfinityConstants.INFINITY_ITEM_TOTAL);
+ java.math.BigInteger total = new java.math.BigInteger(bytes);
+ String formatted = InfinityBigIntegerCellInventory.formatBigInteger(total);
+ tooltip.add(
+ Component.literal("Total: ").withStyle(ChatFormatting.GRAY).append(Component.literal(formatted).withStyle(ChatFormatting.AQUA))
+ );
} catch (Exception ignored) {
- // 解析失败保持为 0
+ // ignore malformed value
+ }
+ } else if (tag.contains(InfinityConstants.INFINITY_CELL_ITEM_COUNT)) {
+ try {
+ byte[] bytes = tag.getByteArray(InfinityConstants.INFINITY_CELL_ITEM_COUNT);
+ java.math.BigInteger total = new java.math.BigInteger(bytes);
+ String formatted = InfinityBigIntegerCellInventory.formatBigInteger(total);
+ tooltip.add(
+ Component.literal("Total: ").withStyle(ChatFormatting.GRAY).append(Component.literal(formatted).withStyle(ChatFormatting.AQUA))
+ );
+ } catch (Exception ignored) {
+ // ignore malformed value
}
- String formatted = InfinityBigIntegerCellInventory.formatBigInteger(total);
- tooltip.add(
- Component.literal("Byte: ").withStyle(ChatFormatting.GRAY).append(Component.literal(formatted).withStyle(ChatFormatting.AQUA))
- );
}
}
}
@@ -90,7 +89,16 @@ public class InfinityBigIntegerCellItem extends Item {
ItemStack stack = new ItemStack(Objects.requireNonNull(
ForgeRegistries.ITEMS.getValue(new ResourceLocation("extendedae_plus", "infinity_biginteger_cell")
)));
- stack.getOrCreateTag().putUUID("uuid", uuid);
+ stack.getOrCreateTag().putUUID(InfinityConstants.INFINITY_CELL_UUID, uuid);
return stack;
}
+
+ @Override
+ public FuzzyMode getFuzzyMode(ItemStack itemStack) {
+ return null;
+ }
+
+ @Override
+ public void setFuzzyMode(ItemStack itemStack, FuzzyMode fuzzyMode) {
+ }
}
\ No newline at end of file
diff --git a/src/main/java/com/extendedae_plus/command/InfinityDiskGiveCommand.java b/src/main/java/com/extendedae_plus/command/InfinityDiskGiveCommand.java
index 306e483..67305f3 100644
--- a/src/main/java/com/extendedae_plus/command/InfinityDiskGiveCommand.java
+++ b/src/main/java/com/extendedae_plus/command/InfinityDiskGiveCommand.java
@@ -1,5 +1,6 @@
package com.extendedae_plus.command;
+import com.extendedae_plus.ExtendedAEPlus;
import com.extendedae_plus.ae.items.InfinityBigIntegerCellItem;
import com.extendedae_plus.util.storage.InfinityStorageManager;
import com.mojang.brigadier.CommandDispatcher;
@@ -33,7 +34,7 @@ public class InfinityDiskGiveCommand {
source.sendFailure(Component.literal("This command must be run on server side."));
return 0;
}
- InfinityStorageManager mgr = InfinityStorageManager.getForLevel((ServerLevel) player.level());
+ InfinityStorageManager mgr = ExtendedAEPlus.STORAGE_INSTANCE;
if (mgr == null) {
source.sendFailure(Component.literal("InfinityStorageManager is not initialized."));
return 0;
@@ -54,7 +55,7 @@ public class InfinityDiskGiveCommand {
} catch (Exception ex) {
source.sendFailure(Component.literal("Error: " + ex.getMessage()));
return 0;
- }
+ }
}
}
diff --git a/src/main/java/com/extendedae_plus/util/storage/InfinityConstants.java b/src/main/java/com/extendedae_plus/util/storage/InfinityConstants.java
new file mode 100644
index 0000000..1fcb598
--- /dev/null
+++ b/src/main/java/com/extendedae_plus/util/storage/InfinityConstants.java
@@ -0,0 +1,30 @@
+package com.extendedae_plus.util.storage;
+
+public interface InfinityConstants {
+ // 当前磁盘格式版本号,增加字段用于向后/向前兼容
+ int FORMAT_VERSION = 2;
+ // 存储磁盘数据的格式版本号
+ String FORMAT_VERSION_FIELD = "infinity_format_version";
+
+ // savedData 文件名常量
+ String SAVE_FILE_NAME = "infinity_biginteger_cells";
+
+ // 磁盘的唯一标识符键名,存储在 ItemStack 和 InfinityStorageManager 中
+ String INFINITY_CELL_UUID = "infinity_cell_uuid";
+ // 单个磁盘的 InfinityDataStorage 数据键名
+ String INFINITY_CELL_DATA = "infinity_cell_data";
+ // 所有磁盘数据的列表键名,存储在 InfinityStorageManager 的 NBT 中
+ String INFINITY_CELL_LIST = "infinity_cell_list";
+
+ // 磁盘中所有物品键的键名(ListTag of CompoundTag)
+ String INFINITY_CELL_KEYS = "infinity_cell_keys";
+ // 磁盘中每种物品数量的键名(ListTag of CompoundTag,包含 "value")
+ String INFINITY_CELL_AMOUNTS = "infinity_cell_amounts";
+ // 磁盘中所有物品的总数键名(ListTag,包含一个 CompoundTag 的 "value")
+ String INFINITY_CELL_ITEM_COUNT = "infinity_cell_item_count";
+
+ // ItemStack 的 NBT 中存储总物品数量的键名
+ String INFINITY_ITEM_TOTAL = "infinity_item_total";
+ // ItemStack 的 NBT 中存储物品种类数量的键名
+ String INFINITY_ITEM_TYPES = "infinity_item_types";
+}
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 c56e421..6462128 100644
--- a/src/main/java/com/extendedae_plus/util/storage/InfinityDataStorage.java
+++ b/src/main/java/com/extendedae_plus/util/storage/InfinityDataStorage.java
@@ -2,62 +2,47 @@ package com.extendedae_plus.util.storage;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.ListTag;
-import net.minecraft.nbt.Tag;
+
+import java.math.BigInteger;
/**
- * InfinityDataStorage
- *
- * 表示单个 UUID 对应的持久化数据容器,直接映射到世界存档中的一项记录。
- * 数据结构说明:
- * - keys: 存放序列化后的 AEKey(每项为 CompoundTag),用于标识不同的存储条目
- * - amounts: 与 keys 一一对应的数量列表(每项为 CompoundTag),采用混合表示:
- * - 当数量能放入 long 时,CompoundTag 包含键 "l" 存放 long 值
- * - 当数量超出 long 时,CompoundTag 包含键 "s" 存放 BigInteger 的字符串形式
- *
- * 该类提供将内存数据与 NBT 之间互转的辅助方法,供 `SavedData` 在世界保存/加载时调用。
+ * This code is inspired by AE2Things[](https://github.com/Technici4n/AE2Things-Forge), licensed under the MIT License.
+ * Original copyright (c) Technici4n
*/
public class InfinityDataStorage {
+ // 定义一个静态常量 EMPTY,表示一个空的 DataStorage 实例,用于默认或占位场景
+ public static final InfinityDataStorage EMPTY = new InfinityDataStorage();
- /** 空实例的访问器(返回新实例以避免共享可变状态) */
- public static InfinityDataStorage empty() {
- return new InfinityDataStorage();
- }
-
- /** 序列化的键列表(NBT ListTag,元素为 CompoundTag) */
public ListTag keys;
- /**
- * 与 keys 对应的数量列表(NBT ListTag,元素为 CompoundTag):
- * - 若数量能放入 long,则 CompoundTag 包含键 "l"(long)
- * - 否则包含键 "s"(String) 存放 BigInteger 的字符串形式
- */
public ListTag amounts;
+ // 存储磁盘中物品的总数,使用 BigInteger 支持大容量
+ public BigInteger itemCount;
public InfinityDataStorage() {
- this(new ListTag(), new ListTag());
+ this(new ListTag(), new ListTag(), BigInteger.ZERO);
}
- private InfinityDataStorage(ListTag keys, ListTag amounts) {
+ private InfinityDataStorage(ListTag keys, ListTag amounts, BigInteger itemCount) {
this.keys = keys;
this.amounts = amounts;
+ this.itemCount = itemCount;
}
- /**
- * 将当前数据封装为 CompoundTag 以写入存档
- */
+ // 将 DataStorage 数据序列化为 NBT 格式
public CompoundTag serializeNBT() {
CompoundTag nbt = new CompoundTag();
- nbt.put("keys", keys);
- nbt.put("amounts", amounts);
+ nbt.put(InfinityConstants.INFINITY_CELL_KEYS, keys);
+ nbt.put(InfinityConstants.INFINITY_CELL_AMOUNTS, amounts);
+ nbt.putByteArray(InfinityConstants.INFINITY_CELL_ITEM_COUNT, itemCount.toByteArray());
return nbt;
}
- /**
- * 从存档读取数据并构造实例
- */
+ // 从 NBT 数据反序列化创建 DataStorage 实例
public static InfinityDataStorage loadFromNBT(CompoundTag nbt) {
- ListTag stackKeys = nbt.getList("keys", Tag.TAG_COMPOUND);
- // amounts 以 CompoundTag 列表存储,每个 CompoundTag 内含 long 或 String
- ListTag stackAmounts = nbt.getList("amounts", Tag.TAG_COMPOUND);
- return new InfinityDataStorage(stackKeys, stackAmounts);
+ ListTag keys = nbt.getList(InfinityConstants.INFINITY_CELL_KEYS, ListTag.TAG_COMPOUND);
+ ListTag amounts = nbt.getList(InfinityConstants.INFINITY_CELL_AMOUNTS, ListTag.TAG_COMPOUND);
+ BigInteger itemCount = new BigInteger(nbt.getByteArray(InfinityConstants.INFINITY_CELL_ITEM_COUNT));
+ // 使用加载的数据创建新的 DataStorage 实例
+ return new InfinityDataStorage(keys, amounts, itemCount);
}
}
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 6ba1080..f27f2a6 100644
--- a/src/main/java/com/extendedae_plus/util/storage/InfinityStorageManager.java
+++ b/src/main/java/com/extendedae_plus/util/storage/InfinityStorageManager.java
@@ -2,145 +2,37 @@ package com.extendedae_plus.util.storage;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.ListTag;
-import net.minecraft.nbt.NbtIo;
+import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.saveddata.SavedData;
-import net.minecraft.world.level.storage.LevelResource;
import org.jetbrains.annotations.NotNull;
-import java.io.File;
-import java.io.IOException;
-import java.nio.file.AtomicMoveNotSupportedException;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.StandardCopyOption;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.UUID;
-
-import static com.extendedae_plus.util.ExtendedAELogger.LOGGER;
+import java.math.BigInteger;
+import java.util.*;
/**
- * InfinityStorageManager
- *
- * 世界级别的持久化容器,集中管理所有 InfinityBigInteger 存储单元的序列化数据。
- * 功能要点:
- * - 在世界加载时从存档恢复所有 cell 的数据
- * - 提供按 UUID 获取/创建单个 cell 的数据容器
- * - 在世界保存时将内存数据打包为 NBT 写回存档
+ * This code is inspired by AE2Things[](https://github.com/Technici4n/AE2Things-Forge), licensed under the MIT License.
+ * Original copyright (c) Technici4n
*/
public class InfinityStorageManager extends SavedData {
- // 当前磁盘格式版本号,增加字段用于向后/向前兼容
- private static final int FORMAT_VERSION = 1;
+ // 存储所有磁盘的Map,键为UUID,值为DataStorage对象
+ private final Map cells;
- /**
- * 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<>();
-
- /**
- * 返回当前已加载的所有 UUID 的不可变视图,用于命令或调试用途
- */
- public java.util.Set getAllLoadedUUIDs() {
- return java.util.Collections.unmodifiableSet(cells.keySet());
- }
-
+ // 构造方法,初始化磁盘Map
public InfinityStorageManager() {
- setDirty();
+ cells = new HashMap<>();
+ // 标记数据为“脏”,确保新创建的实例在下次保存时写入磁盘
+ this.setDirty();
}
- /**
- * 从 NBT 构造:用于在世界加载时从存档恢复数据
- */
- public InfinityStorageManager(CompoundTag nbt) {
- // 读取格式版本,缺省视为 1(兼容旧档)
- int version = nbt.contains("format_version") ? nbt.getInt("format_version") : 1;
- if (!nbt.contains("format_version")) {
- // 旧档未包含 format_version,标记为需要写回以便下次保存写入版本号
- LOGGER.info("Found legacy InfinityStorageManager dat without format_version; treating as version {} and marking dirty for upgrade", version);
- // 立即标记为脏以触发尽快写盘(升级写入 format_version)
- this.setDirty();
- } else {
- LOGGER.info("Loading InfinityStorageManager format_version={}", version);
- }
-
- ListTag cellList = nbt.getList("list", CompoundTag.TAG_COMPOUND);
- for (int i = 0; i < cellList.size(); i++) {
- CompoundTag cell = cellList.getCompound(i);
- java.util.UUID uuid = cell.getUUID("uuid");
- CompoundTag dataTag = cell.getCompound("data");
- InfinityDataStorage data = InfinityDataStorage.loadFromNBT(dataTag);
- cells.put(uuid, data);
- LOGGER.info("Loaded InfinityDataStorage for uuid {}: keys={}, amounts={}", uuid, data.keys.size(), data.amounts.size());
- }
- setDirty();
- }
-
- /**
- * 根据给定的 ServerLevel 获取或创建该世界对应的 SavedData 实例并缓存到 INSTANCE
- */
- public static InfinityStorageManager getForLevel(ServerLevel level) {
- if (INSTANCE == null && level != null) {
- // 迁移逻辑:若旧的压缩 dat 文件存在且缺少 format_version,则读取并补上 version,备份原文件
- try {
- Path dataDir = level.getServer().getWorldPath(new LevelResource("data"));
- Path filePath = dataDir.resolve(FILE_NAME + ".dat");
- File oldFile = filePath.toFile();
- if (oldFile.exists()) {
- // 检测是否为 gzip(压缩 NBT)
- boolean isGzip = false;
- try (var fis = Files.newInputStream(filePath)) {
- int b1 = fis.read();
- int b2 = fis.read();
- isGzip = (b1 == 0x1F && b2 == 0x8B);
- } catch (IOException ignored) {
- }
- if (isGzip) {
- try {
- var root = NbtIo.readCompressed(oldFile);
- if (!root.contains("format_version")) {
- // 备份旧文件
- Path bak = filePath.resolveSibling(FILE_NAME + ".dat.bak");
- try {
- Files.copy(filePath, bak);
- } catch (IOException ignored) {
- }
- // 写回并加入 format_version
- root.putInt("format_version", FORMAT_VERSION);
- Path tmp = filePath.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, filePath, StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.ATOMIC_MOVE);
- } catch (AtomicMoveNotSupportedException ex) {
- Files.move(tmp, filePath, StandardCopyOption.REPLACE_EXISTING);
- }
- LOGGER.info("Migrated old compressed {} to include format_version", filePath);
- }
- } catch (IOException ex) {
- LOGGER.info("Failed to migrate old compressed file {}: {}", filePath, ex.getMessage());
- }
- }
- }
- } catch (Throwable ignored) {
- }
-
- INSTANCE = level.getDataStorage().computeIfAbsent(InfinityStorageManager::new, InfinityStorageManager::new, FILE_NAME);
- }
- return INSTANCE;
+ // 私有构造方法,用于从已有Map创建StorageManager
+ private InfinityStorageManager(Map cells) {
+ // 确保使用已加载的数据
+ this.cells = cells;
+ // 标记数据为“脏”,确保新创建的实例在下次保存时写入磁盘
+ this.setDirty();
}
@Override
@@ -149,83 +41,96 @@ public class InfinityStorageManager extends SavedData {
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());
+ cell.putUUID(InfinityConstants.INFINITY_CELL_UUID, entry.getKey());
+ cell.put(InfinityConstants.INFINITY_CELL_DATA, entry.getValue().serializeNBT());
cellList.add(cell);
}
- nbt.put("list", cellList);
+ nbt.put(InfinityConstants.INFINITY_CELL_LIST, cellList);
// 写入当前格式版本号,便于未来迁移与兼容判断
- nbt.putInt("format_version", FORMAT_VERSION);
+ nbt.putInt(InfinityConstants.FORMAT_VERSION_FIELD, InfinityConstants.FORMAT_VERSION);
return nbt;
}
- /**
- * 更新或添加某个 UUID 对应的数据并标记为脏(需要保存)
- */
+ // 静态方法,从 NBT 数据反序列化创建 StorageManager 实例
+ public static InfinityStorageManager readNbt(CompoundTag nbt) {
+ // 读取格式版本,缺省视为 1(兼容旧档)
+ int version = nbt.contains(InfinityConstants.FORMAT_VERSION_FIELD) ?
+ nbt.getInt(InfinityConstants.FORMAT_VERSION_FIELD) :
+ 1;
+
+ Map cells = new HashMap<>();
+ // 从 NBT 中获取磁盘数据列表,指定类型为 CompoundTag(TAG_COMPOUND)
+ ListTag cellList = nbt.getList(InfinityConstants.INFINITY_CELL_LIST, CompoundTag.TAG_COMPOUND);
+ // 遍历 cellList 中的每个 CompoundTag
+ for (int i = 0; i < cellList.size(); i++) {
+ // 获取当前索引的 CompoundTag,表示单个磁盘的数据
+ CompoundTag cell = cellList.getCompound(i);
+ // 从 CompoundTag 中读取 UUID 和 DataStorage 数据,并存入 cells 映射
+ cells.put(cell.getUUID(InfinityConstants.INFINITY_CELL_UUID), InfinityDataStorage.loadFromNBT(cell.getCompound(InfinityConstants.INFINITY_CELL_DATA)));
+ }
+ // 使用加载的 cells 数据创建新的 StorageManager 实例
+ return new InfinityStorageManager(cells);
+ }
+
+ // 返回当前已加载的所有 UUID 的不可变视图,用于命令或调试用途
+ public Set getAllLoadedUUIDs() {
+ return Collections.unmodifiableSet(cells.keySet());
+ }
+
+
+ // 更新或添加某个 UUID 对应的数据并标记为脏(需要保存)
public void updateCell(UUID uuid, InfinityDataStorage infinityDataStorage) {
cells.put(uuid, infinityDataStorage);
+ // 标记数据为“脏”,确保修改后的数据会在下次保存时写入磁盘
setDirty();
}
- /**
- * 获取或创建某个 UUID 对应的数据容器
- */
+ // 删除某个 UUID 的持久化记录并标记为脏
+ public void removeCell(UUID uuid) {
+ cells.remove(uuid);
+ // 标记数据为“脏”,确保移除操作会在下次保存时反映到磁盘
+ setDirty();
+ }
+
+ // 检查指定 UUID 是否存在于 disks 映射中
+ public boolean hasUUID(UUID uuid) {
+ // 返回 cells 映射是否包含指定 UUID
+ return cells.containsKey(uuid);
+ }
+
+ // 获取或创建某个 UUID 对应的数据容器
public InfinityDataStorage getOrCreateCell(UUID uuid) {
+ // 检查 cells 映射中是否不存在指定 UUID
if (!cells.containsKey(uuid)) {
updateCell(uuid, new InfinityDataStorage());
}
+ // 返回指定 UUID 对应的 DataStorage 对象
return cells.get(uuid);
}
- /**
- * 修改某个 UUID 对应的键与数量列表并保存(新的签名,stackAmounts 为 ListTag 字符串列表)
- */
- public void modifyCell(UUID cellID, ListTag stackKeys, ListTag stackAmounts) {
- InfinityDataStorage cellToModify = getOrCreateCell(cellID);
- if (stackKeys != null && stackAmounts != null) {
- cellToModify.keys = stackKeys;
- cellToModify.amounts = stackAmounts;
+ // 修改指定 UUID 的磁盘数据,包括堆栈键、数量和总项目数
+ public void modifyDisk(UUID uuid, ListTag keys, ListTag amounts, BigInteger itemCount) {
+ // 获取或创建指定 UUID 的 DataStorage 对象
+ InfinityDataStorage cellToModify = getOrCreateCell(uuid);
+ if (keys != null && amounts != null) {
+ cellToModify.keys = keys;
+ cellToModify.amounts = amounts;
}
- updateCell(cellID, cellToModify);
+ // 更新 DataStorage 的 itemCount 字段
+ cellToModify.itemCount = itemCount;
+ // 将修改后的 DataStorage 对象更新到 cells 映射
+ updateCell(uuid, cellToModify);
}
- /**
- * 删除某个 UUID 的持久化记录并标记为脏
- */
- public void removeCell(UUID uuid) {
- cells.remove(uuid);
- setDirty();
- }
-
- /**
- * 强制将内存中的所有 InfinityDataStorage 写入磁盘
- * 可在服务器停止或 tick 中调用,确保数据不会丢失
- */
- public void forceSaveAll(ServerLevel level) {
- try {
- if (level != null) {
- this.setDirty();
- CompoundTag nbt = new CompoundTag();
- this.save(nbt);
- File file = level.getServer().getWorldPath(new LevelResource("data"))
- .resolve(FILE_NAME + ".dat").toFile();
- NbtIo.writeCompressed(nbt, file);
-
- // 打印所有已保存的无限磁盘 UUID 及相关信息
- StringBuilder sb = new StringBuilder();
- sb.append("Saving Infinity Disks (UUIDs and info):\n");
- for (Map.Entry entry : cells.entrySet()) {
- UUID uuid = entry.getKey();
- InfinityDataStorage data = entry.getValue();
- int types = (data.keys != null) ? data.keys.size() : 0;
- sb.append(" - UUID: ").append(uuid)
- .append(", Types: ").append(types)
- .append("\n");
- }
- LOGGER.info(sb.toString());
- }
- } catch (Throwable ex) {
- LOGGER.info("InfinityStorageManager forceSaveAll error: {}", ex.getMessage());
- }
+ // 静态方法,获取 StorageManager 的单例实例
+ public static InfinityStorageManager getInstance(MinecraftServer server) {
+ ServerLevel world = server.getLevel(ServerLevel.OVERWORLD);
+ // 使用 DataStorage 的 computeIfAbsent 方法加载或创建 StorageManager 实例
+ // 如果数据存在,则调用 readNbt 加载;否则调用默认构造器创建新实例
+ return world.getDataStorage().computeIfAbsent(
+ InfinityStorageManager::readNbt,
+ InfinityStorageManager::new,
+ InfinityConstants.SAVE_FILE_NAME
+ );
}
}
From 6b8590c2f29f6e466747ad101f4b75488466eed4 Mon Sep 17 00:00:00 2001
From: GaLicn <133291877+GaLicn@users.noreply.github.com>
Date: Tue, 23 Sep 2025 18:38:13 +0800
Subject: [PATCH 25/34] =?UTF-8?q?=E5=BD=93appflux=E5=AD=98=E5=9C=A8?=
=?UTF-8?q?=E6=97=B6=E4=B8=BA=E6=A0=B7=E6=9D=BF=E4=BE=9B=E5=BA=94=E5=99=A8?=
=?UTF-8?q?=E5=A2=9E=E5=8A=A0=E4=B8=80=E4=B8=AA=E5=8D=87=E7=BA=A7=E6=A7=BD?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
build.gradle | 2 +-
.../java/com/extendedae_plus/mixin/MixinConditions.java | 6 ++++++
src/main/resources/extendedae_plus.mixins.json | 1 +
3 files changed, 8 insertions(+), 1 deletion(-)
diff --git a/build.gradle b/build.gradle
index f22d7c4..25fe6a4 100644
--- a/build.gradle
+++ b/build.gradle
@@ -81,7 +81,7 @@ dependencies {
annotationProcessor "org.spongepowered:mixin:${mixin_version}:processor"
- // modImplementation "curse.maven:applied-flux-965012:6755986"
+ modImplementation "curse.maven:applied-flux-965012:6755986"
modCompileOnly "curse.maven:mega-cells-622112:${mega_cells_version}"
modCompileOnly "curse.maven:jade-324717:${jade_version}"
diff --git a/src/main/java/com/extendedae_plus/mixin/MixinConditions.java b/src/main/java/com/extendedae_plus/mixin/MixinConditions.java
index ed45cbe..30c8213 100644
--- a/src/main/java/com/extendedae_plus/mixin/MixinConditions.java
+++ b/src/main/java/com/extendedae_plus/mixin/MixinConditions.java
@@ -52,6 +52,12 @@ public class MixinConditions implements IMixinConfigPlugin {
}
}
+ // 对于appflux相关的Mixin,总是加载但在运行时检查条件
+ if (mixinClassName.contains("AppfluxPatternProviderLogicMixin")) {
+ System.out.println("[ExtendedAE_Plus] 总是加载appflux Mixin,运行时检查条件: " + mixinClassName);
+ return true; // 总是加载,在Mixin内部进行运行时检查
+ }
+
// 其他Mixin正常应用
System.out.println("[ExtendedAE_Plus] 加载Mixin: " + mixinClassName);
return true;
diff --git a/src/main/resources/extendedae_plus.mixins.json b/src/main/resources/extendedae_plus.mixins.json
index 5c81819..c266b12 100644
--- a/src/main/resources/extendedae_plus.mixins.json
+++ b/src/main/resources/extendedae_plus.mixins.json
@@ -66,6 +66,7 @@
"ae2.compat.PatternProviderLogicCompatMixin",
"ae2.compat.PatternProviderLogicHostCompatMixin",
"ae2.compat.PatternProviderCompatMixin",
+ "appflux.AppfluxPatternProviderLogicMixin",
"ae2.helpers.patternprovider.PatternProviderLogicTickerMixin",
"ae2.parts.AEBasePartClientSyncMixin",
"ae2.parts.automation.IOBusPartChannelCardMixin",
From 66b581c2a702858f1289c8a77d5ed53039a019c3 Mon Sep 17 00:00:00 2001
From: GaLicn <133291877+GaLicn@users.noreply.github.com>
Date: Tue, 23 Sep 2025 18:53:27 +0800
Subject: [PATCH 26/34] =?UTF-8?q?=E4=B8=BAme=E6=8E=A5=E5=8F=A3=E5=A2=9E?=
=?UTF-8?q?=E5=8A=A0=E4=B8=80=E4=B8=AA=E5=8D=87=E7=BA=A7=E6=A7=BD=EF=BC=8C?=
=?UTF-8?q?=E5=B9=B6=E4=B8=94=E5=85=BC=E5=AE=B9applied=20flux?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
build.gradle | 2 +-
.../com/extendedae_plus/compat/UpgradeSlotCompat.java | 10 +++++++++-
.../com/extendedae_plus/mixin/MixinConditions.java | 6 ++++++
src/main/resources/extendedae_plus.mixins.json | 3 ++-
4 files changed, 18 insertions(+), 3 deletions(-)
diff --git a/build.gradle b/build.gradle
index 25fe6a4..f22d7c4 100644
--- a/build.gradle
+++ b/build.gradle
@@ -81,7 +81,7 @@ dependencies {
annotationProcessor "org.spongepowered:mixin:${mixin_version}:processor"
- modImplementation "curse.maven:applied-flux-965012:6755986"
+ // modImplementation "curse.maven:applied-flux-965012:6755986"
modCompileOnly "curse.maven:mega-cells-622112:${mega_cells_version}"
modCompileOnly "curse.maven:jade-324717:${jade_version}"
diff --git a/src/main/java/com/extendedae_plus/compat/UpgradeSlotCompat.java b/src/main/java/com/extendedae_plus/compat/UpgradeSlotCompat.java
index cef62a5..9768050 100644
--- a/src/main/java/com/extendedae_plus/compat/UpgradeSlotCompat.java
+++ b/src/main/java/com/extendedae_plus/compat/UpgradeSlotCompat.java
@@ -27,12 +27,20 @@ import java.util.List;
public class UpgradeSlotCompat {
private static final String APPFLUX_MOD_ID = "appflux";
+ /**
+ * 检测Applied Flux模组是否存在
+ * @return true如果存在,false如果不存在
+ */
+ public static boolean isAppfluxPresent() {
+ return ModList.get().isLoaded(APPFLUX_MOD_ID);
+ }
+
/**
* 检测是否应该启用我们的升级卡槽功能
* @return true如果应该启用,false如果检测到appflux模组存在
*/
public static boolean shouldEnableUpgradeSlots() {
- boolean appfluxExists = ModList.get().isLoaded(APPFLUX_MOD_ID);
+ boolean appfluxExists = isAppfluxPresent();
ExtendedAELogger.LOGGER.info("ExtendedAE-appflux模组检测: {}", appfluxExists ? "存在" : "不存在");
if (appfluxExists) {
diff --git a/src/main/java/com/extendedae_plus/mixin/MixinConditions.java b/src/main/java/com/extendedae_plus/mixin/MixinConditions.java
index 30c8213..7a47b91 100644
--- a/src/main/java/com/extendedae_plus/mixin/MixinConditions.java
+++ b/src/main/java/com/extendedae_plus/mixin/MixinConditions.java
@@ -58,6 +58,12 @@ public class MixinConditions implements IMixinConfigPlugin {
return true; // 总是加载,在Mixin内部进行运行时检查
}
+ // 对于InterfaceLogicUpgradesMixin,总是加载但在运行时检查条件
+ if (mixinClassName.contains("InterfaceLogicUpgradesMixin")) {
+ System.out.println("[ExtendedAE_Plus] 总是加载Interface升级Mixin,运行时检查条件: " + mixinClassName);
+ return true; // 总是加载,在Mixin内部进行运行时检查
+ }
+
// 其他Mixin正常应用
System.out.println("[ExtendedAE_Plus] 加载Mixin: " + mixinClassName);
return true;
diff --git a/src/main/resources/extendedae_plus.mixins.json b/src/main/resources/extendedae_plus.mixins.json
index c266b12..97c7ce5 100644
--- a/src/main/resources/extendedae_plus.mixins.json
+++ b/src/main/resources/extendedae_plus.mixins.json
@@ -80,7 +80,8 @@
"extendedae.container.ContainerExPatternTerminalMixin",
"extendedae.container.ContainerWirelessExPatternTerminalMixin",
"ae2.helpers.InterfaceLogicChannelCardMixin",
- "ae2.helpers.InterfaceLogicTickerMixin"
+ "ae2.helpers.InterfaceLogicTickerMixin",
+ "ae2.InterfaceLogicUpgradesMixin"
],
"injectors": {
"defaultRequire": 1
From 476b66947274f51d1bf8919d89d8d3fc2a313341 Mon Sep 17 00:00:00 2001
From: GaLicn <133291877+GaLicn@users.noreply.github.com>
Date: Tue, 23 Sep 2025 19:03:23 +0800
Subject: [PATCH 27/34] =?UTF-8?q?=E4=B8=BAme=E6=8E=A5=E5=8F=A3=E5=A2=9E?=
=?UTF-8?q?=E5=8A=A0=E4=B8=80=E4=B8=AA=E5=8D=87=E7=BA=A7=E6=A7=BD=EF=BC=8C?=
=?UTF-8?q?=E5=B9=B6=E4=B8=94=E5=85=BC=E5=AE=B9applied=20flux?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
build.gradle | 2 +-
.../ae2/InterfaceLogicUpgradesMixin.java | 80 ++++++
...PatternProviderLogicHostUpgradesMixin.java | 24 --
.../PatternProviderLogicUpgradesMixin.java | 272 ------------------
.../AppfluxPatternProviderLogicMixin.java | 79 +++++
5 files changed, 160 insertions(+), 297 deletions(-)
create mode 100644 src/main/java/com/extendedae_plus/mixin/ae2/InterfaceLogicUpgradesMixin.java
delete mode 100644 src/main/java/com/extendedae_plus/mixin/ae2/helpers/patternprovider/PatternProviderLogicHostUpgradesMixin.java
delete mode 100644 src/main/java/com/extendedae_plus/mixin/ae2/helpers/patternprovider/PatternProviderLogicUpgradesMixin.java
create mode 100644 src/main/java/com/extendedae_plus/mixin/appflux/AppfluxPatternProviderLogicMixin.java
diff --git a/build.gradle b/build.gradle
index f22d7c4..25fe6a4 100644
--- a/build.gradle
+++ b/build.gradle
@@ -81,7 +81,7 @@ dependencies {
annotationProcessor "org.spongepowered:mixin:${mixin_version}:processor"
- // modImplementation "curse.maven:applied-flux-965012:6755986"
+ modImplementation "curse.maven:applied-flux-965012:6755986"
modCompileOnly "curse.maven:mega-cells-622112:${mega_cells_version}"
modCompileOnly "curse.maven:jade-324717:${jade_version}"
diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/InterfaceLogicUpgradesMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/InterfaceLogicUpgradesMixin.java
new file mode 100644
index 0000000..0bc4575
--- /dev/null
+++ b/src/main/java/com/extendedae_plus/mixin/ae2/InterfaceLogicUpgradesMixin.java
@@ -0,0 +1,80 @@
+package com.extendedae_plus.mixin.ae2;
+
+import appeng.api.networking.IManagedGridNode;
+import appeng.api.upgrades.IUpgradeInventory;
+import appeng.api.upgrades.UpgradeInventories;
+import appeng.helpers.InterfaceLogic;
+import appeng.helpers.InterfaceLogicHost;
+import com.extendedae_plus.compat.UpgradeSlotCompat;
+import net.minecraft.world.item.Item;
+import org.spongepowered.asm.mixin.Final;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.Mutable;
+import org.spongepowered.asm.mixin.Shadow;
+import org.spongepowered.asm.mixin.injection.At;
+import org.spongepowered.asm.mixin.injection.Inject;
+import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
+
+/**
+ * 为ME接口增加升级槽数量的Mixin
+ * 兼容Applied Flux模组,避免冲突
+ */
+@Mixin(value = InterfaceLogic.class, remap = false, priority = 1500)
+public class InterfaceLogicUpgradesMixin {
+
+ @Final
+ @Mutable
+ @Shadow
+ private IUpgradeInventory upgrades;
+
+ @Shadow
+ protected void onUpgradesChanged() {}
+
+ /**
+ * 在InterfaceLogic构造函数末尾注入,增加升级槽数量
+ * 使用高优先级(1500)确保在Applied Flux之后执行
+ */
+ @Inject(
+ method = "(Lappeng/api/networking/IManagedGridNode;Lappeng/helpers/InterfaceLogicHost;Lnet/minecraft/world/item/Item;I)V",
+ at = @At("TAIL")
+ )
+ private void expandInterfaceUpgrades(IManagedGridNode gridNode, InterfaceLogicHost host, Item is, int slots, CallbackInfo ci) {
+ try {
+ System.out.println("[ExtendedAE_Plus] InterfaceLogic升级槽扩展开始");
+
+ // 检查Applied Flux是否已经修改了升级槽
+ if (UpgradeSlotCompat.isAppfluxPresent()) {
+ System.out.println("[ExtendedAE_Plus] 检测到Applied Flux存在");
+ // Applied Flux存在,检查当前升级槽数量
+ int currentSlots = this.upgrades.size();
+ System.out.println("[ExtendedAE_Plus] 当前升级槽数量: " + currentSlots);
+
+ if (currentSlots >= 3) {
+ // Applied Flux已经增加了升级槽到3个或更多,我们不需要再修改
+ System.out.println("[ExtendedAE_Plus] Applied Flux已经增加了足够的升级槽,跳过修改");
+ return;
+ } else if (currentSlots == 2) {
+ // Applied Flux增加到2个,我们再增加1个到3个
+ System.out.println("[ExtendedAE_Plus] Applied Flux增加到2个槽,我们再增加到3个");
+ this.upgrades = UpgradeInventories.forMachine(is, 3, this::onUpgradesChanged);
+ } else {
+ // Applied Flux可能还没有生效,我们直接增加到3个
+ System.out.println("[ExtendedAE_Plus] Applied Flux可能未生效,直接增加到3个槽");
+ this.upgrades = UpgradeInventories.forMachine(is, 3, this::onUpgradesChanged);
+ }
+ } else {
+ System.out.println("[ExtendedAE_Plus] Applied Flux不存在");
+ // Applied Flux不存在,我们将升级槽从1个增加到2个
+ int currentSlots = this.upgrades.size();
+ System.out.println("[ExtendedAE_Plus] 当前升级槽数量: " + currentSlots + ",增加到2个");
+ this.upgrades = UpgradeInventories.forMachine(is, 2, this::onUpgradesChanged);
+ }
+
+ System.out.println("[ExtendedAE_Plus] InterfaceLogic升级槽扩展完成,最终槽数: " + this.upgrades.size());
+ } catch (Exception e) {
+ // 发生异常时不修改升级槽,确保不会崩溃
+ System.err.println("[ExtendedAE_Plus] Failed to expand interface upgrades: " + e.getMessage());
+ e.printStackTrace();
+ }
+ }
+}
diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/helpers/patternprovider/PatternProviderLogicHostUpgradesMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/helpers/patternprovider/PatternProviderLogicHostUpgradesMixin.java
deleted file mode 100644
index 47f27dc..0000000
--- a/src/main/java/com/extendedae_plus/mixin/ae2/helpers/patternprovider/PatternProviderLogicHostUpgradesMixin.java
+++ /dev/null
@@ -1,24 +0,0 @@
-package com.extendedae_plus.mixin.ae2.helpers.patternprovider;
-
-import appeng.api.upgrades.IUpgradeInventory;
-import appeng.api.upgrades.IUpgradeableObject;
-import appeng.helpers.patternprovider.PatternProviderLogic;
-import appeng.helpers.patternprovider.PatternProviderLogicHost;
-import org.spongepowered.asm.mixin.Mixin;
-import org.spongepowered.asm.mixin.Shadow;
-
-/**
- * 让 PatternProviderLogicHost 作为 IUpgradeableObject 的代理,菜单可从 host 获取升级槽。
- */
-@Mixin(value = PatternProviderLogicHost.class, priority = 2000, remap = false)
-public interface PatternProviderLogicHostUpgradesMixin extends IUpgradeableObject {
- @Shadow PatternProviderLogic getLogic();
-
- @Override
- default IUpgradeInventory getUpgrades() {
- if (!com.extendedae_plus.compat.UpgradeSlotCompat.shouldEnableUpgradeSlots()) {
- return appeng.api.upgrades.UpgradeInventories.empty();
- }
- return ((IUpgradeableObject) this.getLogic()).getUpgrades();
- }
-}
diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/helpers/patternprovider/PatternProviderLogicUpgradesMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/helpers/patternprovider/PatternProviderLogicUpgradesMixin.java
deleted file mode 100644
index 59f06c9..0000000
--- a/src/main/java/com/extendedae_plus/mixin/ae2/helpers/patternprovider/PatternProviderLogicUpgradesMixin.java
+++ /dev/null
@@ -1,272 +0,0 @@
-package com.extendedae_plus.mixin.ae2.helpers.patternprovider;
-
-import appeng.api.networking.IManagedGridNode;
-import appeng.api.networking.security.IActionSource;
-import appeng.api.upgrades.IUpgradeInventory;
-import appeng.api.upgrades.IUpgradeableObject;
-import appeng.api.upgrades.UpgradeInventories;
-import appeng.helpers.patternprovider.PatternProviderLogic;
-import appeng.helpers.patternprovider.PatternProviderLogicHost;
-import com.extendedae_plus.ae.items.ChannelCardItem;
-import com.extendedae_plus.compat.UpgradeSlotCompat;
-import com.extendedae_plus.init.ModItems;
-import com.extendedae_plus.wireless.WirelessSlaveLink;
-import com.extendedae_plus.wireless.endpoint.GenericNodeEndpointImpl;
-import com.extendedae_plus.bridge.InterfaceWirelessLinkBridge;
-import org.spongepowered.asm.mixin.Final;
-import org.spongepowered.asm.mixin.Mixin;
-import org.spongepowered.asm.mixin.Shadow;
-import org.spongepowered.asm.mixin.Unique;
-import org.spongepowered.asm.mixin.injection.At;
-import org.spongepowered.asm.mixin.injection.Inject;
-import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
-
-import net.minecraft.nbt.CompoundTag;
-import net.minecraft.world.item.ItemStack;
-
-import java.util.List;
-
-/**
- * 为 PatternProviderLogic 注入升级槽,实现 IUpgradeableObject。
- * 仅负责升级槽的持久化/掉落/清空与初始化,不改变原有逻辑。
- */
-@Mixin(value = PatternProviderLogic.class, priority = 2000, remap = false)
-public abstract class PatternProviderLogicUpgradesMixin implements IUpgradeableObject, InterfaceWirelessLinkBridge {
- @Unique
- private IUpgradeInventory eap$upgrades = UpgradeInventories.empty();
-
- @Unique
- private WirelessSlaveLink eap$link;
-
- @Unique
- private long eap$lastChannel = -1;
-
- @Unique
- private boolean eap$clientConnected = false;
-
- @Unique
- private boolean eap$hasInitialized = false;
-
- @Unique
- private int eap$delayedInitTicks = 0;
-
- @Final
- @Shadow
- private PatternProviderLogicHost host;
-
- @Final
- @Shadow
- private IManagedGridNode mainNode;
-
- @Final
- @Shadow
- private IActionSource actionSource;
-
- @Unique
- private void eap$onUpgradesChanged() {
- this.host.saveChanges();
- // 升级变更,重置并尝试初始化
- eap$lastChannel = -1;
- eap$hasInitialized = false;
- eap$initializeChannelLink();
- }
-
-
-
- @Inject(method = "(Lappeng/api/networking/IManagedGridNode;Lappeng/helpers/patternprovider/PatternProviderLogicHost;I)V",
- at = @At("TAIL"))
- private void eap$initUpgrades(IManagedGridNode mainNode, PatternProviderLogicHost host, int patternInventorySize, CallbackInfo ci) {
- // 只有在应该启用升级卡槽时才初始化
- if (!UpgradeSlotCompat.shouldEnableUpgradeSlots()) {
- return;
- }
-
- this.eap$upgrades = UpgradeInventories.forMachine(host.getTerminalIcon().getItem(), 1, this::eap$onUpgradesChanged);
- }
-
- @Inject(method = "writeToNBT", at = @At("TAIL"))
- private void eap$saveUpgrades(CompoundTag tag, CallbackInfo ci) {
- if (!UpgradeSlotCompat.shouldEnableUpgradeSlots()) {
- return;
- }
- this.eap$upgrades.writeToNBT(tag, "upgrades");
- }
-
- @Inject(method = "readFromNBT", at = @At("TAIL"))
- private void eap$loadUpgrades(CompoundTag tag, CallbackInfo ci) {
- if (!UpgradeSlotCompat.shouldEnableUpgradeSlots()) {
- return;
- }
- this.eap$upgrades.readFromNBT(tag, "upgrades");
- // 从 NBT 加载后,重置并尝试初始化(可能刚进入世界)
- eap$lastChannel = -1;
- eap$hasInitialized = false;
- eap$initializeChannelLink();
- }
-
- @Inject(method = "addDrops", at = @At("TAIL"))
- private void eap$dropUpgrades(List drops, CallbackInfo ci) {
- if (!UpgradeSlotCompat.shouldEnableUpgradeSlots()) {
- return;
- }
- for (var stack : this.eap$upgrades) {
- if (!stack.isEmpty()) {
- drops.add(stack);
- }
- }
- }
-
- @Inject(method = "clearContent", at = @At("TAIL"))
- private void eap$clearUpgrades(CallbackInfo ci) {
- if (!UpgradeSlotCompat.shouldEnableUpgradeSlots()) {
- return;
- }
- this.eap$upgrades.clear();
- }
-
- @Override
- public void eap$updateWirelessLink() {
- if (eap$link != null) {
- eap$link.updateStatus();
- }
- }
-
- @Override
- public IUpgradeInventory getUpgrades() {
- if (!UpgradeSlotCompat.shouldEnableUpgradeSlots()) {
- return UpgradeInventories.empty();
- }
- return this.eap$upgrades;
- }
-
- // ===== 频道卡初始化与延迟重试(服务端) =====
- @Unique
- public void eap$initializeChannelLink() {
- // 客户端早退
- if (host.getBlockEntity() != null && host.getBlockEntity().getLevel() != null && host.getBlockEntity().getLevel().isClientSide) {
- return;
- }
-
- // 避免重复初始化
- if (eap$hasInitialized) {
- return;
- }
-
- // 等待网格完成引导
- if (!mainNode.hasGridBooted()) {
- // 安排短延迟,等待后续 tick 再试
- eap$delayedInitTicks = Math.max(eap$delayedInitTicks, 5);
- try {
- mainNode.ifPresent((grid, node) -> {
- try { grid.getTickManager().wakeDevice(node); } catch (Throwable ignored) {}
- });
- } catch (Throwable ignored) {}
- return;
- }
-
- try {
- long channel = 0L;
- boolean found = false;
- for (ItemStack stack : this.eap$upgrades) {
- if (!stack.isEmpty() && stack.getItem() == ModItems.CHANNEL_CARD.get()) {
- channel = ChannelCardItem.getChannel(stack);
- found = true;
- break;
- }
- }
-
- if (!found) {
- // 无频道卡:断开并视为初始化完成
- if (eap$link != null) {
- eap$link.setFrequency(0L);
- eap$link.updateStatus();
- }
- eap$hasInitialized = true;
- return;
- }
-
- if (eap$link == null) {
- var endpoint = new GenericNodeEndpointImpl(() -> host.getBlockEntity(), () -> this.mainNode.getNode());
- eap$link = new WirelessSlaveLink(endpoint);
- }
-
- eap$link.setFrequency(channel);
- eap$link.updateStatus();
-
- if (eap$link.isConnected()) {
- eap$hasInitialized = true;
- } else {
- eap$hasInitialized = false;
- eap$delayedInitTicks = Math.max(eap$delayedInitTicks, 5);
- try {
- mainNode.ifPresent((grid, node) -> {
- try { grid.getTickManager().wakeDevice(node); } catch (Throwable ignored) {}
- });
- } catch (Throwable ignored) {}
- }
- } catch (Exception ignored) {
- }
- }
-
- @Override
- public void eap$setClientWirelessState(boolean connected) {
- eap$clientConnected = connected;
- }
-
- @Override
- public boolean eap$isWirelessConnected() {
- if (host.getBlockEntity() != null && host.getBlockEntity().getLevel() != null && host.getBlockEntity().getLevel().isClientSide) {
- return eap$clientConnected;
- } else {
- return eap$link != null && eap$link.isConnected();
- }
- }
-
- @Override
- public boolean eap$hasTickInitialized() {
- return eap$hasInitialized;
- }
-
- @Override
- public void eap$setTickInitialized(boolean initialized) {
- eap$hasInitialized = initialized;
- }
-
- @Override
- public void eap$handleDelayedInit() {
- // 仅服务端
- if (host.getBlockEntity() != null && host.getBlockEntity().getLevel() != null && host.getBlockEntity().getLevel().isClientSide) {
- return;
- }
- if (!eap$hasInitialized) {
- if (!mainNode.hasGridBooted()) {
- if (eap$delayedInitTicks > 0) {
- eap$delayedInitTicks--;
- }
- if (eap$delayedInitTicks == 0) {
- eap$delayedInitTicks = 5;
- try {
- mainNode.ifPresent((grid, node) -> {
- try { grid.getTickManager().wakeDevice(node); } catch (Throwable ignored) {}
- });
- } catch (Throwable ignored) {}
- }
- } else {
- eap$initializeChannelLink();
- }
- }
- }
-
- // 当主节点状态变化(例如 GRID_BOOT 完成)时,安排一次延迟初始化并唤醒设备
- @Inject(method = "onMainNodeStateChanged", at = @At("TAIL"))
- private void eap$onMainNodeStateChangedTail(CallbackInfo ci) {
- eap$lastChannel = -1;
- eap$hasInitialized = false;
- eap$delayedInitTicks = 10;
- try {
- mainNode.ifPresent((grid, node) -> {
- try { grid.getTickManager().wakeDevice(node); } catch (Throwable ignored) {}
- });
- } catch (Throwable ignored) {}
- }
-}
diff --git a/src/main/java/com/extendedae_plus/mixin/appflux/AppfluxPatternProviderLogicMixin.java b/src/main/java/com/extendedae_plus/mixin/appflux/AppfluxPatternProviderLogicMixin.java
new file mode 100644
index 0000000..201121e
--- /dev/null
+++ b/src/main/java/com/extendedae_plus/mixin/appflux/AppfluxPatternProviderLogicMixin.java
@@ -0,0 +1,79 @@
+package com.extendedae_plus.mixin.appflux;
+
+import appeng.api.networking.IManagedGridNode;
+import appeng.api.upgrades.IUpgradeInventory;
+import appeng.api.upgrades.UpgradeInventories;
+import appeng.helpers.patternprovider.PatternProviderLogic;
+import appeng.helpers.patternprovider.PatternProviderLogicHost;
+import com.extendedae_plus.compat.UpgradeSlotCompat;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.Shadow;
+import org.spongepowered.asm.mixin.injection.At;
+import org.spongepowered.asm.mixin.injection.Inject;
+import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
+
+import java.lang.reflect.Field;
+
+/**
+ * 当appflux存在时,修改PatternProviderLogic的升级槽数量为2个
+ * 优先级设置为2000,确保在appflux之后应用
+ */
+@Mixin(value = PatternProviderLogic.class, priority = 2000, remap = false)
+public class AppfluxPatternProviderLogicMixin {
+
+ /**
+ * 在appflux初始化升级槽之后,替换为2个槽的版本
+ */
+ @Inject(method = "(Lappeng/api/networking/IManagedGridNode;Lappeng/helpers/patternprovider/PatternProviderLogicHost;I)V",
+ at = @At("TAIL"))
+ private void eap$modifyAppfluxUpgradeSlots(IManagedGridNode mainNode, PatternProviderLogicHost host, int patternInventorySize, CallbackInfo ci) {
+ try {
+ com.extendedae_plus.util.ExtendedAELogger.LOGGER.info("AppfluxPatternProviderLogicMixin被调用!");
+
+ // 只有当appflux存在且不启用我们的升级槽时才修改数量
+ if (!UpgradeSlotCompat.shouldEnableUpgradeSlots() && UpgradeSlotCompat.shouldEnableChannelCard()) {
+ com.extendedae_plus.util.ExtendedAELogger.LOGGER.info("尝试修改appflux升级槽数量为2个");
+
+ // 使用反射找到appflux的升级槽字段并替换
+ try {
+ Field upgradesField = this.getClass().getDeclaredField("af_$upgrades");
+ upgradesField.setAccessible(true);
+ IUpgradeInventory currentUpgrades = (IUpgradeInventory) upgradesField.get(this);
+
+ if (currentUpgrades != null) {
+ com.extendedae_plus.util.ExtendedAELogger.LOGGER.info("找到appflux升级槽,当前大小: {}", currentUpgrades.size());
+
+ // 创建新的2槽升级槽
+ IUpgradeInventory newUpgrades = UpgradeInventories.forMachine(
+ host.getTerminalIcon().getItem(),
+ 2,
+ () -> {
+ try {
+ // 调用appflux的升级变更方法
+ this.getClass().getDeclaredMethod("af_$onUpgradesChanged").invoke(this);
+ } catch (Exception e) {
+ com.extendedae_plus.util.ExtendedAELogger.LOGGER.error("调用appflux升级变更方法失败", e);
+ }
+ }
+ );
+
+ // 复制原有升级卡到新的升级槽
+ for (int i = 0; i < Math.min(currentUpgrades.size(), newUpgrades.size()); i++) {
+ if (!currentUpgrades.getStackInSlot(i).isEmpty()) {
+ newUpgrades.insertItem(i, currentUpgrades.getStackInSlot(i).copy(), false);
+ }
+ }
+
+ // 替换升级槽
+ upgradesField.set(this, newUpgrades);
+ com.extendedae_plus.util.ExtendedAELogger.LOGGER.info("成功将appflux升级槽替换为2个槽");
+ }
+ } catch (Exception e) {
+ com.extendedae_plus.util.ExtendedAELogger.LOGGER.error("反射修改appflux升级槽失败", e);
+ }
+ }
+ } catch (Exception e) {
+ com.extendedae_plus.util.ExtendedAELogger.LOGGER.error("AppfluxPatternProviderLogicMixin执行失败", e);
+ }
+ }
+}
From 039fdc3f90913f1a5cbd04fcc738cf4985188e39 Mon Sep 17 00:00:00 2001
From: GaLicn <133291877+GaLicn@users.noreply.github.com>
Date: Tue, 23 Sep 2025 19:08:20 +0800
Subject: [PATCH 28/34] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E6=A3=80=E6=9F=A5?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../ae2/InterfaceLogicUpgradesMixin.java | 44 ++++++++++++++-----
1 file changed, 32 insertions(+), 12 deletions(-)
diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/InterfaceLogicUpgradesMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/InterfaceLogicUpgradesMixin.java
index 0bc4575..bf3bb9b 100644
--- a/src/main/java/com/extendedae_plus/mixin/ae2/InterfaceLogicUpgradesMixin.java
+++ b/src/main/java/com/extendedae_plus/mixin/ae2/InterfaceLogicUpgradesMixin.java
@@ -19,7 +19,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
* 为ME接口增加升级槽数量的Mixin
* 兼容Applied Flux模组,避免冲突
*/
-@Mixin(value = InterfaceLogic.class, remap = false, priority = 1500)
+@Mixin(value = InterfaceLogic.class, remap = false, priority = 1100)
public class InterfaceLogicUpgradesMixin {
@Final
@@ -32,22 +32,34 @@ public class InterfaceLogicUpgradesMixin {
/**
* 在InterfaceLogic构造函数末尾注入,增加升级槽数量
- * 使用高优先级(1500)确保在Applied Flux之后执行
+ * 使用优先级1100确保在Applied Flux之后执行,但不会过度干扰其他组件
*/
@Inject(
method = "(Lappeng/api/networking/IManagedGridNode;Lappeng/helpers/InterfaceLogicHost;Lnet/minecraft/world/item/Item;I)V",
- at = @At("TAIL")
+ at = @At("TAIL"),
+ require = 0 // 设置为可选注入,避免在某些情况下导致崩溃
)
private void expandInterfaceUpgrades(IManagedGridNode gridNode, InterfaceLogicHost host, Item is, int slots, CallbackInfo ci) {
try {
+ // 添加更多的安全检查
+ if (this.upgrades == null) {
+ System.err.println("[ExtendedAE_Plus] InterfaceLogic upgrades为null,跳过升级槽扩展");
+ return;
+ }
+
+ if (gridNode == null || host == null || is == null) {
+ System.err.println("[ExtendedAE_Plus] InterfaceLogic构造参数为null,跳过升级槽扩展");
+ return;
+ }
+
System.out.println("[ExtendedAE_Plus] InterfaceLogic升级槽扩展开始");
+ int currentSlots = this.upgrades.size();
+ System.out.println("[ExtendedAE_Plus] 当前升级槽数量: " + currentSlots);
+
// 检查Applied Flux是否已经修改了升级槽
if (UpgradeSlotCompat.isAppfluxPresent()) {
System.out.println("[ExtendedAE_Plus] 检测到Applied Flux存在");
- // Applied Flux存在,检查当前升级槽数量
- int currentSlots = this.upgrades.size();
- System.out.println("[ExtendedAE_Plus] 当前升级槽数量: " + currentSlots);
if (currentSlots >= 3) {
// Applied Flux已经增加了升级槽到3个或更多,我们不需要再修改
@@ -57,17 +69,25 @@ public class InterfaceLogicUpgradesMixin {
// Applied Flux增加到2个,我们再增加1个到3个
System.out.println("[ExtendedAE_Plus] Applied Flux增加到2个槽,我们再增加到3个");
this.upgrades = UpgradeInventories.forMachine(is, 3, this::onUpgradesChanged);
- } else {
+ } else if (currentSlots == 1) {
// Applied Flux可能还没有生效,我们直接增加到3个
- System.out.println("[ExtendedAE_Plus] Applied Flux可能未生效,直接增加到3个槽");
+ System.out.println("[ExtendedAE_Plus] Applied Flux存在但未生效,直接增加到3个槽");
this.upgrades = UpgradeInventories.forMachine(is, 3, this::onUpgradesChanged);
+ } else {
+ System.out.println("[ExtendedAE_Plus] Applied Flux存在,当前槽数异常: " + currentSlots + ",跳过修改");
+ return;
}
} else {
System.out.println("[ExtendedAE_Plus] Applied Flux不存在");
- // Applied Flux不存在,我们将升级槽从1个增加到2个
- int currentSlots = this.upgrades.size();
- System.out.println("[ExtendedAE_Plus] 当前升级槽数量: " + currentSlots + ",增加到2个");
- this.upgrades = UpgradeInventories.forMachine(is, 2, this::onUpgradesChanged);
+
+ if (currentSlots == 1) {
+ // Applied Flux不存在,我们将升级槽从1个增加到2个
+ System.out.println("[ExtendedAE_Plus] 将升级槽从1个增加到2个");
+ this.upgrades = UpgradeInventories.forMachine(is, 2, this::onUpgradesChanged);
+ } else {
+ System.out.println("[ExtendedAE_Plus] Applied Flux不存在,当前槽数异常: " + currentSlots + ",跳过修改");
+ return;
+ }
}
System.out.println("[ExtendedAE_Plus] InterfaceLogic升级槽扩展完成,最终槽数: " + this.upgrades.size());
From bb6bb610019ae694f2eaba876c5f644c1aabecf5 Mon Sep 17 00:00:00 2001
From: GaLicn <133291877+GaLicn@users.noreply.github.com>
Date: Tue, 23 Sep 2025 19:14:32 +0800
Subject: [PATCH 29/34] =?UTF-8?q?=E5=88=A0=E9=99=A4debug=E4=BF=A1=E6=81=AF?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
gradle.properties | 2 +-
.../AdvPatternProviderLogicAdvancedMixin.java | 1 -
.../AdvPatternProviderLogicDoublingMixin.java | 1 -
.../ae2/InterfaceLogicUpgradesMixin.java | 37 +++----------------
.../PatternProviderLogicAdvancedMixin.java | 1 -
.../PatternProviderLogicDoublingMixin.java | 1 -
6 files changed, 6 insertions(+), 37 deletions(-)
diff --git a/gradle.properties b/gradle.properties
index 32815b9..ba9b599 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-fix
+mod_version = 1.4.3
maven_group = com.extendedae_plus
archives_name = extendedae_plus
diff --git a/src/main/java/com/extendedae_plus/mixin/advancedae/helpers/AdvPatternProviderLogicAdvancedMixin.java b/src/main/java/com/extendedae_plus/mixin/advancedae/helpers/AdvPatternProviderLogicAdvancedMixin.java
index 8df0927..2ae2419 100644
--- a/src/main/java/com/extendedae_plus/mixin/advancedae/helpers/AdvPatternProviderLogicAdvancedMixin.java
+++ b/src/main/java/com/extendedae_plus/mixin/advancedae/helpers/AdvPatternProviderLogicAdvancedMixin.java
@@ -94,7 +94,6 @@ public class AdvPatternProviderLogicAdvancedMixin implements AdvancedBlockingHol
@Inject(method = "exportSettings(Lnet/minecraft/nbt/CompoundTag;)V", at = @At("TAIL"))
private void onExportSettings(CompoundTag output, CallbackInfo ci) {
- System.out.println(this.eap$advancedBlocking);
output.putBoolean(EAP_ADV_BLOCKING_KEY, this.eap$advancedBlocking);
}
diff --git a/src/main/java/com/extendedae_plus/mixin/advancedae/helpers/AdvPatternProviderLogicDoublingMixin.java b/src/main/java/com/extendedae_plus/mixin/advancedae/helpers/AdvPatternProviderLogicDoublingMixin.java
index 85f7041..5436f6f 100644
--- a/src/main/java/com/extendedae_plus/mixin/advancedae/helpers/AdvPatternProviderLogicDoublingMixin.java
+++ b/src/main/java/com/extendedae_plus/mixin/advancedae/helpers/AdvPatternProviderLogicDoublingMixin.java
@@ -76,7 +76,6 @@ public class AdvPatternProviderLogicDoublingMixin implements SmartDoublingHolder
@Inject(method = "exportSettings(Lnet/minecraft/nbt/CompoundTag;)V", at = @At("TAIL"))
private void onExportSettings(CompoundTag output, CallbackInfo ci) {
- System.out.println(this.eap$smartDoubling);
output.putBoolean(EAP_SMART_DOUBLING_KEY, this.eap$smartDoubling);
}
diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/InterfaceLogicUpgradesMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/InterfaceLogicUpgradesMixin.java
index bf3bb9b..cdecdfa 100644
--- a/src/main/java/com/extendedae_plus/mixin/ae2/InterfaceLogicUpgradesMixin.java
+++ b/src/main/java/com/extendedae_plus/mixin/ae2/InterfaceLogicUpgradesMixin.java
@@ -41,60 +41,33 @@ public class InterfaceLogicUpgradesMixin {
)
private void expandInterfaceUpgrades(IManagedGridNode gridNode, InterfaceLogicHost host, Item is, int slots, CallbackInfo ci) {
try {
- // 添加更多的安全检查
- if (this.upgrades == null) {
- System.err.println("[ExtendedAE_Plus] InterfaceLogic upgrades为null,跳过升级槽扩展");
+ // 安全检查
+ if (this.upgrades == null || gridNode == null || host == null || is == null) {
return;
}
- if (gridNode == null || host == null || is == null) {
- System.err.println("[ExtendedAE_Plus] InterfaceLogic构造参数为null,跳过升级槽扩展");
- return;
- }
-
- System.out.println("[ExtendedAE_Plus] InterfaceLogic升级槽扩展开始");
-
int currentSlots = this.upgrades.size();
- System.out.println("[ExtendedAE_Plus] 当前升级槽数量: " + currentSlots);
// 检查Applied Flux是否已经修改了升级槽
if (UpgradeSlotCompat.isAppfluxPresent()) {
- System.out.println("[ExtendedAE_Plus] 检测到Applied Flux存在");
-
if (currentSlots >= 3) {
- // Applied Flux已经增加了升级槽到3个或更多,我们不需要再修改
- System.out.println("[ExtendedAE_Plus] Applied Flux已经增加了足够的升级槽,跳过修改");
+ // Applied Flux已经增加了足够的升级槽,跳过修改
return;
} else if (currentSlots == 2) {
// Applied Flux增加到2个,我们再增加1个到3个
- System.out.println("[ExtendedAE_Plus] Applied Flux增加到2个槽,我们再增加到3个");
this.upgrades = UpgradeInventories.forMachine(is, 3, this::onUpgradesChanged);
} else if (currentSlots == 1) {
- // Applied Flux可能还没有生效,我们直接增加到3个
- System.out.println("[ExtendedAE_Plus] Applied Flux存在但未生效,直接增加到3个槽");
+ // Applied Flux存在但未生效,直接增加到3个
this.upgrades = UpgradeInventories.forMachine(is, 3, this::onUpgradesChanged);
- } else {
- System.out.println("[ExtendedAE_Plus] Applied Flux存在,当前槽数异常: " + currentSlots + ",跳过修改");
- return;
}
} else {
- System.out.println("[ExtendedAE_Plus] Applied Flux不存在");
-
if (currentSlots == 1) {
- // Applied Flux不存在,我们将升级槽从1个增加到2个
- System.out.println("[ExtendedAE_Plus] 将升级槽从1个增加到2个");
+ // Applied Flux不存在,将升级槽从1个增加到2个
this.upgrades = UpgradeInventories.forMachine(is, 2, this::onUpgradesChanged);
- } else {
- System.out.println("[ExtendedAE_Plus] Applied Flux不存在,当前槽数异常: " + currentSlots + ",跳过修改");
- return;
}
}
-
- System.out.println("[ExtendedAE_Plus] InterfaceLogic升级槽扩展完成,最终槽数: " + this.upgrades.size());
} catch (Exception e) {
// 发生异常时不修改升级槽,确保不会崩溃
- System.err.println("[ExtendedAE_Plus] Failed to expand interface upgrades: " + e.getMessage());
- e.printStackTrace();
}
}
}
diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/helpers/PatternProviderLogicAdvancedMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/helpers/PatternProviderLogicAdvancedMixin.java
index 18e5a4e..31b4654 100644
--- a/src/main/java/com/extendedae_plus/mixin/ae2/helpers/PatternProviderLogicAdvancedMixin.java
+++ b/src/main/java/com/extendedae_plus/mixin/ae2/helpers/PatternProviderLogicAdvancedMixin.java
@@ -94,7 +94,6 @@ public class PatternProviderLogicAdvancedMixin implements AdvancedBlockingHolder
@Inject(method = "exportSettings(Lnet/minecraft/nbt/CompoundTag;)V", at = @At("TAIL"))
private void onExportSettings(CompoundTag output, CallbackInfo ci) {
- System.out.println(this.eap$advancedBlocking);
output.putBoolean(EAP_ADV_BLOCKING_KEY, this.eap$advancedBlocking);
}
diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/helpers/PatternProviderLogicDoublingMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/helpers/PatternProviderLogicDoublingMixin.java
index ff3152d..13806f2 100644
--- a/src/main/java/com/extendedae_plus/mixin/ae2/helpers/PatternProviderLogicDoublingMixin.java
+++ b/src/main/java/com/extendedae_plus/mixin/ae2/helpers/PatternProviderLogicDoublingMixin.java
@@ -76,7 +76,6 @@ public class PatternProviderLogicDoublingMixin implements SmartDoublingHolder {
@Inject(method = "exportSettings(Lnet/minecraft/nbt/CompoundTag;)V", at = @At("TAIL"))
private void onExportSettings(CompoundTag output, CallbackInfo ci) {
- System.out.println(this.eap$smartDoubling);
output.putBoolean(EAP_SMART_DOUBLING_KEY, this.eap$smartDoubling);
}
From a3ae6b03350fbb5b32a8b4aa4c0cfbbaba6683cb Mon Sep 17 00:00:00 2001
From: GaLicn <133291877+GaLicn@users.noreply.github.com>
Date: Tue, 23 Sep 2025 19:30:42 +0800
Subject: [PATCH 30/34] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=90=88=E6=88=90?=
=?UTF-8?q?=E8=A1=A8?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
gradle.properties | 2 +-
.../java/com/extendedae_plus/init/ModCreativeTabs.java | 3 +++
.../data/extendedae_plus/recipes/channel_card.json | 8 ++++++++
3 files changed, 12 insertions(+), 1 deletion(-)
create mode 100644 src/main/resources/data/extendedae_plus/recipes/channel_card.json
diff --git a/gradle.properties b/gradle.properties
index ba9b599..d673cb8 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -3,7 +3,7 @@ org.gradle.jvmargs=-Xmx1G
loom.platform = forge
# Mod properties
-mod_version = 1.4.3
+mod_version = 1.4.3-beta
maven_group = com.extendedae_plus
archives_name = extendedae_plus
diff --git a/src/main/java/com/extendedae_plus/init/ModCreativeTabs.java b/src/main/java/com/extendedae_plus/init/ModCreativeTabs.java
index 293ac8e..75a95b7 100644
--- a/src/main/java/com/extendedae_plus/init/ModCreativeTabs.java
+++ b/src/main/java/com/extendedae_plus/init/ModCreativeTabs.java
@@ -37,6 +37,9 @@ public final class ModCreativeTabs {
output.accept(ModItems.createEntitySpeedCardStack(16));
output.accept(ModItems.INFINITY_BIGINTEGER_CELL_ITEM.get());
+
+ // 频道卡
+ output.accept(ModItems.CHANNEL_CARD.get());
})
.build());
}
diff --git a/src/main/resources/data/extendedae_plus/recipes/channel_card.json b/src/main/resources/data/extendedae_plus/recipes/channel_card.json
new file mode 100644
index 0000000..d6b23ce
--- /dev/null
+++ b/src/main/resources/data/extendedae_plus/recipes/channel_card.json
@@ -0,0 +1,8 @@
+{
+ "type": "minecraft:crafting_shapeless",
+ "ingredients": [
+ { "item": "ae2:advanced_card" },
+ { "item": "extendedae_plus:wireless_transceiver" }
+ ],
+ "result": { "item": "extendedae_plus:channel_card", "count": 1 }
+}
From 48a230386f75bc78e8bdca8b5451eba318667177 Mon Sep 17 00:00:00 2001
From: GaLicn <133291877+GaLicn@users.noreply.github.com>
Date: Tue, 23 Sep 2025 19:41:01 +0800
Subject: [PATCH 31/34] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E5=AE=9E=E4=BD=93?=
=?UTF-8?q?=E7=BB=91=E5=AE=9A=E9=97=AE=E9=A2=98?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/main/java/com/extendedae_plus/ExtendedAEPlus.java | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/src/main/java/com/extendedae_plus/ExtendedAEPlus.java b/src/main/java/com/extendedae_plus/ExtendedAEPlus.java
index 00cd4c1..037a65a 100644
--- a/src/main/java/com/extendedae_plus/ExtendedAEPlus.java
+++ b/src/main/java/com/extendedae_plus/ExtendedAEPlus.java
@@ -72,6 +72,14 @@ public class ExtendedAEPlus {
ModNetwork.register();
// 注册自定义 Curios 宿主定位器,便于将菜单宿主信息在服务端与客户端间同步
MenuLocators.register(CuriosItemLocator.class, CuriosItemLocator::writeToPacket, CuriosItemLocator::readFromPacket);
+
+ // 绑定方块实体类型,避免 blockEntityClass 为 null 的问题
+ ModBlocks.ASSEMBLER_MATRIX_UPLOAD_CORE.get().setBlockEntity(
+ com.extendedae_plus.content.matrix.UploadCoreBlockEntity.class,
+ ModBlockEntities.UPLOAD_CORE_BE.get(),
+ null,
+ null
+ );
});
}
From d3a47ea3d3562bb453d989304e5c0bcdea0bb4c9 Mon Sep 17 00:00:00 2001
From: GaLicn <133291877+GaLicn@users.noreply.github.com>
Date: Tue, 23 Sep 2025 20:11:31 +0800
Subject: [PATCH 32/34] 1
---
build.gradle | 2 +-
.../mixin/MixinConditions.java | 23 +++++++
.../mixin/ae2/CraftingCPUClusterMixin.java | 62 ++++++++-----------
3 files changed, 49 insertions(+), 38 deletions(-)
diff --git a/build.gradle b/build.gradle
index 25fe6a4..4b8c05f 100644
--- a/build.gradle
+++ b/build.gradle
@@ -106,7 +106,7 @@ dependencies {
//mae2
-/* modRuntimeOnly "curse.maven:modern-ae2-additions-1028068:6827727"*/
+ modRuntimeOnly "curse.maven:modern-ae2-additions-1028068:6827727"
modCompileOnly "curse.maven:modern-ae2-additions-1028068:6827727"
//aea
diff --git a/src/main/java/com/extendedae_plus/mixin/MixinConditions.java b/src/main/java/com/extendedae_plus/mixin/MixinConditions.java
index 7a47b91..595bf1c 100644
--- a/src/main/java/com/extendedae_plus/mixin/MixinConditions.java
+++ b/src/main/java/com/extendedae_plus/mixin/MixinConditions.java
@@ -64,6 +64,29 @@ public class MixinConditions implements IMixinConfigPlugin {
return true; // 总是加载,在Mixin内部进行运行时检查
}
+ // 对于CraftingCPUClusterMixin,检查MAE2是否存在
+ if (mixinClassName.contains("CraftingCPUClusterMixin")) {
+ try {
+ // 检查ModList是否已初始化
+ if (net.minecraftforge.fml.ModList.get() == null) {
+ System.out.println("[ExtendedAE_Plus] ModList未初始化,默认应用CraftingCPU Mixin: " + mixinClassName);
+ return true; // 未初始化时默认应用
+ }
+
+ boolean mae2Exists = net.minecraftforge.fml.ModList.get().isLoaded("mae2");
+ boolean shouldApply = !mae2Exists;
+
+ System.out.println("[ExtendedAE_Plus] CraftingCPU Mixin检查: " + mixinClassName +
+ ", MAE2存在: " + mae2Exists +
+ ", 应用Mixin: " + shouldApply);
+
+ return shouldApply;
+ } catch (Exception e) {
+ System.out.println("[ExtendedAE_Plus] ModList检查失败,默认跳过CraftingCPU Mixin: " + mixinClassName);
+ return false; // 出错时默认跳过,避免冲突
+ }
+ }
+
// 其他Mixin正常应用
System.out.println("[ExtendedAE_Plus] 加载Mixin: " + mixinClassName);
return true;
diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/CraftingCPUClusterMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/CraftingCPUClusterMixin.java
index 7c76d90..ec1a5c8 100644
--- a/src/main/java/com/extendedae_plus/mixin/ae2/CraftingCPUClusterMixin.java
+++ b/src/main/java/com/extendedae_plus/mixin/ae2/CraftingCPUClusterMixin.java
@@ -1,48 +1,36 @@
package com.extendedae_plus.mixin.ae2;
import appeng.blockentity.crafting.CraftingBlockEntity;
-import appeng.blockentity.crafting.CraftingMonitorBlockEntity;
import appeng.me.cluster.implementations.CraftingCPUCluster;
-import appeng.me.helpers.MachineSource;
import org.spongepowered.asm.mixin.Mixin;
-import org.spongepowered.asm.mixin.Overwrite;
-import org.spongepowered.asm.mixin.Shadow;
-
-import java.util.List;
+import org.spongepowered.asm.mixin.injection.At;
+import org.spongepowered.asm.mixin.injection.Constant;
+import org.spongepowered.asm.mixin.injection.ModifyConstant;
+import org.spongepowered.asm.mixin.injection.Redirect;
@Mixin(value = CraftingCPUCluster.class, remap = false, priority = 2000)
public abstract class CraftingCPUClusterMixin {
-
- @Shadow private List blockEntities;
- @Shadow private List status;
- @Shadow private MachineSource machineSrc;
- @Shadow private long storage;
- @Shadow private int accelerator;
-
- /**
- * 完全重写addBlockEntity方法,移除16线程的硬限制
- * @author ExtendedAE_Plus
- * @reason 移除单方块16线程的硬限制,允许更高的线程数
- */
- @Overwrite
- void addBlockEntity(CraftingBlockEntity te) {
- if (this.machineSrc == null || te.isCoreBlock()) {
- this.machineSrc = new MachineSource(te);
- }
+ // 1) 提升“单方块线程上限”的常量,避免抛出 IAE 的 IllegalArgumentException
+ @ModifyConstant(
+ method = "addBlockEntity(Lappeng/blockentity/crafting/CraftingBlockEntity;)V",
+ constant = @Constant(intValue = 16)
+ )
+ private int extendedae_plus$raisePerUnitLimit(int original) {
+ // 放宽到极大值,完全取消单方块 16 线程的硬限制
+ return Integer.MAX_VALUE;
+ }
- te.setCoreBlock(false);
- te.saveChanges();
- this.blockEntities.add(0, te);
-
- if (te instanceof CraftingMonitorBlockEntity) {
- this.status.add((CraftingMonitorBlockEntity) te);
- }
- if (te.getStorageBytes() > 0) {
- this.storage += te.getStorageBytes();
- }
- if (te.getAcceleratorThreads() > 0) {
- // 移除原来的16线程限制,直接添加线程数
- this.accelerator += te.getAcceleratorThreads();
- }
+ // 2) 保持统计使用原始线程值(若存在多处调用),不再返回固定 16
+ @Redirect(
+ method = "addBlockEntity(Lappeng/blockentity/crafting/CraftingBlockEntity;)V",
+ at = @At(
+ value = "INVOKE",
+ target = "Lappeng/blockentity/crafting/CraftingBlockEntity;getAcceleratorThreads()I",
+ ordinal = 1
+ )
+ )
+ private int extendedae_plus$onGetThreadsForLimitCheck(CraftingBlockEntity te) {
+ // 返回原始线程数,确保总并行单元不被错误下限
+ return te.getAcceleratorThreads();
}
}
From dbf2ad7be24560c153096e93fb308427371a16b2 Mon Sep 17 00:00:00 2001
From: C-H716 <1536152356@qq.com>
Date: Tue, 23 Sep 2025 23:00:58 +0800
Subject: [PATCH 33/34] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E5=AE=9E?=
=?UTF-8?q?=E4=BD=93=E5=8A=A0=E9=80=9F=E5=99=A8=E9=BB=91=E5=90=8D=E5=8D=95?=
=?UTF-8?q?=E5=92=8C=E5=80=8D=E7=8E=87=E5=90=8D=E5=8D=95=E6=97=A0=E6=B3=95?=
=?UTF-8?q?=E7=83=AD=E9=87=8D=E8=BD=BD=E7=9A=84=E9=97=AE=E9=A2=98?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../util/ConfigParsingUtils.java | 66 +++++++++++++++----
1 file changed, 52 insertions(+), 14 deletions(-)
diff --git a/src/main/java/com/extendedae_plus/util/ConfigParsingUtils.java b/src/main/java/com/extendedae_plus/util/ConfigParsingUtils.java
index b9377a5..6323978 100644
--- a/src/main/java/com/extendedae_plus/util/ConfigParsingUtils.java
+++ b/src/main/java/com/extendedae_plus/util/ConfigParsingUtils.java
@@ -1,6 +1,7 @@
package com.extendedae_plus.util;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
@@ -114,34 +115,71 @@ public final class ConfigParsingUtils {
// ------------------ 全局缓存与接口 ------------------
private static volatile List cachedBlacklist = null;
private static volatile List cachedMultiplierEntries = null;
+ private static volatile List cachedBlacklistSourceSnapshot = null;
+ private static volatile List cachedMultiplierSourceSnapshot = null;
private static final Object CACHE_LOCK = new Object();
/**
* 获取已解析并缓存的黑名单(线程安全、懒加载)。
*/
public static List getCachedBlacklist(java.util.List extends String> source) {
- if (cachedBlacklist == null) {
- synchronized (CACHE_LOCK) {
- if (cachedBlacklist == null) {
- cachedBlacklist = compilePatterns(source);
- }
- }
+ List normalized = normalizeSource(source);
+
+ // fast path: identical snapshot reference or equal contents
+ if (cachedBlacklist != null && listEquals(cachedBlacklistSourceSnapshot, normalized)) {
+ return Collections.unmodifiableList(cachedBlacklist);
+ }
+
+ synchronized (CACHE_LOCK) {
+ if (cachedBlacklist == null || !listEquals(cachedBlacklistSourceSnapshot, normalized)) {
+ cachedBlacklist = compilePatterns(normalized);
+ cachedBlacklistSourceSnapshot = normalized.isEmpty() ? Collections.emptyList() : new ArrayList<>(normalized);
+ }
+ return Collections.unmodifiableList(cachedBlacklist);
}
- return java.util.List.copyOf(cachedBlacklist);
}
/**
* 获取已解析并缓存的倍率列表(线程安全、懒加载)。
*/
public static List getCachedMultiplierEntries(java.util.List extends String> source) {
- if (cachedMultiplierEntries == null) {
- synchronized (CACHE_LOCK) {
- if (cachedMultiplierEntries == null) {
- cachedMultiplierEntries = parseMultiplierList(source);
- }
- }
+ List normalized = normalizeSource(source);
+
+ if (cachedMultiplierEntries != null && listEquals(cachedMultiplierSourceSnapshot, normalized)) {
+ return Collections.unmodifiableList(cachedMultiplierEntries);
}
- return java.util.List.copyOf(cachedMultiplierEntries);
+
+ synchronized (CACHE_LOCK) {
+ if (cachedMultiplierEntries == null || !listEquals(cachedMultiplierSourceSnapshot, normalized)) {
+ cachedMultiplierEntries = parseMultiplierList(normalized);
+ cachedMultiplierSourceSnapshot = normalized.isEmpty() ? Collections.emptyList() : new ArrayList<>(normalized);
+ }
+ return Collections.unmodifiableList(cachedMultiplierEntries);
+ }
+ }
+
+ // Normalize the incoming source list: trim entries, drop blanks, keep stable ordering
+ private static List normalizeSource(java.util.List extends String> source) {
+ List out = new ArrayList<>();
+ if (source == null) return out;
+ for (String s : source) {
+ if (s == null) continue;
+ String t = s.trim();
+ if (t.isEmpty()) continue;
+ out.add(t);
+ }
+ return out;
+ }
+
+ // Null-safe equality for two lists of strings. Uses size + element equals.
+ private static boolean listEquals(List a, List b) {
+ if (a == b) return true;
+ if (a == null || b == null) return false;
+ if (a.size() != b.size()) return false;
+ for (int i = 0; i < a.size(); i++) {
+ if (!a.get(i).equals(b.get(i))) return false;
+ }
+ return true;
}
/**
From 8c3af60eb19fd8618f733b474579c46c9a24113d Mon Sep 17 00:00:00 2001
From: C-H716 <1536152356@qq.com>
Date: Tue, 23 Sep 2025 23:23:08 +0800
Subject: [PATCH 34/34] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E5=AE=9E?=
=?UTF-8?q?=E4=BD=93=E5=8A=A0=E9=80=9F=E5=99=A8=E8=83=BD=E9=87=8F=E4=B8=8D?=
=?UTF-8?q?=E8=B6=B3=E6=8F=90=E7=A4=BA?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../ae/menu/EntitySpeedTickerMenu.java | 10 +++++++
.../ae/parts/EntitySpeedTickerPart.java | 30 +++++++++++++++++--
.../ae/screen/EntitySpeedTickerScreen.java | 5 +++-
.../ae2/screens/entity_speed_ticker.json | 3 +-
.../assets/extendedae_plus/lang/en_us.json | 1 +
.../assets/extendedae_plus/lang/zh_cn.json | 1 +
6 files changed, 45 insertions(+), 5 deletions(-)
diff --git a/src/main/java/com/extendedae_plus/ae/menu/EntitySpeedTickerMenu.java b/src/main/java/com/extendedae_plus/ae/menu/EntitySpeedTickerMenu.java
index fcc6e5d..8c09948 100644
--- a/src/main/java/com/extendedae_plus/ae/menu/EntitySpeedTickerMenu.java
+++ b/src/main/java/com/extendedae_plus/ae/menu/EntitySpeedTickerMenu.java
@@ -30,6 +30,8 @@ public class EntitySpeedTickerMenu extends UpgradeableMenu extends Up
int energyCardCount = getMenu().energyCardCount;
double multiplier = getMenu().multiplier;
int effectiveSpeed = getMenu().effectiveSpeed;
-
double finalPower = PowerUtils.computeFinalPowerForProduct(effectiveSpeed, energyCardCount);
double remainingRatio = PowerUtils.getRemainingRatio(energyCardCount);
+ // 如果网络能量不足,优先显示警告信息并在能量值处显示 0
+ if (getMenu().networkEnergyInsufficient) {
+ setTextContent("enable", Component.translatable("screen.extendedae_plus.entity_speed_ticker.warning_network_energy_insufficient"));
+ }
setTextContent("speed", Component.translatable("screen.extendedae_plus.entity_speed_ticker.speed", effectiveSpeed));
setTextContent("energy", Component.translatable("screen.extendedae_plus.entity_speed_ticker.energy", Platform.formatPower(finalPower, false)));
setTextContent("power_ratio", Component.translatable("screen.extendedae_plus.entity_speed_ticker.power_ratio", PowerUtils.formatPercentage(remainingRatio)));
diff --git a/src/main/resources/assets/ae2/screens/entity_speed_ticker.json b/src/main/resources/assets/ae2/screens/entity_speed_ticker.json
index e30da1a..f8156f6 100644
--- a/src/main/resources/assets/ae2/screens/entity_speed_ticker.json
+++ b/src/main/resources/assets/ae2/screens/entity_speed_ticker.json
@@ -50,8 +50,7 @@
"left": 88
},
"align": "CENTER"
- }
- ,
+ },
"multiplier": {
"position": {
"top": 80,
diff --git a/src/main/resources/assets/extendedae_plus/lang/en_us.json b/src/main/resources/assets/extendedae_plus/lang/en_us.json
index 07156e3..9d88bc6 100644
--- a/src/main/resources/assets/extendedae_plus/lang/en_us.json
+++ b/src/main/resources/assets/extendedae_plus/lang/en_us.json
@@ -54,6 +54,7 @@
"screen.extendedae_plus.entity_speed_ticker.power_ratio": "Power ratio: %s",
"screen.extendedae_plus.entity_speed_ticker.speed": "Current speed multiplier: %d",
"screen.extendedae_plus.entity_speed_ticker.multiplier": "Extra consumption multiplier: %s",
+ "screen.extendedae_plus.entity_speed_ticker.warning_network_energy_insufficient": "§c§lInsufficient network energy",
"item.extendedae_plus.entity_speed_ticker.tip.requirement": "Requires Entity Acceleration Card(s) to enable acceleration",
"item.extendedae_plus.entity_speed_ticker.tip.max": "Maximum up to 1024x speed",
diff --git a/src/main/resources/assets/extendedae_plus/lang/zh_cn.json b/src/main/resources/assets/extendedae_plus/lang/zh_cn.json
index a245edb..eb60ec2 100644
--- a/src/main/resources/assets/extendedae_plus/lang/zh_cn.json
+++ b/src/main/resources/assets/extendedae_plus/lang/zh_cn.json
@@ -54,6 +54,7 @@
"screen.extendedae_plus.entity_speed_ticker.power_ratio": "功耗比例: %s",
"screen.extendedae_plus.entity_speed_ticker.speed": "当前加速倍率: %d",
"screen.extendedae_plus.entity_speed_ticker.multiplier": "额外消耗倍率: %s",
+ "screen.extendedae_plus.entity_speed_ticker.warning_network_energy_insufficient": "§c§l网络能量不足",
"item.extendedae_plus.entity_speed_ticker.tip.requirement": "需要放入实体加速卡以启用加速",
"item.extendedae_plus.entity_speed_ticker.tip.max": "最高可达 1024x 加速",