From 1ee535391fd68d4684491e94d451b2fce40f346b Mon Sep 17 00:00:00 2001 From: C-H716 <1536152356@qq.com> Date: Sat, 20 Sep 2025 19:53:52 +0800 Subject: [PATCH 01/43] =?UTF-8?q?fix:=20=E7=A7=BB=E9=99=A4=E6=97=A0?= =?UTF-8?q?=E7=BA=BF=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 --- .../com/extendedae_plus/ExtendedAEPlus.java | 20 +- .../InfinityBigIntegerCellHandler.java | 36 -- .../InfinityBigIntegerCellInventory.java | 435 ------------------ .../ae/items/InfinityBigIntegerCellItem.java | 81 ---- .../extendedae_plus/init/ModCreativeTabs.java | 2 +- .../com/extendedae_plus/init/ModItems.java | 7 +- .../util/storage/InfinityDataStorage.java | 61 --- .../util/storage/InfinityStorageManager.java | 166 ------- 8 files changed, 5 insertions(+), 803 deletions(-) delete mode 100644 src/main/java/com/extendedae_plus/ae/api/storage/InfinityBigIntegerCellHandler.java delete mode 100644 src/main/java/com/extendedae_plus/ae/api/storage/InfinityBigIntegerCellInventory.java delete mode 100644 src/main/java/com/extendedae_plus/ae/items/InfinityBigIntegerCellItem.java delete mode 100644 src/main/java/com/extendedae_plus/util/storage/InfinityDataStorage.java delete mode 100644 src/main/java/com/extendedae_plus/util/storage/InfinityStorageManager.java diff --git a/src/main/java/com/extendedae_plus/ExtendedAEPlus.java b/src/main/java/com/extendedae_plus/ExtendedAEPlus.java index 5a6277c..6670527 100644 --- a/src/main/java/com/extendedae_plus/ExtendedAEPlus.java +++ b/src/main/java/com/extendedae_plus/ExtendedAEPlus.java @@ -1,18 +1,13 @@ package com.extendedae_plus; -import appeng.api.storage.StorageCells; import appeng.block.AEBaseEntityBlock; import appeng.blockentity.crafting.CraftingBlockEntity; -import com.extendedae_plus.ae.api.storage.InfinityBigIntegerCellHandler; -import com.extendedae_plus.ae.api.storage.InfinityBigIntegerCellInventory; import com.extendedae_plus.config.ModConfigs; import com.extendedae_plus.init.*; import com.extendedae_plus.network.ModNetwork; -import com.extendedae_plus.util.storage.InfinityStorageManager; import com.mojang.logging.LogUtils; import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.resources.ResourceLocation; -import net.minecraft.server.level.ServerLevel; import net.minecraft.world.level.block.Blocks; import net.neoforged.bus.api.IEventBus; import net.neoforged.bus.api.SubscribeEvent; @@ -21,7 +16,6 @@ import net.neoforged.fml.common.Mod; import net.neoforged.fml.config.ModConfig; import net.neoforged.fml.event.lifecycle.FMLCommonSetupEvent; import net.neoforged.neoforge.common.NeoForge; -import net.neoforged.neoforge.event.level.LevelEvent; import net.neoforged.neoforge.event.server.ServerStartingEvent; import org.slf4j.Logger; @@ -56,10 +50,6 @@ public class ExtendedAEPlus { // Note that this is necessary if and only if we want *this* class (ExtendedAEPlus) to respond directly to events. // Do not add this line if there are no @SubscribeEvent-annotated functions in this class, like onServerStarting() below. NeoForge.EVENT_BUS.register(this); - NeoForge.EVENT_BUS.addListener(ExtendedAEPlus::onLevelLoad); - - NeoForge.EVENT_BUS.addListener(InfinityBigIntegerCellInventory::onServerTick); - NeoForge.EVENT_BUS.addListener(InfinityBigIntegerCellInventory::onServerStopping); // 注册配置:接入自定义的 ModConfigs modContainer.registerConfig(ModConfig.Type.COMMON, ModConfigs.COMMON_SPEC); @@ -75,7 +65,7 @@ public class ExtendedAEPlus { LOGGER.info("HELLO FROM COMMON SETUP"); // 示例日志,避免引用不存在的模板 Config 字段 LOGGER.info("DIRT BLOCK >> {}", BuiltInRegistries.BLOCK.getKey(Blocks.DIRT)); - StorageCells.addCellHandler(InfinityBigIntegerCellHandler.INSTANCE); + // 绑定 AE2 的 CraftingBlockEntity 到本模组的自定义加速器方块,避免 AEBaseEntityBlock.blockEntityType 为空 event.enqueueWork(() -> { try { @@ -120,13 +110,5 @@ public class ExtendedAEPlus { // Do something when the server starts LOGGER.info("HELLO from server starting"); } - - - // 在世界加载时注册/加载 SavedData - private static void onLevelLoad(LevelEvent.Load event) { - if (event.getLevel() instanceof ServerLevel serverLevel) { - InfinityStorageManager.getForLevel(serverLevel); - } - } } 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 deleted file mode 100644 index bc7c29c..0000000 --- a/src/main/java/com/extendedae_plus/ae/api/storage/InfinityBigIntegerCellHandler.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.extendedae_plus.ae.api.storage; - -import appeng.api.storage.cells.ICellHandler; -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); - } -} \ No newline at end of file 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 deleted file mode 100644 index b7e3e14..0000000 --- a/src/main/java/com/extendedae_plus/ae/api/storage/InfinityBigIntegerCellInventory.java +++ /dev/null @@ -1,435 +0,0 @@ -package com.extendedae_plus.ae.api.storage; - -import appeng.api.config.Actionable; -import appeng.api.networking.security.IActionSource; -import appeng.api.stacks.AEItemKey; -import appeng.api.stacks.AEKey; -import appeng.api.stacks.KeyCounter; -import appeng.api.storage.cells.CellState; -import appeng.api.storage.cells.ISaveProvider; -import appeng.api.storage.cells.StorageCell; -import com.extendedae_plus.ae.items.InfinityBigIntegerCellItem; -import com.extendedae_plus.util.storage.InfinityDataStorage; -import com.extendedae_plus.util.storage.InfinityStorageManager; -import it.unimi.dsi.fastutil.objects.Object2ObjectMap; -import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; -import net.minecraft.core.HolderLookup; -import net.minecraft.core.component.DataComponents; -import net.minecraft.nbt.CompoundTag; -import net.minecraft.nbt.ListTag; -import net.minecraft.network.chat.Component; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.item.component.CustomData; -import net.neoforged.neoforge.event.server.ServerStoppingEvent; -import net.neoforged.neoforge.event.tick.ServerTickEvent; - -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; - -/** - * InfinityBigIntegerCellInventory - *

- * 本类实现 AE2 的 StorageCell,表示单个 Infinity 存储单元的运行时数据与行为。 - * 主要职责: - * - 在内存中维护条目映射 (AEKey -> BigInteger 数量) - * - 提供插入/提取/列举/持久化等操作的实现 - * - 通过 UUID 将 ItemStack 与世界级的 SavedData 关联以实现持久化 - *

- * 重要字段: - * - stack: 关联的 ItemStack,NB T 中保存 UUID 与缓存信息 - * - container: AE2 提供的保存回调 (ISaveProvider),用于合并与触发持久化 - * - storedMap: 延迟初始化的内存映射,减少未使用时内存占用 - * - totalStored: 缓存的总数量 (BigInteger),避免频繁全表扫描 - * - isPersisted: 标记内存状态是否已同步到持久层 - */ -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; - // AE2 提供的保存提供者,用于在容器中批量保存时触发回调 - private final ISaveProvider container; - // 内存中的键-数量映射(使用 BigInteger 支持超长数量,延迟初始化) - private Object2ObjectMap storedMap = null; - // 标记是否已持久化到 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; - } - - // 服务器 tick 回调:合并并执行待持久化项 - public static void onServerTick(ServerTickEvent.Post event) { - // NeoForge 提供 Pre/Post 子类以区分阶段,Post 对应原来的 Phase.END - InfinityBigIntegerCellInventory inv; - // 处理本次 tick 中的全部待持久化项 - while ((inv = PENDING_PERSIST.poll()) != null) { - try { - if (!inv.isPersisted) { - inv.persist(); - } - } catch (Throwable ignored) { - // 忽略单项错误,继续处理其余队列 - } - } - } - - // 在服务器停止时被调用,立即强制持久化队列中的所有实例 - public static void onServerStopping(ServerStoppingEvent event) { - InfinityBigIntegerCellInventory inv; - while ((inv = PENDING_PERSIST.poll()) != null) { - try { - if (!inv.isPersisted) { - inv.persist(); - } - } catch (Throwable ignored) { - // 忽略单项错误,继续尝试持久化其它实例 - } - } - // 额外尝试将全局存储管理器标记为脏以确保 SavedData 被写回(在单人模式下可能直接由系统触发) - try { - var stor = getStorageInstance(); - if (stor != null) stor.setDirty(); - } catch (Throwable ignored) { - } - } - - // 将 BigInteger 格式化为带单位的字符串,保留两位小数 - public static String formatBigInteger(BigInteger number) { - // 使用局部 DF(非线程安全),但 Minecraft 通常在主线程运行 - BigDecimal bd = new BigDecimal(number); - BigDecimal thousand = new BigDecimal(1000); - String[] units = new String[]{"", "K", "M", "G", "T", "P", "E", "Z", "Y"}; - int idx = 0; - while (bd.compareTo(thousand) >= 0 && idx < units.length - 1) { - bd = bd.divide(thousand, 2, RoundingMode.HALF_UP); - idx++; - } - if (idx == 0) { - return bd.setScale(0, RoundingMode.DOWN).toPlainString(); - } - return DF.format(bd.doubleValue()) + units[idx]; - } - - // 获取当前存储单元的数据存储对象 - private InfinityDataStorage getCellStorage() { - if (this.getUUID() == null) { - // 如果没有UUID,返回空存储 - return InfinityDataStorage.EMPTY; - } else { - // 否则获取或创建对应UUID的存储 - return getStorageInstance().getOrCreateCell(getUUID()); - } - } - - // 获取存储单元状态(空/非空) - @Override - public CellState getStatus() { - if (this.getCellStoredMap().isEmpty()) { - return CellState.EMPTY; - } - return CellState.NOT_EMPTY; - } - - // 获取存储单元的待机能耗 - @Override - public double getIdleDrain() { - return 512; - } - - // 获取存储单元的描述(此处返回null,可自定义) - @Override - public Component getDescription() { - return null; - } - - // 判断物品堆栈是否有UUID - public boolean hasUUID() { - CustomData customData = stack.get(DataComponents.CUSTOM_DATA); - if (customData == null) return false; - CompoundTag tag = customData.copyTag(); - return tag != null && tag.contains("uuid"); - } - - // 获取物品堆栈的UUID - public UUID getUUID() { - CustomData customData = stack.get(DataComponents.CUSTOM_DATA); - if (customData == null) return null; - CompoundTag tag = customData.copyTag(); - if (tag != null && tag.contains("uuid")) { - return tag.getUUID("uuid"); - } - return null; - } - - // 获取或初始化存储映射 - private Object2ObjectMap getCellStoredMap() { - if (storedMap == null) { - storedMap = new Object2ObjectOpenHashMap<>(); - this.loadCellStoredMap(); - } - return storedMap; - } - - // 从存储中加载物品映射 - private void loadCellStoredMap() { - boolean corruptedTag = false; // 标记数据是否损坏 - if (!this.hasUUID()) return; - - ListTag keys = this.getCellStorage().keys; - ListTag amounts = this.getCellStorage().amounts; - - int len = Math.min(keys.size(), amounts.size()); - - HolderLookup.Provider registries = getStorageInstance().getRegistries(); - - for (int i = 0; i < len; i++) { - AEKey key = AEKey.fromTagGeneric(registries,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); - } - } - } - - // 获取所有可用的物品堆栈及其数量 - @Override - public void getAvailableStacks(KeyCounter out) { - BigInteger maxLong = BigInteger.valueOf(Long.MAX_VALUE); - Object2ObjectMap map = getCellStoredMap(); - for (Object2ObjectMap.Entry entry : map.object2ObjectEntrySet()) { - AEKey key = entry.getKey(); - BigInteger value = entry.getValue(); - - // 当前 KeyCounter 中已有的值(long) - long existing = out.get(key); - - // 将 existing 与当前 value 做 BigInteger 累加并饱和到 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) - if (existing == Long.MAX_VALUE) { - // 已经饱和,无需再添加 - continue; - } - long delta; - if (toSet == Long.MAX_VALUE) { - delta = Long.MAX_VALUE - existing; - } else { - delta = toSet - existing; - } - if (delta != 0) { - out.add(key, delta); - } - } - } - - // 持久化存储单元数据到全局存储 - @Override - public void persist() { - if (this.isPersisted) - return; - Object2ObjectMap map = this.getCellStoredMap(); - if (map.isEmpty()) { - // 如果存储为空,移除UUID和全局存储中的数据 - if (this.hasUUID()) { - getStorageInstance().removeCell(getUUID()); - // 从 CustomData 中移除缓存字段 - try { - CustomData.update(DataComponents.CUSTOM_DATA, stack, tag -> { - tag.remove("uuid"); - tag.remove("total"); - }); - } catch (Throwable ignored) { - } - } - return; - } - // 构建要保存的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(getStorageInstance().getRegistries())); - 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()) { - getStorageInstance().updateCell(this.getUUID(), new InfinityDataStorage()); - } else { - // amounts 现在为 CompoundTag 列表 - getStorageInstance().modifyCell(this.getUUID(), keys, amountTags); - } - // 将缓存的 totalStored 同步到 ItemStack 的 CustomData(优先使用 long) - try { - CustomData.update(DataComponents.CUSTOM_DATA, stack, tag -> { - if (totalStored.compareTo(BigInteger.valueOf(Long.MAX_VALUE)) <= 0) { - tag.putLong("total", totalStored.longValue()); - } else { - tag.putString("total", totalStored.toString()); - } - // 将当前已存储的不同物品种类数缓存到 CustomData(键名: "types"),用于客户端 tooltip 显示 - int typesCount = this.getCellStoredMap().size(); - tag.putInt("types", typesCount); - }); - } catch (Throwable ignored) { - } - isPersisted = true; - } - - // 插入物品到存储单元 - @Override - public long insert(AEKey what, long amount, Actionable mode, IActionSource source) { - // 数量为0或类型不匹配直接返回 - if (amount == 0) - return 0; - // 不允许存储无限单元自身 - if (what instanceof AEItemKey itemKey && itemKey.getItem() instanceof InfinityBigIntegerCellItem) - return 0; - // 如果没有UUID,生成UUID并初始化存储 - if (!this.hasUUID()) { - UUID id = UUID.randomUUID(); - try { - CustomData.update(DataComponents.CUSTOM_DATA, stack, tag -> tag.putUUID("uuid", id)); - } catch (Throwable ignored) { - } - getStorageInstance().getOrCreateCell(getUUID()); - // 确保 storedMap 初始化并从持久层加载数据 - this.getCellStoredMap(); - } - Object2ObjectMap map = this.getCellStoredMap(); - BigInteger currentAmount = map.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)); - this.saveChanges(); - } - return amount; - } - - // 从存储单元提取物品 - @Override - public long extract(AEKey what, long amount, Actionable mode, IActionSource source) { - Object2ObjectMap map = this.getCellStoredMap(); - BigInteger currentAmount = map.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 (mode == Actionable.MODULATE) { - map.remove(what); - // 更新 cached total - // 如果 currentAmount 大于 Long.MAX_VALUE,totalStored 减去 currentAmount 会保留大整数 - totalStored = totalStored.subtract(currentAmount); - this.saveChanges(); - } - return ret; - } else { - // 提取部分 - if (mode == Actionable.MODULATE) { - map.put(what, currentAmount.subtract(requested)); - // 更新 cached total - totalStored = totalStored.subtract(requested); - this.saveChanges(); - } - return amount; - } - } - 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 deleted file mode 100644 index 7002ba9..0000000 --- a/src/main/java/com/extendedae_plus/ae/items/InfinityBigIntegerCellItem.java +++ /dev/null @@ -1,81 +0,0 @@ -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.core.component.DataComponents; -import net.minecraft.nbt.CompoundTag; -import net.minecraft.nbt.LongTag; -import net.minecraft.nbt.Tag; -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.item.component.CustomData; -import org.jetbrains.annotations.NotNull; - -import java.math.BigInteger; -import java.util.List; - -public class InfinityBigIntegerCellItem extends Item { - - public InfinityBigIntegerCellItem() { - super(new Properties().stacksTo(1).fireResistant()); - } - - @Override - public void appendHoverText(ItemStack stack, - @NotNull TooltipContext context, - List tooltip, - @NotNull TooltipFlag tooltipFlag) { - 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); - CustomData customData = stack.get(DataComponents.CUSTOM_DATA); - if (customData != null) { - CompoundTag tag = customData.copyTag(); - - 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)) - ); - - if (tag.contains("types")) { - try { - int types = tag.getInt("types"); - tooltip.add( - Component.literal("Types: ") - .withStyle(ChatFormatting.GRAY) - .append(Component.literal(String.valueOf(types)).withStyle(ChatFormatting.GREEN)) - ); - } catch (Exception ignored) { - } - } - - if (tag.contains("total")) { - BigInteger total = BigInteger.ZERO; - Tag t = tag.get("total"); - try { - if (t instanceof LongTag) { - total = BigInteger.valueOf(tag.getLong("total")); - } else { - String s = tag.getString("total"); - total = new BigInteger(s); - } - } catch (Exception ignored) { - } - 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/init/ModCreativeTabs.java b/src/main/java/com/extendedae_plus/init/ModCreativeTabs.java index 2846159..f07a83b 100644 --- a/src/main/java/com/extendedae_plus/init/ModCreativeTabs.java +++ b/src/main/java/com/extendedae_plus/init/ModCreativeTabs.java @@ -27,7 +27,7 @@ public final class ModCreativeTabs { output.accept(ModItems.ACCELERATOR_256x.get()); output.accept(ModItems.ACCELERATOR_1024x.get()); - output.accept(ModItems.INFINITY_BIGINTEGER_CELL_ITEM.get()); +// output.accept(ModItems.INFINITY_BIGINTEGER_CELL_ITEM.get()); }) .build()); } diff --git a/src/main/java/com/extendedae_plus/init/ModItems.java b/src/main/java/com/extendedae_plus/init/ModItems.java index 113ec98..71a6220 100644 --- a/src/main/java/com/extendedae_plus/init/ModItems.java +++ b/src/main/java/com/extendedae_plus/init/ModItems.java @@ -1,7 +1,6 @@ package com.extendedae_plus.init; import com.extendedae_plus.ExtendedAEPlus; -import com.extendedae_plus.ae.items.InfinityBigIntegerCellItem; import net.minecraft.world.item.BlockItem; import net.minecraft.world.item.Item; import net.neoforged.neoforge.registries.DeferredItem; @@ -48,8 +47,8 @@ public final class ModItems { () -> new BlockItem(ModBlocks.ACCELERATOR_1024x.get(), new Item.Properties()) ); - public static final DeferredItem INFINITY_BIGINTEGER_CELL_ITEM = ITEMS.register( - "infinity_biginteger_cell", InfinityBigIntegerCellItem::new - ); +// public static final DeferredItem INFINITY_BIGINTEGER_CELL_ITEM = ITEMS.register( +// "infinity_biginteger_cell", InfinityBigIntegerCellItem::new +// ); } diff --git a/src/main/java/com/extendedae_plus/util/storage/InfinityDataStorage.java b/src/main/java/com/extendedae_plus/util/storage/InfinityDataStorage.java deleted file mode 100644 index 4ef9036..0000000 --- a/src/main/java/com/extendedae_plus/util/storage/InfinityDataStorage.java +++ /dev/null @@ -1,61 +0,0 @@ -package com.extendedae_plus.util.storage; - -import net.minecraft.nbt.CompoundTag; -import net.minecraft.nbt.ListTag; -import net.minecraft.nbt.Tag; - -/** - * InfinityDataStorage - * - * 表示单个 UUID 对应的持久化数据容器,直接映射到世界存档中的一项记录。 - * 数据结构说明: - * - keys: 存放序列化后的 AEKey(每项为 CompoundTag),用于标识不同的存储条目 - * - amounts: 与 keys 一一对应的数量列表(每项为 CompoundTag),采用混合表示: - * - 当数量能放入 long 时,CompoundTag 包含键 "l" 存放 long 值 - * - 当数量超出 long 时,CompoundTag 包含键 "s" 存放 BigInteger 的字符串形式 - * - * 该类提供将内存数据与 NBT 之间互转的辅助方法,供 `SavedData` 在世界保存/加载时调用。 - */ -public class InfinityDataStorage { - - /** 空实例(表示没有数据) */ - public static final InfinityDataStorage EMPTY = new InfinityDataStorage(); - - /** 序列化的键列表(NBT ListTag,元素为 CompoundTag) */ - public ListTag keys; - /** - * 与 keys 对应的数量列表(NBT ListTag,元素为 CompoundTag): - * - 若数量能放入 long,则 CompoundTag 包含键 "l"(long) - * - 否则包含键 "s"(String) 存放 BigInteger 的字符串形式 - */ - public ListTag amounts; - - public InfinityDataStorage() { - this(new ListTag(), new ListTag()); - } - - private InfinityDataStorage(ListTag keys, ListTag amounts) { - this.keys = keys; - this.amounts = amounts; - } - - /** - * 将当前数据封装为 CompoundTag 以写入存档 - */ - public CompoundTag serializeNBT() { - CompoundTag nbt = new CompoundTag(); - nbt.put("keys", keys); - nbt.put("amounts", amounts); - return nbt; - } - - /** - * 从存档读取数据并构造实例 - */ - 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); - } -} diff --git a/src/main/java/com/extendedae_plus/util/storage/InfinityStorageManager.java b/src/main/java/com/extendedae_plus/util/storage/InfinityStorageManager.java deleted file mode 100644 index ea48942..0000000 --- a/src/main/java/com/extendedae_plus/util/storage/InfinityStorageManager.java +++ /dev/null @@ -1,166 +0,0 @@ -package com.extendedae_plus.util.storage; - -import net.minecraft.core.HolderLookup; -import net.minecraft.nbt.CompoundTag; -import net.minecraft.nbt.ListTag; -import net.minecraft.server.MinecraftServer; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.world.level.saveddata.SavedData; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.lang.ref.WeakReference; -import java.util.HashMap; -import java.util.Map; -import java.util.UUID; - -/** - * InfinityStorageManager - *

- * 世界级别的持久化容器,集中管理所有 InfinityBigInteger 存储单元的序列化数据。 - * 功能要点: - * - 在世界加载时从存档恢复所有 cell 的数据 - * - 提供按 UUID 获取/创建单个 cell 的数据容器 - * - 在世界保存时将内存数据打包为 NBT 写回存档 - */ -public class InfinityStorageManager extends SavedData { - private static final Factory FACTORY = new Factory<>(InfinityStorageManager::new, InfinityStorageManager::readNbt); - - /** - * SavedData 文件名常量 - */ - public static final String FILE_NAME = "eap_infinity_biginteger_cells"; - /** - * 全局单例实例(在世界加载时由 InfiniteBigIntegerStorageCell.onLevelLoad 填充) - */ - public static InfinityStorageManager INSTANCE = null; - /** - * UUID -> 数据 的内存映射 - */ - private final Map cells; - - @Nullable - private WeakReference registries; - - public InfinityStorageManager() { - this.cells = new HashMap<>(); - setDirty(); - } - - public InfinityStorageManager(Map cells) { - this.cells = cells; - setDirty(); - } - - /** - * 从 NBT 构造:用于在世界加载时从存档恢复数据 - */ - public InfinityStorageManager(CompoundTag nbt) { - this.cells = new HashMap<>(); - 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"))); - } - setDirty(); - } - - /** - * 根据给定的 ServerLevel 获取或创建该世界对应的 SavedData 实例并缓存到 INSTANCE - */ - public static InfinityStorageManager getForLevel(ServerLevel level) { - if (INSTANCE == null && level != null) { - INSTANCE = level.getDataStorage().computeIfAbsent(FACTORY, FILE_NAME); - INSTANCE.registries = new WeakReference<>(level.registryAccess()); - } - return INSTANCE; - } - - public HolderLookup.Provider getRegistries() { - var r = this.registries; - if (r == null) { - throw new IllegalStateException("StorageManager was not initialized properly."); - } - - var registries = r.get(); - if (registries == null) { - throw new IllegalStateException("Using a StorageManager whose server was already closed"); - } - - return registries; - } - - @Override - public @NotNull CompoundTag save(@NotNull CompoundTag nbt, HolderLookup.@NotNull Provider registries) { - // 将内存中的所有 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(); - } - - /** - * 获取或创建某个 UUID 对应的数据容器 - */ - public InfinityDataStorage getOrCreateCell(UUID uuid) { - if (!cells.containsKey(uuid)) { - updateCell(uuid, new InfinityDataStorage()); - } - 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; - } - updateCell(cellID, cellToModify); - } - - /** - * 删除某个 UUID 的持久化记录并标记为脏 - */ - public void removeCell(UUID uuid) { - cells.remove(uuid); - setDirty(); - } - - public static InfinityStorageManager readNbt(CompoundTag nbt, HolderLookup.Provider registries) { - Map cell = new HashMap<>(); - ListTag diskList = nbt.getList("list", CompoundTag.TAG_COMPOUND); - for (int i = 0; i < diskList.size(); i++) { - CompoundTag disk = diskList.getCompound(i); - cell.put(disk.getUUID("uuid"), InfinityDataStorage.loadFromNBT(disk.getCompound("data"))); - } - return new InfinityStorageManager(cell); - } - - public static InfinityStorageManager getInstance(MinecraftServer server) { - ServerLevel world = server.getLevel(ServerLevel.OVERWORLD); - InfinityStorageManager manager = null; - if (world != null) { - manager = world.getDataStorage().computeIfAbsent(FACTORY, FILE_NAME); - } - if (manager != null) { - manager.registries = new WeakReference<>(server.registryAccess()); - } - return manager; - } -} From 51c5d4e3d7b52d5e6fa1a7ae26603896b0961fd7 Mon Sep 17 00:00:00 2001 From: C-H716 <1536152356@qq.com> Date: Sat, 20 Sep 2025 20:20:41 +0800 Subject: [PATCH 02/43] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E9=85=8D?= =?UTF-8?q?=E7=BD=AEAE=E5=90=88=E6=88=90=E6=9A=82=E5=81=9C=E6=A3=80?= =?UTF-8?q?=E6=9F=A5=E9=98=88=E5=80=BC=E4=BB=A5=E9=99=8D=E4=BD=8E=E4=B8=8A?= =?UTF-8?q?=E4=B8=8B=E6=96=87=E5=88=87=E6=8D=A2=E9=A2=91=E7=8E=87=EF=BC=8C?= =?UTF-8?q?=E9=BB=98=E8=AE=A4=E5=80=BC=20100000?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../extendedae_plus/config/ModConfigs.java | 10 ++++++++- .../mixin/ae2/CraftingCalculationMixin.java | 21 +++++++++++++++++++ .../resources/extendedae_plus.mixins.json | 5 +++-- 3 files changed, 33 insertions(+), 3 deletions(-) create mode 100644 src/main/java/com/extendedae_plus/mixin/ae2/CraftingCalculationMixin.java diff --git a/src/main/java/com/extendedae_plus/config/ModConfigs.java b/src/main/java/com/extendedae_plus/config/ModConfigs.java index 7a3ae32..d74f914 100644 --- a/src/main/java/com/extendedae_plus/config/ModConfigs.java +++ b/src/main/java/com/extendedae_plus/config/ModConfigs.java @@ -11,6 +11,7 @@ public final class ModConfigs { public static final ModConfigSpec.BooleanValue PROVIDER_ROUND_ROBIN_ENABLE; public static final ModConfigSpec.BooleanValue PATTERN_TERMINAL_SHOW_SLOTS_DEFAULT; public static final ModConfigSpec.IntValue SMART_SCALING_MAX_MULTIPLIER; + public static final ModConfigSpec.IntValue CRAFTING_PAUSE_THRESHOLD; static { ModConfigSpec.Builder builder = new ModConfigSpec.Builder(); @@ -41,6 +42,12 @@ public final class ModConfigs { ) .define("patternTerminalShowSlotsDefault", true); + CRAFTING_PAUSE_THRESHOLD = builder + .comment( + "值越大将减少AE构建合成计划过程中的 wait/notify 次数,提升吞吐但会降低调度响应性" + ) + .defineInRange("craftingPauseThreshold", 100000, 100, Integer.MAX_VALUE); + // end general builder.pop(); @@ -88,5 +95,6 @@ public final class ModConfigs { COMMON_SPEC = builder.build(); } - private ModConfigs() {} + private ModConfigs() { + } } diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/CraftingCalculationMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/CraftingCalculationMixin.java new file mode 100644 index 0000000..255abd3 --- /dev/null +++ b/src/main/java/com/extendedae_plus/mixin/ae2/CraftingCalculationMixin.java @@ -0,0 +1,21 @@ +package com.extendedae_plus.mixin.ae2; + +import appeng.crafting.CraftingCalculation; +import com.extendedae_plus.config.ModConfigs; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.Constant; +import org.spongepowered.asm.mixin.injection.ModifyConstant; + +/** + * Mixin 用于增加 CraftingCalculation.handlePausing() 中 incTime 的阈值。 + * 调整 CRAFTING_PAUSE_THRESHOLD 可以控制计算让出控制权的频率。 + */ +@Mixin(value = CraftingCalculation.class, remap = false) +public abstract class CraftingCalculationMixin { + // 可调节的阈值:在执行 pause 检查前 handlePausing 被调用的次数。 + // 增大此值可以减少 wait/notify 的频率(提升吞吐量但降低响应速度)。 + @ModifyConstant(method = "handlePausing", constant = @Constant(intValue = 100)) + private int modifyIncTimeThreshold(int original) { + return ModConfigs.CRAFTING_PAUSE_THRESHOLD.get(); + } +} \ No newline at end of file diff --git a/src/main/resources/extendedae_plus.mixins.json b/src/main/resources/extendedae_plus.mixins.json index 3f9f3e1..314e662 100644 --- a/src/main/resources/extendedae_plus.mixins.json +++ b/src/main/resources/extendedae_plus.mixins.json @@ -4,6 +4,7 @@ "compatibilityLevel": "JAVA_21", "mixins": [ "ae2.AEProcessingPatternMixin", + "ae2.CraftingCalculationMixin", "ae2.CraftingCPUClusterMixin", "ae2.EncodedPatternItemMixin", "ae2.QuartzCuttingKnifeItemMixin", @@ -36,18 +37,18 @@ "PickFromWirelessMixin", "accessor.AbstractContainerScreenAccessor", "accessor.ScreenAccessor", - "ae2.menu.CraftConfirmMenuGoBackMixin", "ae2.accessor.AEBaseScreenAccessor", "ae2.accessor.AEBaseScreenInvoker", "ae2.accessor.MEStorageScreenAccessor", "ae2.accessor.PatternAccessTermScreenAccessor", "ae2.accessor.PatternAccessTermScreenSlotsRowAccessor", - "ae2.client.gui.InterfaceScreenMixin", "ae2.client.gui.AEBaseScreenMixin", + "ae2.client.gui.InterfaceScreenMixin", "ae2.client.gui.PatternEncodingTermScreenMixin", "ae2.client.gui.PatternProviderCloseMixin", "ae2.client.gui.PatternProviderScreenMixin", "ae2.client.gui.SlotGridLayoutMixin", + "ae2.menu.CraftConfirmMenuGoBackMixin", "extendedae.accessor.GuiExPatternTerminalAccessor", "extendedae.accessor.GuiExPatternTerminalSlotsRowAccessor", "extendedae.client.HighlightButtonMixin", From 4e2be178717ce10e9a2072349b70ec01b014366d Mon Sep 17 00:00:00 2001 From: C-H716 <1536152356@qq.com> Date: Sat, 20 Sep 2025 20:28:11 +0800 Subject: [PATCH 03/43] =?UTF-8?q?feat:=20=E6=94=BE=E5=AE=BD=E6=89=A9?= =?UTF-8?q?=E5=B1=95=E4=BE=9B=E5=BA=94=E5=99=A8=E5=86=85=E7=9A=84=E6=A0=B7?= =?UTF-8?q?=E6=9D=BF=E6=89=8B=E5=8A=A8=E5=80=8D=E5=A2=9E=E9=99=90=E5=88=B6?= =?UTF-8?q?(=E4=B8=8D=E8=B6=85int),=20=E8=B0=83=E6=95=B4=E6=A0=B7=E6=9D=BF?= =?UTF-8?q?=E6=98=BE=E7=A4=BA=EF=BC=8C=E4=BB=A5=E6=94=AF=E6=8C=81=E5=A4=A7?= =?UTF-8?q?=E6=95=B0=E6=98=BE=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ContainerExPatternProviderMixin.java | 27 +++++++++---- .../util/NumberFormatUtil.java | 39 +++++++++++++------ 2 files changed, 47 insertions(+), 19 deletions(-) diff --git a/src/main/java/com/extendedae_plus/mixin/extendedae/container/ContainerExPatternProviderMixin.java b/src/main/java/com/extendedae_plus/mixin/extendedae/container/ContainerExPatternProviderMixin.java index a91cb09..145bc62 100644 --- a/src/main/java/com/extendedae_plus/mixin/extendedae/container/ContainerExPatternProviderMixin.java +++ b/src/main/java/com/extendedae_plus/mixin/extendedae/container/ContainerExPatternProviderMixin.java @@ -13,7 +13,6 @@ import com.glodblock.github.extendedae.container.ContainerExPatternProvider; import net.minecraft.world.entity.player.Inventory; import net.minecraft.world.inventory.MenuType; import net.minecraft.world.inventory.Slot; -import org.jetbrains.annotations.NotNull; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Pseudo; import org.spongepowered.asm.mixin.Unique; @@ -21,6 +20,7 @@ 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; @Pseudo @@ -107,7 +107,7 @@ public abstract class ContainerExPatternProviderMixin extends PatternProviderMen } @Unique - private boolean eap$checkModify(java.util.List stacks, int scale, boolean div) { + private boolean eap$checkModify(List stacks, int scale, boolean div) { if (stacks == null) return false; if (div) { for (var stack : stacks) { @@ -121,10 +121,12 @@ public abstract class ContainerExPatternProviderMixin extends PatternProviderMen } else { for (var stack : stacks) { if (stack != null) { - long upper = 999999L * stack.what().getAmountPerUnit(); - if (stack.amount() * scale > upper) { + long amt = stack.amount(); + // 先检查乘法是否会导致超出 Long.MAX_VALUE,避免溢出 + if (amt > Integer.MAX_VALUE / scale) { return false; } + // 已移除原有的业务上限检查(999999 * amountPerUnit),仅保留溢出检查 } } return true; @@ -132,12 +134,23 @@ public abstract class ContainerExPatternProviderMixin extends PatternProviderMen } @Unique - private java.util.List eap$modifyStacks(java.util.List src, int scale, boolean div) { - var dst = new java.util.ArrayList(src.size()); + private List eap$modifyStacks(List src, int scale, boolean div) { + var dst = new ArrayList(src.size()); for (var stack : src) { if (stack != null) { long amt = stack.amount(); - long newAmt = div ? (amt / scale) : (amt * scale); + long newAmt; + if (div) { // 如果是除法操作 + newAmt = amt / scale; // 执行除法 + } else { // 如果是乘法操作 + // 防御性检查:确保不会发生溢出,尽管上层应已验证 + if (amt > Integer.MAX_VALUE / scale) { + // 遇到潜在溢出时跳过,保持为 null;调用方应通过 eap$checkModify 避免此分支 + dst.add(null); + continue; + } + newAmt = amt * scale; // 执行乘法 + } dst.add(new GenericStack(stack.what(), newAmt)); } else { dst.add(null); diff --git a/src/main/java/com/extendedae_plus/util/NumberFormatUtil.java b/src/main/java/com/extendedae_plus/util/NumberFormatUtil.java index 760b7be..b9aec75 100644 --- a/src/main/java/com/extendedae_plus/util/NumberFormatUtil.java +++ b/src/main/java/com/extendedae_plus/util/NumberFormatUtil.java @@ -1,5 +1,7 @@ package com.extendedae_plus.util; +import java.text.DecimalFormat; + /** * 数字格式化工具类,提供大数字和小数的格式化功能 */ @@ -15,13 +17,18 @@ public class NumberFormatUtil { public static String formatNumber(long number) { if (number < 1000) { return String.valueOf(number); - } else if (number < 1000000) { - double value = number / 1000.0; - return formatDecimal(value, "k"); - } else { - double value = number / 1000000.0; - return formatDecimal(value, "m"); } + + String[] preFixes = new String[]{"k", "M", "G", "T", "P", "E", "Z", "Y"}; + double value = number; + String level = ""; + + for (int offset = 0; value >= 1000.0 && offset < preFixes.length; ++offset) { + value /= 1000.0; + level = preFixes[offset]; + } + + return formatDecimal(value, level); } /** @@ -31,16 +38,24 @@ public class NumberFormatUtil { */ public static String formatNumberWithDecimal(double value) { if (value < 1000) { + DecimalFormat smallDf = new DecimalFormat("#.##"); + // 小于1000时,若是整数则显示整数,否则显示最多两位小数 if (value == (long) value) { return String.valueOf((long) value); } else { - return String.format("%.1f", value).replaceAll("\\.0$", ""); + return smallDf.format(value); } - } else if (value < 1000000) { - return formatDecimal(value / 1000.0, "k"); - } else { - return formatDecimal(value / 1000000.0, "m"); } + + String[] preFixes = new String[]{"k", "M", "G", "T", "P", "E", "Z", "Y"}; + String level = ""; + for (int offset = 0; value >= 1000.0 && offset < preFixes.length; ++offset) { + value /= 1000.0; + level = preFixes[offset]; + } + + DecimalFormat df = new DecimalFormat("#.##"); + return df.format(value) + level; } /** @@ -54,7 +69,7 @@ public class NumberFormatUtil { if (Math.abs(value - Math.round(value)) < 0.001) { return String.valueOf(Math.round(value)) + suffix; } else { - // 修复重复后缀问题:先格式化数字,再添加后缀 + // 使用一位小数显示,去掉末尾 .0 String formatted = String.format("%.1f", value).replaceAll("\\.0$", ""); return formatted + suffix; } From 20262814ba5c36bd948f676c4078346d8467f527 Mon Sep 17 00:00:00 2001 From: C-H716 <1536152356@qq.com> Date: Sat, 20 Sep 2025 20:40:59 +0800 Subject: [PATCH 04/43] =?UTF-8?q?feat:=20=E8=B0=83=E6=95=B4=E6=97=A0?= =?UTF-8?q?=E7=BA=BF=E6=94=B6=E5=8F=91=E5=99=A8=E7=BB=A7=E6=89=BFAEBaseBlo?= =?UTF-8?q?ckEntity=E4=BB=A5=E6=94=AF=E6=8C=81ae=E6=96=B9=E5=9D=97?= =?UTF-8?q?=E9=87=8D=E5=91=BD=E5=90=8D=E5=8A=9F=E8=83=BD;=20=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0jade=E4=BB=8E=E8=8A=82=E7=82=B9=E6=96=B9=E5=9D=97?= =?UTF-8?q?=E6=98=BE=E7=A4=BA=E4=B8=BB=E8=8A=82=E7=82=B9=E5=90=8D=E7=A7=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../wireless/WirelessTransceiverBlockEntity.java | 12 ++++++------ .../WirelessTransceiverJadePluginComponents.java | 7 ++++++- .../jade/WirelessTransceiverProvider.java | 3 +++ 3 files changed, 15 insertions(+), 7 deletions(-) diff --git a/src/main/java/com/extendedae_plus/content/wireless/WirelessTransceiverBlockEntity.java b/src/main/java/com/extendedae_plus/content/wireless/WirelessTransceiverBlockEntity.java index 9ac4d75..b4dd71d 100644 --- a/src/main/java/com/extendedae_plus/content/wireless/WirelessTransceiverBlockEntity.java +++ b/src/main/java/com/extendedae_plus/content/wireless/WirelessTransceiverBlockEntity.java @@ -2,6 +2,7 @@ package com.extendedae_plus.content.wireless; import appeng.api.networking.*; import appeng.api.util.AECableType; +import appeng.blockentity.AEBaseBlockEntity; import com.extendedae_plus.init.ModBlockEntities; import com.extendedae_plus.init.ModItems; import com.extendedae_plus.wireless.IWirelessEndpoint; @@ -13,7 +14,6 @@ import net.minecraft.core.HolderLookup; import net.minecraft.nbt.CompoundTag; import net.minecraft.server.level.ServerLevel; import net.minecraft.world.level.Level; -import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.state.BlockState; import org.jetbrains.annotations.Nullable; @@ -27,7 +27,7 @@ import java.util.Objects; * - 集成 AE2 节点; * - 集成无线主/从逻辑。 */ -public class WirelessTransceiverBlockEntity extends BlockEntity implements IWirelessEndpoint, IInWorldGridNodeHost { +public class WirelessTransceiverBlockEntity extends AEBaseBlockEntity implements IWirelessEndpoint, IInWorldGridNodeHost { private IManagedGridNode managedNode; @@ -55,7 +55,7 @@ public class WirelessTransceiverBlockEntity extends BlockEntity implements IWire } @Override - public appeng.api.util.AECableType getCableConnectionType(Direction dir) { + public AECableType getCableConnectionType(Direction dir) { // 根据相邻方块的实际连接类型渲染(优先采用相邻主机返回的类型),回退为 GLASS。 if (this.level == null) return AECableType.GLASS; var adjacentPos = this.worldPosition.relative(dir); @@ -188,7 +188,7 @@ public class WirelessTransceiverBlockEntity extends BlockEntity implements IWire /* ===================== NBT ===================== */ @Override - protected void saveAdditional(CompoundTag tag, HolderLookup.Provider registries) { + public void saveAdditional(CompoundTag tag, HolderLookup.Provider registries) { super.saveAdditional(tag, registries); tag.putLong("frequency", frequency); tag.putBoolean("master", masterMode); @@ -199,8 +199,8 @@ public class WirelessTransceiverBlockEntity extends BlockEntity implements IWire } @Override - public void loadAdditional(CompoundTag tag, HolderLookup.Provider registries) { - super.loadAdditional(tag, registries); + public void loadTag(CompoundTag tag, HolderLookup.Provider registries) { + super.loadTag(tag, registries); this.frequency = tag.getLong("frequency"); this.masterMode = tag.getBoolean("master"); this.locked = tag.getBoolean("locked"); diff --git a/src/main/java/com/extendedae_plus/integration/jade/WirelessTransceiverJadePluginComponents.java b/src/main/java/com/extendedae_plus/integration/jade/WirelessTransceiverJadePluginComponents.java index e653a0b..22095a6 100644 --- a/src/main/java/com/extendedae_plus/integration/jade/WirelessTransceiverJadePluginComponents.java +++ b/src/main/java/com/extendedae_plus/integration/jade/WirelessTransceiverJadePluginComponents.java @@ -37,7 +37,12 @@ public enum WirelessTransceiverJadePluginComponents implements IBlockComponentPr if (data.contains("masterMode") && !data.getBoolean("masterMode") && data.contains("masterPos")) { BlockPos pos = BlockPos.of(data.getLong("masterPos")); String dim = data.contains("masterDim") ? data.getString("masterDim") : ""; - tooltip.add(Component.literal("主节点位置: (" + pos.getX() + ", " + pos.getY() + ", " + pos.getZ() + ")")); + String customName = data.contains("customName") ? data.getString("customName") : null; + if (customName != null) { + tooltip.add(Component.literal("主节点: " + customName + "(" + pos.getX() + ", " + pos.getY() + ", " + pos.getZ() + ")")); + } else { + tooltip.add(Component.literal("主节点位置: (" + pos.getX() + ", " + pos.getY() + ", " + pos.getZ() + ")")); + } if (!dim.isEmpty()) { tooltip.add(Component.literal("维度: " + dim)); } diff --git a/src/main/java/com/extendedae_plus/integration/jade/WirelessTransceiverProvider.java b/src/main/java/com/extendedae_plus/integration/jade/WirelessTransceiverProvider.java index cfec04b..acf11d3 100644 --- a/src/main/java/com/extendedae_plus/integration/jade/WirelessTransceiverProvider.java +++ b/src/main/java/com/extendedae_plus/integration/jade/WirelessTransceiverProvider.java @@ -46,6 +46,9 @@ public enum WirelessTransceiverProvider implements IServerDataProvider Date: Sat, 20 Sep 2025 21:25:26 +0800 Subject: [PATCH 05/43] =?UTF-8?q?=E5=9B=9E=E9=80=80=E6=A0=B7=E6=9D=BF?= =?UTF-8?q?=E4=BE=9B=E5=BA=94=E5=99=A8=E6=8C=89=E9=92=AE=E5=80=8D=E5=A2=9E?= =?UTF-8?q?=E7=9A=84=E8=B0=83=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ContainerExPatternProviderMixin.java | 26 +++++-------------- 1 file changed, 6 insertions(+), 20 deletions(-) diff --git a/src/main/java/com/extendedae_plus/mixin/extendedae/container/ContainerExPatternProviderMixin.java b/src/main/java/com/extendedae_plus/mixin/extendedae/container/ContainerExPatternProviderMixin.java index 145bc62..d4fa16c 100644 --- a/src/main/java/com/extendedae_plus/mixin/extendedae/container/ContainerExPatternProviderMixin.java +++ b/src/main/java/com/extendedae_plus/mixin/extendedae/container/ContainerExPatternProviderMixin.java @@ -20,7 +20,6 @@ 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; @Pseudo @@ -107,7 +106,7 @@ public abstract class ContainerExPatternProviderMixin extends PatternProviderMen } @Unique - private boolean eap$checkModify(List stacks, int scale, boolean div) { + private boolean eap$checkModify(java.util.List stacks, int scale, boolean div) { if (stacks == null) return false; if (div) { for (var stack : stacks) { @@ -121,12 +120,10 @@ public abstract class ContainerExPatternProviderMixin extends PatternProviderMen } else { for (var stack : stacks) { if (stack != null) { - long amt = stack.amount(); - // 先检查乘法是否会导致超出 Long.MAX_VALUE,避免溢出 - if (amt > Integer.MAX_VALUE / scale) { + long upper = 999999L * stack.what().getAmountPerUnit(); + if (stack.amount() * scale > upper) { return false; } - // 已移除原有的业务上限检查(999999 * amountPerUnit),仅保留溢出检查 } } return true; @@ -134,23 +131,12 @@ public abstract class ContainerExPatternProviderMixin extends PatternProviderMen } @Unique - private List eap$modifyStacks(List src, int scale, boolean div) { - var dst = new ArrayList(src.size()); + private java.util.List eap$modifyStacks(java.util.List src, int scale, boolean div) { + var dst = new java.util.ArrayList(src.size()); for (var stack : src) { if (stack != null) { long amt = stack.amount(); - long newAmt; - if (div) { // 如果是除法操作 - newAmt = amt / scale; // 执行除法 - } else { // 如果是乘法操作 - // 防御性检查:确保不会发生溢出,尽管上层应已验证 - if (amt > Integer.MAX_VALUE / scale) { - // 遇到潜在溢出时跳过,保持为 null;调用方应通过 eap$checkModify 避免此分支 - dst.add(null); - continue; - } - newAmt = amt * scale; // 执行乘法 - } + long newAmt = div ? (amt / scale) : (amt * scale); dst.add(new GenericStack(stack.what(), newAmt)); } else { dst.add(null); From e45bcf87b04468716dba35259e348e32f80c6b20 Mon Sep 17 00:00:00 2001 From: C-H716 <1536152356@qq.com> Date: Sat, 20 Sep 2025 22:05:01 +0800 Subject: [PATCH 06/43] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E6=99=BA?= =?UTF-8?q?=E8=83=BD=E7=B3=BB=E5=88=97=E5=AF=B9=E9=AB=98=E7=BA=A7ae?= =?UTF-8?q?=E4=BE=9B=E5=BA=94=E5=99=A8=E7=9A=84=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 6 + ...ernProviderLogicContainsRedirectMixin.java | 38 +++++ ...vPatternProviderLogicPatternsAccessor.java | 14 ++ ...dvPatternProviderMenuAdvancedAccessor.java | 12 ++ .../gui/AdvPatternProviderScreenMixin.java | 139 ++++++++++++++++++ .../SmallAdvPatternProviderScreenMixin.java | 139 ++++++++++++++++++ .../AdvPatternProviderLogicAdvancedMixin.java | 94 ++++++++++++ .../AdvPatternProviderLogicDoublingMixin.java | 76 ++++++++++ .../AdvPatternProviderMenuAdvancedMixin.java | 43 ++++++ .../AdvPatternProviderMenuDoublingMixin.java | 41 ++++++ .../PatternProviderLogicAdvancedMixin.java | 9 +- .../PatternProviderLogicDoublingMixin.java | 9 +- .../ToggleAdvancedBlockingC2SPacket.java | 47 ++++-- .../network/ToggleSmartDoublingC2SPacket.java | 35 +++-- .../resources/extendedae_plus.mixins.json | 9 ++ 15 files changed, 675 insertions(+), 36 deletions(-) create mode 100644 src/main/java/com/extendedae_plus/mixin/advancedae/AdvPatternProviderLogicContainsRedirectMixin.java create mode 100644 src/main/java/com/extendedae_plus/mixin/advancedae/accessor/AdvPatternProviderLogicPatternsAccessor.java create mode 100644 src/main/java/com/extendedae_plus/mixin/advancedae/accessor/AdvPatternProviderMenuAdvancedAccessor.java create mode 100644 src/main/java/com/extendedae_plus/mixin/advancedae/client/gui/AdvPatternProviderScreenMixin.java create mode 100644 src/main/java/com/extendedae_plus/mixin/advancedae/client/gui/SmallAdvPatternProviderScreenMixin.java create mode 100644 src/main/java/com/extendedae_plus/mixin/advancedae/helpers/AdvPatternProviderLogicAdvancedMixin.java create mode 100644 src/main/java/com/extendedae_plus/mixin/advancedae/helpers/AdvPatternProviderLogicDoublingMixin.java create mode 100644 src/main/java/com/extendedae_plus/mixin/advancedae/menu/AdvPatternProviderMenuAdvancedMixin.java create mode 100644 src/main/java/com/extendedae_plus/mixin/advancedae/menu/AdvPatternProviderMenuDoublingMixin.java diff --git a/build.gradle b/build.gradle index 9687e17..8bbce35 100644 --- a/build.gradle +++ b/build.gradle @@ -162,6 +162,12 @@ dependencies { // runtimeOnly "mekanism:Mekanism:1.21.1-10.7.0.55" runtimeOnly "curse.maven:ex-pattern-provider-892005:6863556" + //aea + implementation "curse.maven:advancedae-1084104:6921281" + + //geckolib + runtimeOnly "curse.maven:geckolib-388172:7009924" + // setup Xei (EMI/REI/JEI) using project property 'use_Xei' switch (project.findProperty('use_Xei') ?: 'emi') { case 'emi': diff --git a/src/main/java/com/extendedae_plus/mixin/advancedae/AdvPatternProviderLogicContainsRedirectMixin.java b/src/main/java/com/extendedae_plus/mixin/advancedae/AdvPatternProviderLogicContainsRedirectMixin.java new file mode 100644 index 0000000..0773388 --- /dev/null +++ b/src/main/java/com/extendedae_plus/mixin/advancedae/AdvPatternProviderLogicContainsRedirectMixin.java @@ -0,0 +1,38 @@ +package com.extendedae_plus.mixin.advancedae; + +import appeng.api.crafting.IPatternDetails; +import com.extendedae_plus.content.ScaledProcessingPattern; +import net.pedroksl.advanced_ae.common.logic.AdvPatternProviderLogic; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; + +import java.util.List; + +/**适配 + * Redirect PatternProviderLogic.pushPattern 中对 List.contains 的调用, + * 在遇到缩放样板时回退匹配到原始样板实例。 + */ +@Mixin(value = AdvPatternProviderLogic.class, remap = false) +public class AdvPatternProviderLogicContainsRedirectMixin { + + @Redirect(method = "pushPattern", + at = @At( + value = "INVOKE", + target = "Ljava/util/List;contains(Ljava/lang/Object;)Z") + ) + private boolean eap$patternsContains(List list, Object o) { + try { + if (o instanceof ScaledProcessingPattern scaled) { + IPatternDetails base = scaled.getOriginal(); + if (base != null && list.indexOf(base) != -1) { + return true; + } + } + // 使用 indexOf 避免再次触发对 List.contains 的 redirect(防止递归) + return list.indexOf(o) != -1; + } catch (Throwable t) { + return list.indexOf(o) != -1; + } + } +} \ No newline at end of file diff --git a/src/main/java/com/extendedae_plus/mixin/advancedae/accessor/AdvPatternProviderLogicPatternsAccessor.java b/src/main/java/com/extendedae_plus/mixin/advancedae/accessor/AdvPatternProviderLogicPatternsAccessor.java new file mode 100644 index 0000000..a75643c --- /dev/null +++ b/src/main/java/com/extendedae_plus/mixin/advancedae/accessor/AdvPatternProviderLogicPatternsAccessor.java @@ -0,0 +1,14 @@ +package com.extendedae_plus.mixin.advancedae.accessor; + +import appeng.api.crafting.IPatternDetails; +import net.pedroksl.advanced_ae.common.logic.AdvPatternProviderLogic; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +import java.util.List; + +@Mixin(value = AdvPatternProviderLogic.class, remap = false) +public interface AdvPatternProviderLogicPatternsAccessor { + @Accessor("patterns") + List eap$patterns(); +} diff --git a/src/main/java/com/extendedae_plus/mixin/advancedae/accessor/AdvPatternProviderMenuAdvancedAccessor.java b/src/main/java/com/extendedae_plus/mixin/advancedae/accessor/AdvPatternProviderMenuAdvancedAccessor.java new file mode 100644 index 0000000..bd75482 --- /dev/null +++ b/src/main/java/com/extendedae_plus/mixin/advancedae/accessor/AdvPatternProviderMenuAdvancedAccessor.java @@ -0,0 +1,12 @@ +package com.extendedae_plus.mixin.advancedae.accessor; + +import net.pedroksl.advanced_ae.common.logic.AdvPatternProviderLogic; +import net.pedroksl.advanced_ae.gui.advpatternprovider.AdvPatternProviderMenu; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +@Mixin(AdvPatternProviderMenu.class) +public interface AdvPatternProviderMenuAdvancedAccessor { + @Accessor(value = "logic", remap = false) + AdvPatternProviderLogic eap$logic(); +} diff --git a/src/main/java/com/extendedae_plus/mixin/advancedae/client/gui/AdvPatternProviderScreenMixin.java b/src/main/java/com/extendedae_plus/mixin/advancedae/client/gui/AdvPatternProviderScreenMixin.java new file mode 100644 index 0000000..2136f1a --- /dev/null +++ b/src/main/java/com/extendedae_plus/mixin/advancedae/client/gui/AdvPatternProviderScreenMixin.java @@ -0,0 +1,139 @@ +package com.extendedae_plus.mixin.advancedae.client.gui; + +import appeng.api.config.Settings; +import appeng.api.config.YesNo; +import appeng.client.gui.AEBaseScreen; +import appeng.client.gui.style.ScreenStyle; +import appeng.client.gui.widgets.SettingToggleButton; +import com.extendedae_plus.api.PatternProviderMenuAdvancedSync; +import com.extendedae_plus.api.PatternProviderMenuDoublingSync; +import com.extendedae_plus.network.ToggleAdvancedBlockingC2SPacket; +import com.extendedae_plus.network.ToggleSmartDoublingC2SPacket; +import net.minecraft.client.Minecraft; +import net.minecraft.network.chat.Component; +import net.minecraft.world.entity.player.Inventory; +import net.pedroksl.advanced_ae.client.gui.AdvPatternProviderScreen; +import net.pedroksl.advanced_ae.gui.advpatternprovider.AdvPatternProviderMenu; +import org.checkerframework.checker.units.qual.C; +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 static com.extendedae_plus.util.ExtendedAELogger.LOGGER; + +/** + * 为高级ae样板供应器界面添加“高级阻挡模式”按钮。 + * - 位于左侧工具栏 + * - 点击仅发送 C2S 切换请求;状态由 AE2 @GuiSync 回传决定 + */ +@Mixin(AdvPatternProviderScreen.class) +public abstract class AdvPatternProviderScreenMixin extends AEBaseScreen { + + @Unique + private SettingToggleButton eap$AdvancedBlockingToggle; + + @Unique + private boolean eap$AdvancedBlockingEnabled = false; + + @Unique + private SettingToggleButton eap$SmartDoublingToggle; + + @Unique + private boolean eap$SmartDoublingEnabled = false; + + public AdvPatternProviderScreenMixin(C menu, Inventory playerInventory, Component title, ScreenStyle style) { + super((AdvPatternProviderMenu) menu, playerInventory, title, style); + } + + @Inject(method = "", at = @At("RETURN")) + private void eap$initAdvancedBlocking(AdvPatternProviderMenu menu, Inventory playerInventory, Component title, ScreenStyle style, CallbackInfo ci) { + // 使用 @GuiSync 初始化 + try { + if (menu instanceof PatternProviderMenuAdvancedSync sync) { + this.eap$AdvancedBlockingEnabled = sync.eap$getAdvancedBlockingSynced(); + } + } catch (Throwable t) { + LOGGER.error("Error initializing advanced sync", t); + } + + // 使用 SettingToggleButton 的外观(原版图标),但自定义悬停描述为“智能阻挡” + this.eap$AdvancedBlockingToggle = new SettingToggleButton<>( + Settings.BLOCKING_MODE, + this.eap$AdvancedBlockingEnabled ? YesNo.YES : YesNo.NO, + (btn, backwards) -> { + // 不做本地切换,点击仅发送自定义C2S,显示由@GuiSync回传 + var conn = Minecraft.getInstance().getConnection(); + if (conn != null) conn.send(ToggleAdvancedBlockingC2SPacket.INSTANCE); + } + ) { + @Override + public java.util.List getTooltipMessage() { + boolean enabled = eap$AdvancedBlockingEnabled; + var title = Component.literal("智能阻挡"); + var line = enabled + ? Component.literal("已启用:对于同一种配方将不再阻挡(需要开启原版的阻挡模式)") + : Component.literal("已禁用:这么好的功能为什么不打开呢"); + return java.util.List.of(title, line); + } + }; + // 初始化后立刻对齐当前@GuiSync状态,避免首帧显示不一致 + this.eap$AdvancedBlockingToggle.set(this.eap$AdvancedBlockingEnabled ? YesNo.YES : YesNo.NO); + + this.addToLeftToolbar(this.eap$AdvancedBlockingToggle); + + // 智能翻倍按钮:与高级阻挡同款样式,点击仅发送C2S,状态由@GuiSync驱动 + try { + if (menu instanceof PatternProviderMenuDoublingSync sync2) { + this.eap$SmartDoublingEnabled = sync2.eap$getSmartDoublingSynced(); + } + } catch (Throwable t) { + LOGGER.error("Error initializing smart doubling sync", t); + } + + this.eap$SmartDoublingToggle = new SettingToggleButton<>( + Settings.BLOCKING_MODE, + this.eap$SmartDoublingEnabled ? YesNo.YES : YesNo.NO, + (btn, backwards) -> { + var conn = Minecraft.getInstance().getConnection(); + if (conn != null) conn.send(ToggleSmartDoublingC2SPacket.INSTANCE); + } + ) { + @Override + public java.util.List getTooltipMessage() { + boolean enabled = eap$SmartDoublingEnabled; + var title = Component.literal("智能翻倍"); + var line = enabled + ? Component.literal("已启用:根据请求量对处理样板进行智能缩放") + : Component.literal("已禁用:按原始样板数量进行发配"); + return java.util.List.of(title, line); + } + }; + + this.eap$SmartDoublingToggle.set(this.eap$SmartDoublingEnabled ? YesNo.YES : YesNo.NO); + this.addToLeftToolbar(this.eap$SmartDoublingToggle); + } + + // 每帧刷新:仅从菜单(@GuiSync)同步布尔值,保持按钮状态一致 + @Inject(method = "updateBeforeRender", at = @At("HEAD"), remap = false) + private void eap$updateAdvancedBlocking(CallbackInfo ci) { + if (this.eap$AdvancedBlockingToggle != null) { + boolean desired = this.eap$AdvancedBlockingEnabled; + if (this.menu instanceof PatternProviderMenuAdvancedSync sync) { + desired = sync.eap$getAdvancedBlockingSynced(); + } + this.eap$AdvancedBlockingEnabled = desired; + this.eap$AdvancedBlockingToggle.set(desired ? YesNo.YES : YesNo.NO); + } + + if (this.eap$SmartDoublingToggle != null) { + boolean desired2 = this.eap$SmartDoublingEnabled; + if (this.menu instanceof PatternProviderMenuDoublingSync sync2) { + desired2 = sync2.eap$getSmartDoublingSynced(); + } + this.eap$SmartDoublingEnabled = desired2; + this.eap$SmartDoublingToggle.set(desired2 ? YesNo.YES : YesNo.NO); + } + } +} diff --git a/src/main/java/com/extendedae_plus/mixin/advancedae/client/gui/SmallAdvPatternProviderScreenMixin.java b/src/main/java/com/extendedae_plus/mixin/advancedae/client/gui/SmallAdvPatternProviderScreenMixin.java new file mode 100644 index 0000000..d7be718 --- /dev/null +++ b/src/main/java/com/extendedae_plus/mixin/advancedae/client/gui/SmallAdvPatternProviderScreenMixin.java @@ -0,0 +1,139 @@ +package com.extendedae_plus.mixin.advancedae.client.gui; + +import appeng.api.config.Settings; +import appeng.api.config.YesNo; +import appeng.client.gui.AEBaseScreen; +import appeng.client.gui.style.ScreenStyle; +import appeng.client.gui.widgets.SettingToggleButton; +import com.extendedae_plus.api.PatternProviderMenuAdvancedSync; +import com.extendedae_plus.api.PatternProviderMenuDoublingSync; +import com.extendedae_plus.network.ToggleAdvancedBlockingC2SPacket; +import com.extendedae_plus.network.ToggleSmartDoublingC2SPacket; +import net.minecraft.client.Minecraft; +import net.minecraft.network.chat.Component; +import net.minecraft.world.entity.player.Inventory; +import net.pedroksl.advanced_ae.client.gui.SmallAdvPatternProviderScreen; +import net.pedroksl.advanced_ae.gui.advpatternprovider.SmallAdvPatternProviderMenu; +import org.checkerframework.checker.units.qual.C; +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 static com.extendedae_plus.util.ExtendedAELogger.LOGGER; + +/** + * 为高级ae样板供应器界面添加“高级阻挡模式”按钮。 + * - 位于左侧工具栏 + * - 点击仅发送 C2S 切换请求;状态由 AE2 @GuiSync 回传决定 + */ +@Mixin(SmallAdvPatternProviderScreen.class) +public abstract class SmallAdvPatternProviderScreenMixin extends AEBaseScreen { + + @Unique + private SettingToggleButton eap$AdvancedBlockingToggle; + + @Unique + private boolean eap$AdvancedBlockingEnabled = false; + + @Unique + private SettingToggleButton eap$SmartDoublingToggle; + + @Unique + private boolean eap$SmartDoublingEnabled = false; + + public SmallAdvPatternProviderScreenMixin(C menu, Inventory playerInventory, Component title, ScreenStyle style) { + super((SmallAdvPatternProviderMenu) menu, playerInventory, title, style); + } + + @Inject(method = "", at = @At("RETURN")) + private void eap$initAdvancedBlocking(SmallAdvPatternProviderMenu menu, Inventory playerInventory, Component title, ScreenStyle style, CallbackInfo ci) { + // 使用 @GuiSync 初始化 + try { + if (menu instanceof PatternProviderMenuAdvancedSync sync) { + this.eap$AdvancedBlockingEnabled = sync.eap$getAdvancedBlockingSynced(); + } + } catch (Throwable t) { + LOGGER.error("Error initializing advanced sync", t); + } + + // 使用 SettingToggleButton 的外观(原版图标),但自定义悬停描述为“智能阻挡” + this.eap$AdvancedBlockingToggle = new SettingToggleButton<>( + Settings.BLOCKING_MODE, + this.eap$AdvancedBlockingEnabled ? YesNo.YES : YesNo.NO, + (btn, backwards) -> { + // 不做本地切换,点击仅发送自定义C2S,显示由@GuiSync回传 + var conn = Minecraft.getInstance().getConnection(); + if (conn != null) conn.send(ToggleAdvancedBlockingC2SPacket.INSTANCE); + } + ) { + @Override + public java.util.List getTooltipMessage() { + boolean enabled = eap$AdvancedBlockingEnabled; + var title = Component.literal("智能阻挡"); + var line = enabled + ? Component.literal("已启用:对于同一种配方将不再阻挡(需要开启原版的阻挡模式)") + : Component.literal("已禁用:这么好的功能为什么不打开呢"); + return java.util.List.of(title, line); + } + }; + // 初始化后立刻对齐当前@GuiSync状态,避免首帧显示不一致 + this.eap$AdvancedBlockingToggle.set(this.eap$AdvancedBlockingEnabled ? YesNo.YES : YesNo.NO); + + this.addToLeftToolbar(this.eap$AdvancedBlockingToggle); + + // 智能翻倍按钮:与高级阻挡同款样式,点击仅发送C2S,状态由@GuiSync驱动 + try { + if (menu instanceof PatternProviderMenuDoublingSync sync2) { + this.eap$SmartDoublingEnabled = sync2.eap$getSmartDoublingSynced(); + } + } catch (Throwable t) { + LOGGER.error("Error initializing smart doubling sync", t); + } + + this.eap$SmartDoublingToggle = new SettingToggleButton<>( + Settings.BLOCKING_MODE, + this.eap$SmartDoublingEnabled ? YesNo.YES : YesNo.NO, + (btn, backwards) -> { + var conn = Minecraft.getInstance().getConnection(); + if (conn != null) conn.send(ToggleSmartDoublingC2SPacket.INSTANCE); + } + ) { + @Override + public java.util.List getTooltipMessage() { + boolean enabled = eap$SmartDoublingEnabled; + var title = Component.literal("智能翻倍"); + var line = enabled + ? Component.literal("已启用:根据请求量对处理样板进行智能缩放") + : Component.literal("已禁用:按原始样板数量进行发配"); + return java.util.List.of(title, line); + } + }; + + this.eap$SmartDoublingToggle.set(this.eap$SmartDoublingEnabled ? YesNo.YES : YesNo.NO); + this.addToLeftToolbar(this.eap$SmartDoublingToggle); + } + + // 每帧刷新:仅从菜单(@GuiSync)同步布尔值,保持按钮状态一致 + @Inject(method = "updateBeforeRender", at = @At("HEAD"), remap = false) + private void eap$updateAdvancedBlocking(CallbackInfo ci) { + if (this.eap$AdvancedBlockingToggle != null) { + boolean desired = this.eap$AdvancedBlockingEnabled; + if (this.menu instanceof PatternProviderMenuAdvancedSync sync) { + desired = sync.eap$getAdvancedBlockingSynced(); + } + this.eap$AdvancedBlockingEnabled = desired; + this.eap$AdvancedBlockingToggle.set(desired ? YesNo.YES : YesNo.NO); + } + + if (this.eap$SmartDoublingToggle != null) { + boolean desired2 = this.eap$SmartDoublingEnabled; + if (this.menu instanceof PatternProviderMenuDoublingSync sync2) { + desired2 = sync2.eap$getSmartDoublingSynced(); + } + this.eap$SmartDoublingEnabled = desired2; + this.eap$SmartDoublingToggle.set(desired2 ? YesNo.YES : YesNo.NO); + } + } +} 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 new file mode 100644 index 0000000..0cce26d --- /dev/null +++ b/src/main/java/com/extendedae_plus/mixin/advancedae/helpers/AdvPatternProviderLogicAdvancedMixin.java @@ -0,0 +1,94 @@ +package com.extendedae_plus.mixin.advancedae.helpers; + +import appeng.api.crafting.IPatternDetails; +import appeng.api.crafting.IPatternDetails.IInput; +import appeng.api.stacks.AEKey; +import appeng.api.stacks.GenericStack; +import appeng.helpers.patternprovider.PatternProviderTarget; +import com.extendedae_plus.api.AdvancedBlockingHolder; +import net.minecraft.core.HolderLookup; +import net.minecraft.nbt.CompoundTag; +import net.pedroksl.advanced_ae.common.logic.AdvPatternProviderLogic; +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.Redirect; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import java.util.Collections; + +@Mixin(value = AdvPatternProviderLogic.class, remap = false) +public class AdvPatternProviderLogicAdvancedMixin implements AdvancedBlockingHolder { + @Unique + private static final String EAP_ADV_BLOCKING_KEY = "eap_advanced_blocking"; + + @Unique + private boolean eap$advancedBlocking = false; + + @Override + public boolean eap$getAdvancedBlocking() { + return eap$advancedBlocking; + } + + @Override + public void eap$setAdvancedBlocking(boolean value) { + this.eap$advancedBlocking = value; + } + + @Inject(method = "writeToNBT", at = @At("TAIL")) + private void eap$writeAdvancedToNbt(CompoundTag tag, HolderLookup.Provider registries, CallbackInfo ci) { + tag.putBoolean(EAP_ADV_BLOCKING_KEY, this.eap$advancedBlocking); + } + + @Inject(method = "readFromNBT", at = @At("TAIL")) + private void eap$readAdvancedFromNbt(CompoundTag tag, HolderLookup.Provider registries, CallbackInfo ci) { + if (tag.contains(EAP_ADV_BLOCKING_KEY)) { + this.eap$advancedBlocking = tag.getBoolean(EAP_ADV_BLOCKING_KEY); + } + } + + // 在 pushPattern 中,重定向对 adapter.containsPatternInput(...) 的调用 + @Redirect(method = "pushPattern", at = @At(value = "INVOKE", target = "Lappeng/helpers/patternprovider/PatternProviderTarget;containsPatternInput(Ljava/util/Set;)Z")) + private boolean eap$redirectBlockingContains(PatternProviderTarget adapter, + java.util.Set patternInputs, + IPatternDetails patternDetails, + appeng.api.stacks.KeyCounter[] inputHolder) { + // 原版是否打开阻挡 + boolean vanillaBlocking = ((AdvPatternProviderLogic)(Object)this).isBlocking(); + if (!vanillaBlocking) { + return adapter.containsPatternInput(patternInputs); + } + + // 仅当高级阻挡启用时启用“匹配则不阻挡” + if (this.eap$advancedBlocking) { + if (eap$targetFullyMatchesPatternInputs(adapter, patternDetails)) { + // 返回 false 表示“不包含阻挡关键物”,从而不触发 continue,允许发配 + return false; + } + } + // 否则使用原判定 + return adapter.containsPatternInput(patternInputs); + } + + @Unique + private boolean eap$targetFullyMatchesPatternInputs(PatternProviderTarget adapter, IPatternDetails patternDetails) { + for (IInput in : patternDetails.getInputs()) { + boolean slotMatched = false; + for (GenericStack candidate : in.getPossibleInputs()) { + AEKey key = candidate.what().dropSecondary(); + if (adapter.containsPatternInput(Collections.singleton(key))) { + slotMatched = true; + break; + } + } + if (!slotMatched) { + return false; // 任一输入槽未匹配则失败 + } + } + return true; // 每个输入槽都至少匹配了一个候选输入 + } + + @Shadow public void saveChanges() {} +} 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 new file mode 100644 index 0000000..e3f3b3e --- /dev/null +++ b/src/main/java/com/extendedae_plus/mixin/advancedae/helpers/AdvPatternProviderLogicDoublingMixin.java @@ -0,0 +1,76 @@ +package com.extendedae_plus.mixin.advancedae.helpers; + +import appeng.api.crafting.IPatternDetails; +import appeng.crafting.pattern.AEProcessingPattern; +import com.extendedae_plus.api.SmartDoublingAwarePattern; +import com.extendedae_plus.api.SmartDoublingHolder; +import com.extendedae_plus.mixin.advancedae.accessor.AdvPatternProviderLogicPatternsAccessor; +import net.minecraft.core.HolderLookup; +import net.minecraft.nbt.CompoundTag; +import net.pedroksl.advanced_ae.common.logic.AdvPatternProviderLogic; +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 = AdvPatternProviderLogic.class, remap = false) +public class AdvPatternProviderLogicDoublingMixin implements SmartDoublingHolder { + @Unique + private static final String EAP_SMART_DOUBLING_KEY = "eap_smart_doubling"; + + @Unique + private boolean eap$smartDoubling = false; + + @Override + public boolean eap$getSmartDoubling() { + return eap$smartDoubling; + } + + @Override + public void eap$setSmartDoubling(boolean value) { + this.eap$smartDoubling = value; + // 立即将开关状态应用到当前 Provider 的样板上,避免等待下一次 updatePatterns + try { + var list = ((AdvPatternProviderLogicPatternsAccessor) this).eap$patterns(); + for (IPatternDetails details : list) { + if (details instanceof AEProcessingPattern proc && proc instanceof SmartDoublingAwarePattern aware) { + aware.eap$setAllowScaling(value); + } + } + // 触发一次刷新,让网络及时拿到最新状态(也会触发 ICraftingProvider.requestUpdate(mainNode)) + ((AdvPatternProviderLogic) (Object) this).updatePatterns(); + } catch (Throwable ignored) { + } + } + + @Inject(method = "writeToNBT", at = @At("TAIL")) + private void eap$writeSmartDoublingToNbt(CompoundTag tag, HolderLookup.Provider registries, CallbackInfo ci) { + tag.putBoolean(EAP_SMART_DOUBLING_KEY, this.eap$smartDoubling); + } + + @Inject(method = "readFromNBT", at = @At("TAIL")) + private void eap$readSmartDoublingFromNbt(CompoundTag tag, HolderLookup.Provider registries, CallbackInfo ci) { + if (tag.contains(EAP_SMART_DOUBLING_KEY)) { + this.eap$smartDoubling = tag.getBoolean(EAP_SMART_DOUBLING_KEY); + } + } + + @Inject(method = "updatePatterns", at = @At("TAIL")) + private void eap$applySmartDoublingToPatterns(CallbackInfo ci) { + try { + var list = ((AdvPatternProviderLogicPatternsAccessor) this).eap$patterns(); + boolean allow = this.eap$smartDoubling; + for (IPatternDetails details : list) { + if (details instanceof AEProcessingPattern proc && proc instanceof SmartDoublingAwarePattern aware) { + aware.eap$setAllowScaling(allow); + } + } + } catch (Throwable ignored) { + } + } + + @Shadow + public void saveChanges() {} +} diff --git a/src/main/java/com/extendedae_plus/mixin/advancedae/menu/AdvPatternProviderMenuAdvancedMixin.java b/src/main/java/com/extendedae_plus/mixin/advancedae/menu/AdvPatternProviderMenuAdvancedMixin.java new file mode 100644 index 0000000..65e8b9d --- /dev/null +++ b/src/main/java/com/extendedae_plus/mixin/advancedae/menu/AdvPatternProviderMenuAdvancedMixin.java @@ -0,0 +1,43 @@ +package com.extendedae_plus.mixin.advancedae.menu; + +import appeng.menu.AEBaseMenu; +import appeng.menu.guisync.GuiSync; +import com.extendedae_plus.api.AdvancedBlockingHolder; +import com.extendedae_plus.api.PatternProviderMenuAdvancedSync; +import net.pedroksl.advanced_ae.common.logic.AdvPatternProviderLogic; +import net.pedroksl.advanced_ae.gui.advpatternprovider.AdvPatternProviderMenu; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(AdvPatternProviderMenu.class) +public abstract class AdvPatternProviderMenuAdvancedMixin implements PatternProviderMenuAdvancedSync { + @Final + @Shadow(remap = false) + protected AdvPatternProviderLogic logic; + + // 选择一个未占用的 GUI 同步 id(AE2 已用到 7),这里使用 21 以避冲突 + @Unique + @GuiSync(22) + public boolean eap$AdvancedBlocking = false; + + @Inject(method = "broadcastChanges", at = @At("HEAD")) + private void eap$syncAdvancedBlocking(CallbackInfo ci) { + // 避免@Shadow父类方法,改用公共API:AEBaseMenu#isClientSide() + if (!((AEBaseMenu) (Object) this).isClientSide()) { + var l = this.logic; + if (l instanceof AdvancedBlockingHolder holder) { + this.eap$AdvancedBlocking = holder.eap$getAdvancedBlocking(); + } + } + } + + @Override + public boolean eap$getAdvancedBlockingSynced() { + return this.eap$AdvancedBlocking; + } +} diff --git a/src/main/java/com/extendedae_plus/mixin/advancedae/menu/AdvPatternProviderMenuDoublingMixin.java b/src/main/java/com/extendedae_plus/mixin/advancedae/menu/AdvPatternProviderMenuDoublingMixin.java new file mode 100644 index 0000000..b5f0493 --- /dev/null +++ b/src/main/java/com/extendedae_plus/mixin/advancedae/menu/AdvPatternProviderMenuDoublingMixin.java @@ -0,0 +1,41 @@ +package com.extendedae_plus.mixin.advancedae.menu; + +import appeng.menu.AEBaseMenu; +import appeng.menu.guisync.GuiSync; +import com.extendedae_plus.api.PatternProviderMenuDoublingSync; +import com.extendedae_plus.api.SmartDoublingHolder; +import net.pedroksl.advanced_ae.common.logic.AdvPatternProviderLogic; +import net.pedroksl.advanced_ae.gui.advpatternprovider.AdvPatternProviderMenu; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(AdvPatternProviderMenu.class) +public abstract class AdvPatternProviderMenuDoublingMixin implements PatternProviderMenuDoublingSync { + @Final + @Shadow(remap = false) + protected AdvPatternProviderLogic logic; + + @Unique + @GuiSync(23) + public boolean eap$SmartDoubling = false; + + @Inject(method = "broadcastChanges", at = @At("HEAD")) + private void eap$syncSmartDoubling(CallbackInfo ci) { + if (!((AEBaseMenu) (Object) this).isClientSide()) { + var l = this.logic; + if (l instanceof SmartDoublingHolder holder) { + this.eap$SmartDoubling = holder.eap$getSmartDoubling(); + } + } + } + + @Override + public boolean eap$getSmartDoublingSynced() { + return this.eap$SmartDoubling; + } +} 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 3990ae3..cb00349 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 @@ -8,7 +8,6 @@ import appeng.helpers.patternprovider.PatternProviderLogic; import appeng.helpers.patternprovider.PatternProviderTarget; import com.extendedae_plus.api.AdvancedBlockingHolder; import net.minecraft.nbt.CompoundTag; -import net.minecraft.world.entity.player.Player; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.Unique; @@ -22,7 +21,7 @@ import java.util.Collections; @Mixin(value = PatternProviderLogic.class, remap = false) public class PatternProviderLogicAdvancedMixin implements AdvancedBlockingHolder { @Unique - private static final String EPP_ADV_BLOCKING_KEY = "epp_advanced_blocking"; + private static final String EAP_ADV_BLOCKING_KEY = "epp_advanced_blocking"; @Unique private boolean eap$advancedBlocking = false; @@ -39,13 +38,13 @@ public class PatternProviderLogicAdvancedMixin implements AdvancedBlockingHolder @Inject(method = "writeToNBT", at = @At("TAIL")) private void eap$writeAdvancedToNbt(CompoundTag tag, net.minecraft.core.HolderLookup.Provider registries, CallbackInfo ci) { - tag.putBoolean(EPP_ADV_BLOCKING_KEY, this.eap$advancedBlocking); + tag.putBoolean(EAP_ADV_BLOCKING_KEY, this.eap$advancedBlocking); } @Inject(method = "readFromNBT", at = @At("TAIL")) private void eap$readAdvancedFromNbt(CompoundTag tag, net.minecraft.core.HolderLookup.Provider registries, CallbackInfo ci) { - if (tag.contains(EPP_ADV_BLOCKING_KEY)) { - this.eap$advancedBlocking = tag.getBoolean(EPP_ADV_BLOCKING_KEY); + if (tag.contains(EAP_ADV_BLOCKING_KEY)) { + this.eap$advancedBlocking = tag.getBoolean(EAP_ADV_BLOCKING_KEY); } } 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 ad455d6..60b65ec 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 @@ -7,7 +7,6 @@ import com.extendedae_plus.api.SmartDoublingAwarePattern; import com.extendedae_plus.api.SmartDoublingHolder; import com.extendedae_plus.mixin.ae2.accessor.PatternProviderLogicPatternsAccessor; import net.minecraft.nbt.CompoundTag; -import net.minecraft.world.entity.player.Player; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.Unique; @@ -18,7 +17,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; @Mixin(value = PatternProviderLogic.class, remap = false) public class PatternProviderLogicDoublingMixin implements SmartDoublingHolder { @Unique - private static final String EPP_SMART_DOUBLING_KEY = "epp_smart_doubling"; + private static final String EAP_SMART_DOUBLING_KEY = "epp_smart_doubling"; @Unique private boolean eap$smartDoubling = false; @@ -47,13 +46,13 @@ public class PatternProviderLogicDoublingMixin implements SmartDoublingHolder { @Inject(method = "writeToNBT", at = @At("TAIL")) private void eap$writeSmartDoublingToNbt(CompoundTag tag, net.minecraft.core.HolderLookup.Provider registries, CallbackInfo ci) { - tag.putBoolean(EPP_SMART_DOUBLING_KEY, this.eap$smartDoubling); + tag.putBoolean(EAP_SMART_DOUBLING_KEY, this.eap$smartDoubling); } @Inject(method = "readFromNBT", at = @At("TAIL")) private void eap$readSmartDoublingFromNbt(CompoundTag tag, net.minecraft.core.HolderLookup.Provider registries, CallbackInfo ci) { - if (tag.contains(EPP_SMART_DOUBLING_KEY)) { - this.eap$smartDoubling = tag.getBoolean(EPP_SMART_DOUBLING_KEY); + if (tag.contains(EAP_SMART_DOUBLING_KEY)) { + this.eap$smartDoubling = tag.getBoolean(EAP_SMART_DOUBLING_KEY); } } diff --git a/src/main/java/com/extendedae_plus/network/ToggleAdvancedBlockingC2SPacket.java b/src/main/java/com/extendedae_plus/network/ToggleAdvancedBlockingC2SPacket.java index c64655f..1c057e3 100644 --- a/src/main/java/com/extendedae_plus/network/ToggleAdvancedBlockingC2SPacket.java +++ b/src/main/java/com/extendedae_plus/network/ToggleAdvancedBlockingC2SPacket.java @@ -1,17 +1,19 @@ package com.extendedae_plus.network; +import appeng.api.config.Settings; +import appeng.api.config.YesNo; import appeng.menu.implementations.PatternProviderMenu; -import com.extendedae_plus.api.AdvancedBlockingHolder; -import com.extendedae_plus.mixin.ae2.accessor.PatternProviderMenuAdvancedAccessor; import com.extendedae_plus.ExtendedAEPlus; +import com.extendedae_plus.api.AdvancedBlockingHolder; +import com.extendedae_plus.mixin.advancedae.accessor.AdvPatternProviderMenuAdvancedAccessor; +import com.extendedae_plus.mixin.ae2.accessor.PatternProviderMenuAdvancedAccessor; import net.minecraft.network.FriendlyByteBuf; import net.minecraft.network.codec.StreamCodec; import net.minecraft.network.protocol.common.custom.CustomPacketPayload; import net.minecraft.resources.ResourceLocation; -import net.neoforged.neoforge.network.handling.IPayloadContext; import net.minecraft.server.level.ServerPlayer; -import appeng.api.config.Settings; -import appeng.api.config.YesNo; +import net.neoforged.neoforge.network.handling.IPayloadContext; +import net.pedroksl.advanced_ae.gui.advpatternprovider.AdvPatternProviderMenu; /** * C2S:切换高级阻挡模式。 @@ -36,16 +38,31 @@ public class ToggleAdvancedBlockingC2SPacket implements CustomPacketPayload { public static void handle(final ToggleAdvancedBlockingC2SPacket msg, final IPayloadContext ctx) { ctx.enqueueWork(() -> { if (!(ctx.player() instanceof ServerPlayer player)) return; - if (!(player.containerMenu instanceof PatternProviderMenu menu)) return; - - var accessor = (PatternProviderMenuAdvancedAccessor) menu; - var logic = accessor.eap$logic(); - if (logic instanceof AdvancedBlockingHolder holder) { - boolean next = !holder.eap$getAdvancedBlocking(); - holder.eap$setAdvancedBlocking(next); - // 自动开启原版阻挡 - logic.getConfigManager().putSetting(Settings.BLOCKING_MODE, YesNo.YES); - logic.saveChanges(); + var containerMenu = player.containerMenu; + if (containerMenu instanceof PatternProviderMenu menu) { + var accessor = (PatternProviderMenuAdvancedAccessor) menu; + var logic = accessor.eap$logic(); + if (logic instanceof AdvancedBlockingHolder holder) { + boolean current = holder.eap$getAdvancedBlocking(); + boolean next = !current; + holder.eap$setAdvancedBlocking(next); + // 自动开启原版阻挡 + logic.getConfigManager().putSetting(Settings.BLOCKING_MODE, YesNo.YES); + // 保存并触发 AE2 的菜单 @GuiSync 广播到所有观看该菜单的玩家 + logic.saveChanges(); + } + }else if (containerMenu instanceof AdvPatternProviderMenu menu){ + var accessor = (AdvPatternProviderMenuAdvancedAccessor) menu; + var logic = accessor.eap$logic(); + if (logic instanceof AdvancedBlockingHolder holder) { + boolean current = holder.eap$getAdvancedBlocking(); + boolean next = !current; + holder.eap$setAdvancedBlocking(next); + // 自动开启原版阻挡 + logic.getConfigManager().putSetting(Settings.BLOCKING_MODE, YesNo.YES); + // 保存并触发 AE2 的菜单 @GuiSync 广播到所有观看该菜单的玩家 + logic.saveChanges(); + } } }); } diff --git a/src/main/java/com/extendedae_plus/network/ToggleSmartDoublingC2SPacket.java b/src/main/java/com/extendedae_plus/network/ToggleSmartDoublingC2SPacket.java index 2e2bd82..79f6664 100644 --- a/src/main/java/com/extendedae_plus/network/ToggleSmartDoublingC2SPacket.java +++ b/src/main/java/com/extendedae_plus/network/ToggleSmartDoublingC2SPacket.java @@ -1,15 +1,17 @@ package com.extendedae_plus.network; import appeng.menu.implementations.PatternProviderMenu; -import com.extendedae_plus.api.SmartDoublingHolder; -import com.extendedae_plus.mixin.ae2.accessor.PatternProviderMenuAdvancedAccessor; import com.extendedae_plus.ExtendedAEPlus; +import com.extendedae_plus.api.SmartDoublingHolder; +import com.extendedae_plus.mixin.advancedae.accessor.AdvPatternProviderMenuAdvancedAccessor; +import com.extendedae_plus.mixin.ae2.accessor.PatternProviderMenuAdvancedAccessor; import net.minecraft.network.FriendlyByteBuf; import net.minecraft.network.codec.StreamCodec; import net.minecraft.network.protocol.common.custom.CustomPacketPayload; import net.minecraft.resources.ResourceLocation; -import net.neoforged.neoforge.network.handling.IPayloadContext; import net.minecraft.server.level.ServerPlayer; +import net.neoforged.neoforge.network.handling.IPayloadContext; +import net.pedroksl.advanced_ae.gui.advpatternprovider.AdvPatternProviderMenu; /** * C2S:切换智能翻倍启用状态。 @@ -34,14 +36,25 @@ public class ToggleSmartDoublingC2SPacket implements CustomPacketPayload { public static void handle(final ToggleSmartDoublingC2SPacket msg, final IPayloadContext ctx) { ctx.enqueueWork(() -> { if (!(ctx.player() instanceof ServerPlayer player)) return; - if (!(player.containerMenu instanceof PatternProviderMenu menu)) return; - - var accessor = (PatternProviderMenuAdvancedAccessor) menu; - var logic = accessor.eap$logic(); - if (logic instanceof SmartDoublingHolder holder) { - boolean next = !holder.eap$getSmartDoubling(); - holder.eap$setSmartDoubling(next); - logic.saveChanges(); + var containerMenu = player.containerMenu; + if (containerMenu instanceof PatternProviderMenu menu) { + var accessor = (PatternProviderMenuAdvancedAccessor) menu; + var logic = accessor.eap$logic(); + if (logic instanceof SmartDoublingHolder holder) { + boolean current = holder.eap$getSmartDoubling(); + boolean next = !current; + holder.eap$setSmartDoubling(next); + logic.saveChanges(); + } + }else if (containerMenu instanceof AdvPatternProviderMenu menu){ + var accessor = (AdvPatternProviderMenuAdvancedAccessor) menu; + var logic = accessor.eap$logic(); + if (logic instanceof SmartDoublingHolder holder) { + boolean current = holder.eap$getSmartDoubling(); + boolean next = !current; + holder.eap$setSmartDoubling(next); + logic.saveChanges(); + } } }); } diff --git a/src/main/resources/extendedae_plus.mixins.json b/src/main/resources/extendedae_plus.mixins.json index 314e662..f7a85bd 100644 --- a/src/main/resources/extendedae_plus.mixins.json +++ b/src/main/resources/extendedae_plus.mixins.json @@ -3,6 +3,13 @@ "package": "com.extendedae_plus.mixin", "compatibilityLevel": "JAVA_21", "mixins": [ + "advancedae.AdvPatternProviderLogicContainsRedirectMixin", + "advancedae.accessor.AdvPatternProviderLogicPatternsAccessor", + "advancedae.accessor.AdvPatternProviderMenuAdvancedAccessor", + "advancedae.helpers.AdvPatternProviderLogicAdvancedMixin", + "advancedae.helpers.AdvPatternProviderLogicDoublingMixin", + "advancedae.menu.AdvPatternProviderMenuAdvancedMixin", + "advancedae.menu.AdvPatternProviderMenuDoublingMixin", "ae2.AEProcessingPatternMixin", "ae2.CraftingCalculationMixin", "ae2.CraftingCPUClusterMixin", @@ -37,6 +44,8 @@ "PickFromWirelessMixin", "accessor.AbstractContainerScreenAccessor", "accessor.ScreenAccessor", + "advancedae.client.gui.AdvPatternProviderScreenMixin", + "advancedae.client.gui.SmallAdvPatternProviderScreenMixin", "ae2.accessor.AEBaseScreenAccessor", "ae2.accessor.AEBaseScreenInvoker", "ae2.accessor.MEStorageScreenAccessor", From 3968b262173e1cc88ac34bc1705e2518fba80e39 Mon Sep 17 00:00:00 2001 From: C-H716 <1536152356@qq.com> Date: Sat, 20 Sep 2025 23:29:10 +0800 Subject: [PATCH 07/43] =?UTF-8?q?fix:=20=E5=90=88=E6=88=90=E8=AE=A1?= =?UTF-8?q?=E5=88=92=E7=95=8C=E9=9D=A2=E6=B7=BB=E5=8A=A0=E7=BC=BA=E5=B0=91?= =?UTF-8?q?=E7=89=A9=E5=93=81=E9=81=87=E5=88=B0=E6=B5=81=E4=BD=93=EF=BC=8C?= =?UTF-8?q?mek=E5=8C=96=E5=AD=A6=E5=93=81=E4=BC=9A=E5=87=BA=E7=8E=B0?= =?UTF-8?q?=E9=94=99=E8=AF=AF=E4=B9=A6=E7=AD=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../integration/jei/JeiRuntimeProxy.java | 83 +++++++++++++++++++ .../ae2/menu/CraftConfirmMenuGoBackMixin.java | 28 +++++-- 2 files changed, 104 insertions(+), 7 deletions(-) diff --git a/src/main/java/com/extendedae_plus/integration/jei/JeiRuntimeProxy.java b/src/main/java/com/extendedae_plus/integration/jei/JeiRuntimeProxy.java index b5e76f7..d5f2de3 100644 --- a/src/main/java/com/extendedae_plus/integration/jei/JeiRuntimeProxy.java +++ b/src/main/java/com/extendedae_plus/integration/jei/JeiRuntimeProxy.java @@ -2,7 +2,9 @@ package com.extendedae_plus.integration.jei; import com.extendedae_plus.mixin.jei.accessor.BookmarkOverlayAccessor; import mezz.jei.api.constants.VanillaTypes; +import mezz.jei.api.ingredients.IIngredientType; import mezz.jei.api.ingredients.ITypedIngredient; +import mezz.jei.api.neoforge.NeoForgeTypes; import mezz.jei.api.runtime.IBookmarkOverlay; import mezz.jei.api.runtime.IIngredientListOverlay; import mezz.jei.api.runtime.IJeiRuntime; @@ -10,8 +12,12 @@ import mezz.jei.gui.bookmarks.BookmarkList; import mezz.jei.gui.bookmarks.IngredientBookmark; import mezz.jei.gui.overlay.elements.IElement; import net.minecraft.world.item.ItemStack; +import net.neoforged.fml.ModList; +import net.neoforged.neoforge.fluids.FluidStack; import javax.annotation.Nullable; +import java.lang.reflect.Field; +import java.lang.reflect.Method; import java.util.Collections; import java.util.List; import java.util.Optional; @@ -155,6 +161,83 @@ public final class JeiRuntimeProxy { } } + public static void addBookmark(FluidStack fluidStack) { + IJeiRuntime rt = RUNTIME; + if (rt == null) return; + + IBookmarkOverlay overlay = rt.getBookmarkOverlay(); + if (overlay instanceof BookmarkOverlayAccessor accessor) { + BookmarkList list = accessor.eap$getBookmarkList(); + Optional> typedOpt = rt.getIngredientManager() + .createTypedIngredient(NeoForgeTypes.FLUID_STACK, fluidStack); + typedOpt.ifPresent(typed -> { + IngredientBookmark bookmark = IngredientBookmark.create(typed, rt.getIngredientManager()); + list.add(bookmark); // add 内部会自动保存到配置 + }); + } + } + + /** + * 如果存在 Mekanism/appmek,则将 Mekanism 化学堆栈添加到 JEI 书签。 + */ + public static void addBookmark(Object chemicalStack) { + if (!ModList.get().isLoaded("mekanism") && !ModList.get().isLoaded("appmek")) return; + + IJeiRuntime rt = RUNTIME; + if (rt == null) return; + + IBookmarkOverlay overlay = rt.getBookmarkOverlay(); + if (overlay instanceof BookmarkOverlayAccessor accessor) { + BookmarkList list = accessor.eap$getBookmarkList(); + try { + if (chemicalStack == null) return; + + // Determine Mekanism JEI ingredient type constant by runtime class name + String clsName = chemicalStack.getClass().getName(); + String mekanismJeiClass = "mekanism.client.recipe_viewer.jei.MekanismJEI"; + Class jeiCls = Class.forName(mekanismJeiClass); + Field typeField = null; + if ("mekanism.api.chemical.ChemicalStack".equals(clsName)) { + typeField = jeiCls.getField("TYPE_CHEMICAL"); + } + if (typeField == null) return; + Object typeConst = typeField.get(null); + + // Use ingredient manager reflectively to create a typed ingredient + Object ingredientManager = rt.getIngredientManager(); + Method createTypedIngredient = ingredientManager.getClass().getMethod("createTypedIngredient", IIngredientType.class, Object.class); + Object opt = createTypedIngredient.invoke(ingredientManager, typeConst, chemicalStack); + if (!(opt instanceof Optional typedOpt)) return; + if (typedOpt.isPresent()) { + Object typed = typedOpt.get(); + // Find a compatible static create(...) method on IngredientBookmark where + // the second parameter is assignable from the actual ingredientManager instance. + Method createMethod = null; + for (Method m : IngredientBookmark.class.getMethods()) { + if (!m.getName().equals("create")) continue; + Class[] params = m.getParameterTypes(); + if (params.length != 2) continue; + // first param should accept the typed ingredient + boolean firstOk = params[0].isAssignableFrom(typed.getClass()) || params[0].isAssignableFrom(ITypedIngredient.class); + boolean secondOk = params[1].isAssignableFrom(ingredientManager.getClass()); + if (firstOk && secondOk) { + createMethod = m; + break; + } + } + if (createMethod != null) { + Object bookmark = createMethod.invoke(null, typed, ingredientManager); + if (bookmark != null) { + list.add((IngredientBookmark) bookmark); + } + } + } + } catch (Throwable e) { + e.printStackTrace(); + } + } + } + /** * 从 JEI 书签移除物品 */ diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/menu/CraftConfirmMenuGoBackMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/menu/CraftConfirmMenuGoBackMixin.java index 9280311..02a24eb 100644 --- a/src/main/java/com/extendedae_plus/mixin/ae2/menu/CraftConfirmMenuGoBackMixin.java +++ b/src/main/java/com/extendedae_plus/mixin/ae2/menu/CraftConfirmMenuGoBackMixin.java @@ -1,10 +1,13 @@ package com.extendedae_plus.mixin.ae2.menu; +import appeng.api.stacks.AEFluidKey; +import appeng.api.stacks.AEItemKey; import appeng.menu.me.crafting.CraftConfirmMenu; import appeng.menu.me.crafting.CraftingPlanSummary; import appeng.menu.me.crafting.CraftingPlanSummaryEntry; import com.extendedae_plus.integration.jei.JeiRuntimeProxy; import net.minecraft.client.gui.screens.Screen; +import net.neoforged.fml.ModList; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; @@ -42,13 +45,24 @@ public class CraftConfirmMenuGoBackMixin { // 为缺失的条目添加 JEI 书签 for (CraftingPlanSummaryEntry entry : entries) { if (entry.getMissingAmount() > 0) { - try { - var display = entry.getWhat().wrapForDisplayOrFilter(); - if (display != null && !display.isEmpty()) { - // wrapForDisplayOrFilter 返回 ItemStack - JeiRuntimeProxy.addBookmark(display); - } - } catch (Throwable ignored) {} + var what = entry.getWhat(); + if (what instanceof AEItemKey aeItemKey) { + JeiRuntimeProxy.addBookmark(aeItemKey.getReadOnlyStack()); + } else if (what instanceof AEFluidKey aeFluidKey) { + JeiRuntimeProxy.addBookmark(aeFluidKey.toStack(1000)); + } else if (ModList.get().isLoaded("appmek") && ModList.get().isLoaded("mekanism")) { + try { + if (what != null) { + // avoid compile-time dependency on MekanismKey by reflection + Class mekanismKeyCls = Class.forName("me.ramidzkh.mekae2.ae2.MekanismKey"); + if (mekanismKeyCls.isInstance(what)) { + java.lang.reflect.Method m = mekanismKeyCls.getMethod("getStack"); + Object stack = m.invoke(what); + JeiRuntimeProxy.addBookmark(stack); + } + } + } catch (Throwable ignored) {} + } } } } catch (Throwable ignored) {} From 10afb16acbaf1f48d4f20638ef186c2e991726f6 Mon Sep 17 00:00:00 2001 From: C-H716 <1536152356@qq.com> Date: Sun, 21 Sep 2025 01:57:28 +0800 Subject: [PATCH 08/43] =?UTF-8?q?feat:=20=E7=A7=BB=E6=A4=8D1.20=E7=9A=84?= =?UTF-8?q?=E5=AE=9E=E4=BD=93=E5=8A=A0=E9=80=9F=E5=99=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 5 +- .../com/extendedae_plus/ExtendedAEPlus.java | 14 +- .../upgrades/EntitySpeedCardItem.java | 80 +++++ .../ae/items/EntitySpeedTickerPartItem.java | 25 ++ .../ae/menu/EntitySpeedTickerMenu.java | 151 +++++++++ .../ae/parts/EntitySpeedTickerPart.java | 309 ++++++++++++++++++ .../ae/screen/EntitySpeedTickerScreen.java | 136 ++++++++ .../extendedae_plus/client/ClientProxy.java | 33 +- .../client/ui/ProviderSelectScreen.java | 1 - .../extendedae_plus/config/ModConfigs.java | 57 +++- .../extendedae_plus/init/ModCreativeTabs.java | 8 +- .../com/extendedae_plus/init/ModItems.java | 38 ++- .../extendedae_plus/init/ModMenuTypes.java | 15 +- .../{network => init}/ModNetwork.java | 4 +- .../extendedae_plus/init/UpgradeCards.java | 17 + .../integration/jei/ExtendedAEJeiPlugin.java | 31 ++ .../ae2/client/gui/AEBaseScreenMixin.java | 1 - .../network/ToggleEntityTickerC2SPacket.java | 50 +++ .../util/ConfigParsingUtils.java | 172 ++++++++++ .../com/extendedae_plus/util/PowerUtils.java | 162 +++++++++ .../ae2/screens/entity_speed_ticker.json | 78 +++++ .../ae2/textures/guis/blank_background.png | Bin 0 -> 1776 bytes .../assets/extendedae_plus/lang/zh_cn.json | 25 ++ .../models/item/entity_speed_card.json | 14 + .../models/item/entity_speed_card_x16.json | 6 + .../models/item/entity_speed_card_x2.json | 6 + .../models/item/entity_speed_card_x4.json | 6 + .../models/item/entity_speed_card_x8.json | 6 + .../models/item/entity_speed_ticker.json | 7 + .../part/entity_speed_ticker_has_channel.json | 3 + .../models/part/entity_speed_ticker_off.json | 3 + .../models/part/entity_speed_ticker_on.json | 3 + .../models/part/entity_speed_ticker_part.json | 9 + .../textures/item/entity_speed_card_x16.png | Bin 0 -> 2485 bytes .../item/entity_speed_card_x16.png.mcmeta | 15 + .../textures/item/entity_speed_card_x2.png | Bin 0 -> 2271 bytes .../item/entity_speed_card_x2.png.mcmeta | 15 + .../textures/item/entity_speed_card_x4.png | Bin 0 -> 2501 bytes .../item/entity_speed_card_x4.png.mcmeta | 15 + .../textures/item/entity_speed_card_x8.png | Bin 0 -> 2476 bytes .../item/entity_speed_card_x8.png.mcmeta | 15 + .../part/entity_speed_ticker_back.png | Bin 0 -> 4001 bytes .../part/entity_speed_ticker_font.png | Bin 0 -> 3846 bytes .../part/entity_speed_ticker_sides.png | Bin 0 -> 4023 bytes .../recipe/entity_speed_card_x16.json | 37 +++ .../recipe/entity_speed_card_x2.json | 30 ++ .../recipe/entity_speed_card_x4.json | 34 ++ .../recipe/entity_speed_card_x8.json | 34 ++ .../recipe/entity_speed_ticker.json | 33 ++ 49 files changed, 1668 insertions(+), 35 deletions(-) create mode 100644 src/main/java/com/extendedae_plus/ae/definitions/upgrades/EntitySpeedCardItem.java create mode 100644 src/main/java/com/extendedae_plus/ae/items/EntitySpeedTickerPartItem.java create mode 100644 src/main/java/com/extendedae_plus/ae/menu/EntitySpeedTickerMenu.java create mode 100644 src/main/java/com/extendedae_plus/ae/parts/EntitySpeedTickerPart.java create mode 100644 src/main/java/com/extendedae_plus/ae/screen/EntitySpeedTickerScreen.java rename src/main/java/com/extendedae_plus/{network => init}/ModNetwork.java (94%) create mode 100644 src/main/java/com/extendedae_plus/init/UpgradeCards.java create mode 100644 src/main/java/com/extendedae_plus/network/ToggleEntityTickerC2SPacket.java create mode 100644 src/main/java/com/extendedae_plus/util/ConfigParsingUtils.java create mode 100644 src/main/java/com/extendedae_plus/util/PowerUtils.java create mode 100644 src/main/resources/assets/ae2/screens/entity_speed_ticker.json create mode 100644 src/main/resources/assets/ae2/textures/guis/blank_background.png create mode 100644 src/main/resources/assets/extendedae_plus/models/item/entity_speed_card.json create mode 100644 src/main/resources/assets/extendedae_plus/models/item/entity_speed_card_x16.json create mode 100644 src/main/resources/assets/extendedae_plus/models/item/entity_speed_card_x2.json create mode 100644 src/main/resources/assets/extendedae_plus/models/item/entity_speed_card_x4.json create mode 100644 src/main/resources/assets/extendedae_plus/models/item/entity_speed_card_x8.json create mode 100644 src/main/resources/assets/extendedae_plus/models/item/entity_speed_ticker.json create mode 100644 src/main/resources/assets/extendedae_plus/models/part/entity_speed_ticker_has_channel.json create mode 100644 src/main/resources/assets/extendedae_plus/models/part/entity_speed_ticker_off.json create mode 100644 src/main/resources/assets/extendedae_plus/models/part/entity_speed_ticker_on.json create mode 100644 src/main/resources/assets/extendedae_plus/models/part/entity_speed_ticker_part.json create mode 100644 src/main/resources/assets/extendedae_plus/textures/item/entity_speed_card_x16.png create mode 100644 src/main/resources/assets/extendedae_plus/textures/item/entity_speed_card_x16.png.mcmeta create mode 100644 src/main/resources/assets/extendedae_plus/textures/item/entity_speed_card_x2.png create mode 100644 src/main/resources/assets/extendedae_plus/textures/item/entity_speed_card_x2.png.mcmeta create mode 100644 src/main/resources/assets/extendedae_plus/textures/item/entity_speed_card_x4.png create mode 100644 src/main/resources/assets/extendedae_plus/textures/item/entity_speed_card_x4.png.mcmeta create mode 100644 src/main/resources/assets/extendedae_plus/textures/item/entity_speed_card_x8.png create mode 100644 src/main/resources/assets/extendedae_plus/textures/item/entity_speed_card_x8.png.mcmeta create mode 100644 src/main/resources/assets/extendedae_plus/textures/part/entity_speed_ticker_back.png create mode 100644 src/main/resources/assets/extendedae_plus/textures/part/entity_speed_ticker_font.png create mode 100644 src/main/resources/assets/extendedae_plus/textures/part/entity_speed_ticker_sides.png create mode 100644 src/main/resources/data/extendedae_plus/recipe/entity_speed_card_x16.json create mode 100644 src/main/resources/data/extendedae_plus/recipe/entity_speed_card_x2.json create mode 100644 src/main/resources/data/extendedae_plus/recipe/entity_speed_card_x4.json create mode 100644 src/main/resources/data/extendedae_plus/recipe/entity_speed_card_x8.json create mode 100644 src/main/resources/data/extendedae_plus/recipe/entity_speed_ticker.json diff --git a/build.gradle b/build.gradle index 8bbce35..7394979 100644 --- a/build.gradle +++ b/build.gradle @@ -159,7 +159,10 @@ dependencies { runtimeOnly "de.mari_023:ae2wtlib:19.2.1" runtimeOnly "curse.maven:jade-324717:5427817" runtimeOnly "curse.maven:mega-cells-622112:6005043" - // runtimeOnly "mekanism:Mekanism:1.21.1-10.7.0.55" + + runtimeOnly "curse.maven:mekanism-268560:6895130" + runtimeOnly "curse.maven:applied-mekanistics-574300:5978711" + runtimeOnly "curse.maven:ex-pattern-provider-892005:6863556" //aea diff --git a/src/main/java/com/extendedae_plus/ExtendedAEPlus.java b/src/main/java/com/extendedae_plus/ExtendedAEPlus.java index 6670527..3f6640a 100644 --- a/src/main/java/com/extendedae_plus/ExtendedAEPlus.java +++ b/src/main/java/com/extendedae_plus/ExtendedAEPlus.java @@ -1,10 +1,12 @@ package com.extendedae_plus; +import appeng.api.parts.IPart; +import appeng.api.parts.PartModels; import appeng.block.AEBaseEntityBlock; import appeng.blockentity.crafting.CraftingBlockEntity; +import appeng.items.parts.PartModelsHelper; import com.extendedae_plus.config.ModConfigs; import com.extendedae_plus.init.*; -import com.extendedae_plus.network.ModNetwork; import com.mojang.logging.LogUtils; import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.resources.ResourceLocation; @@ -69,6 +71,16 @@ public class ExtendedAEPlus { // 绑定 AE2 的 CraftingBlockEntity 到本模组的自定义加速器方块,避免 AEBaseEntityBlock.blockEntityType 为空 event.enqueueWork(() -> { try { + // 注册升级卡 + new UpgradeCards(event); + + // 为 PartItem 注册 AE2 部件模型 + PartModels.registerModels( + PartModelsHelper.createModels( + ModItems.ENTITY_TICKER_PART_ITEM.get().getPartClass().asSubclass(IPart.class) + ) + ); + // 注册自定义 AE2 MenuLocator(用于 Curios 槽位打开菜单) try { appeng.menu.locator.MenuLocators.register( diff --git a/src/main/java/com/extendedae_plus/ae/definitions/upgrades/EntitySpeedCardItem.java b/src/main/java/com/extendedae_plus/ae/definitions/upgrades/EntitySpeedCardItem.java new file mode 100644 index 0000000..fe253ae --- /dev/null +++ b/src/main/java/com/extendedae_plus/ae/definitions/upgrades/EntitySpeedCardItem.java @@ -0,0 +1,80 @@ +package com.extendedae_plus.ae.definitions.upgrades; + +import appeng.items.materials.UpgradeCardItem; +import com.extendedae_plus.init.ModItems; +import net.minecraft.core.component.DataComponents; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.network.chat.Component; +import net.minecraft.network.chat.MutableComponent; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.TooltipFlag; +import net.minecraft.world.item.component.CustomData; +import net.neoforged.api.distmarker.Dist; +import net.neoforged.api.distmarker.OnlyIn; +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +/** + * 单一的实体加速卡 Item,通过 ItemStack 的 NBT 存储 exponent(0/1/2/3)来区分等级 + */ +public class EntitySpeedCardItem extends UpgradeCardItem { + public static final String NBT_MULTIPLIER = "EAS:mult"; + + public EntitySpeedCardItem(Properties props) { + super(props); + } + + public static ItemStack withMultiplier(int multiplier) { + ItemStack stack = new ItemStack(ModItems.ENTITY_SPEED_CARD.get()); + CompoundTag tag = new CompoundTag(); + tag.putInt(NBT_MULTIPLIER, multiplier); + stack.set(DataComponents.CUSTOM_DATA, CustomData.of(tag)); + return stack; + } + + public static int readMultiplier(ItemStack stack) { + if (stack == null || stack.isEmpty()) return 1; + CustomData customData = stack.get(DataComponents.CUSTOM_DATA); + if (customData == null || !customData.copyTag().contains(NBT_MULTIPLIER)) return 1; + return customData.copyTag().getInt(NBT_MULTIPLIER); + } + + @Override + public @NotNull Component getName(@NotNull ItemStack stack) { + int mult = readMultiplier(stack); + String key; + switch (mult) { + case 2 -> key = "item." + com.extendedae_plus.ExtendedAEPlus.MODID + ".entity_speed_card.x2"; + case 4 -> key = "item." + com.extendedae_plus.ExtendedAEPlus.MODID + ".entity_speed_card.x4"; + case 8 -> key = "item." + com.extendedae_plus.ExtendedAEPlus.MODID + ".entity_speed_card.x8"; + case 16 -> key = "item." + com.extendedae_plus.ExtendedAEPlus.MODID + ".entity_speed_card.x16"; + default -> key = "item." + com.extendedae_plus.ExtendedAEPlus.MODID + ".entity_speed_card.x1"; + } + return Component.translatable(key); + } + + + public List getTooltipLines(ItemStack stack) { + int mult = readMultiplier(stack); + long cap = 1L; + switch (mult) { + case 16 -> cap = 1024L; + case 8 -> cap = 256L; + case 4 -> cap = 64L; + case 2 -> cap = 8L; + } + MutableComponent line1 = Component.translatable("tooltip." + com.extendedae_plus.ExtendedAEPlus.MODID + ".entity_speed_card.multiplier", "x" + mult); + MutableComponent line2 = Component.translatable("tooltip." + com.extendedae_plus.ExtendedAEPlus.MODID + ".entity_speed_card.max", cap); + return List.of(line1, line2); + } + + @OnlyIn(Dist.CLIENT) + @Override + public void appendHoverText(ItemStack stack, TooltipContext context, List lines, TooltipFlag advancedTooltips) { + super.appendHoverText(stack, context, lines, advancedTooltips); + lines.addAll(this.getTooltipLines(stack)); + } +} + + diff --git a/src/main/java/com/extendedae_plus/ae/items/EntitySpeedTickerPartItem.java b/src/main/java/com/extendedae_plus/ae/items/EntitySpeedTickerPartItem.java new file mode 100644 index 0000000..ccee587 --- /dev/null +++ b/src/main/java/com/extendedae_plus/ae/items/EntitySpeedTickerPartItem.java @@ -0,0 +1,25 @@ +package com.extendedae_plus.ae.items; + +import appeng.items.parts.PartItem; +import com.extendedae_plus.ae.parts.EntitySpeedTickerPart; +import net.minecraft.network.chat.Component; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.TooltipFlag; + +import java.util.List; + + +public class EntitySpeedTickerPartItem extends PartItem { + public EntitySpeedTickerPartItem(Properties properties) { + super(properties, EntitySpeedTickerPart.class, EntitySpeedTickerPart::new); + } + + @Override + public void appendHoverText(ItemStack stack, TooltipContext context, List tooltip, TooltipFlag tooltipFlag) { + super.appendHoverText(stack, context, tooltip, tooltipFlag); + tooltip.add(Component.translatable("item.extendedae_plus.entity_speed_ticker.tip.requirement", "需要放入实体加速卡以启用加速")); + tooltip.add(Component.translatable("item.extendedae_plus.entity_speed_ticker.tip.max", "最高可达 1024x 加速")); + tooltip.add(Component.translatable("item.extendedae_plus.entity_speed_ticker.tip.energy", "加速将消耗 AE 网络能量,网络能量不足时无法加速")); + + } +} \ No newline at end of file diff --git a/src/main/java/com/extendedae_plus/ae/menu/EntitySpeedTickerMenu.java b/src/main/java/com/extendedae_plus/ae/menu/EntitySpeedTickerMenu.java new file mode 100644 index 0000000..e72e8ce --- /dev/null +++ b/src/main/java/com/extendedae_plus/ae/menu/EntitySpeedTickerMenu.java @@ -0,0 +1,151 @@ +package com.extendedae_plus.ae.menu; + +import appeng.core.definitions.AEItems; +import appeng.menu.guisync.GuiSync; +import appeng.menu.implementations.UpgradeableMenu; +import appeng.menu.slot.OptionalFakeSlot; +import com.extendedae_plus.ae.parts.EntitySpeedTickerPart; +import com.extendedae_plus.ae.screen.EntitySpeedTickerScreen; +import com.extendedae_plus.config.ModConfigs; +import com.extendedae_plus.init.ModItems; +import com.extendedae_plus.init.ModMenuTypes; +import com.extendedae_plus.util.ConfigParsingUtils; +import com.extendedae_plus.util.PowerUtils; +import it.unimi.dsi.fastutil.shorts.ShortSet; +import net.minecraft.client.Minecraft; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.world.entity.player.Inventory; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.entity.BlockEntity; + +// 实体加速器菜单,负责与客户端界面同步数据 +public class EntitySpeedTickerMenu extends UpgradeableMenu { + @GuiSync(716) + public boolean accelerateEnabled = true; + // 已安装的实体加速卡数量(用于能耗计算) + @GuiSync(717) + public int entitySpeedCardCount; + // 已安装的能量卡数量 + @GuiSync(718) + public int energyCardCount; + // 当前生效的配置倍率(从配置中读取并同步) + // 当前计算出的生效速度(product of multipliers),同步给客户端用于显示 + @GuiSync(719) + public int effectiveSpeed = 1; + @GuiSync(720) + public double multiplier = 1.0; + @GuiSync(721) + public boolean targetBlacklisted = false; + + // 构造方法,初始化菜单并与部件绑定 + public EntitySpeedTickerMenu(int id, Inventory ip, EntitySpeedTickerPart host) { + super(ModMenuTypes.ENTITY_TICKER_MENU.get(), id, ip, host); + // 让部件持有当前菜单实例,便于通信 + getHost().menu = this; + // 初始同步部件上的开关状态到菜单(服务器端构造时保证一致) + try { + this.accelerateEnabled = getHost().getAccelerateEnabled(); + } catch (Exception ignored) { + } + } + + public boolean getAccelerateEnabled() { + return this.accelerateEnabled; + } + + public void setAccelerateEnabled(boolean enabled) { + this.accelerateEnabled = enabled; + } + + @Override + public void onServerDataSync(ShortSet updatedFields) { + super.onServerDataSync(updatedFields); + // 重新统计实体加速卡和能量卡数量 + this.entitySpeedCardCount = this.getUpgrades().getInstalledUpgrades(ModItems.ENTITY_SPEED_CARD.get()); + this.energyCardCount = this.getUpgrades().getInstalledUpgrades(AEItems.ENERGY_CARD); + + // 计算当前面向方块的倍率(服务器端),并同步给客户端 + double mult = 1.0; + try { + BlockEntity target = getHost().getLevel().getBlockEntity( + getHost().getBlockEntity().getBlockPos().relative(getHost().getSide()) + ); + if (target != null) { + String blockId = BuiltInRegistries.BLOCK.getKey(target.getBlockState().getBlock()).toString(); + for (ConfigParsingUtils.MultiplierEntry me : + ConfigParsingUtils.getCachedMultiplierEntries(ModConfigs.ENTITY_TICKER_MULTIPLIERS.get())) { + if (me.pattern.matcher(blockId).matches()) { + mult = Math.max(mult, me.multiplier); + } + } + } + } catch (Exception ignored) {} + this.multiplier = mult; + + // 检查目标是否在黑名单中,如果是则标记并将生效速度设为 0(服务器端计算) + boolean blacklisted = false; + try { + BlockEntity target = getHost().getLevel().getBlockEntity( + getHost().getBlockEntity().getBlockPos().relative(getHost().getSide()) + ); + if (target != null) { + Block block = target.getBlockState().getBlock(); + String blockId = BuiltInRegistries.BLOCK.getKey(block).toString(); // 直接拿到 "minecraft:stone" + + for (java.util.regex.Pattern p : ConfigParsingUtils.getCachedBlacklist( + ModConfigs.ENTITY_TICKER_BLACK_LIST.get())) { + if (p.matcher(blockId).matches()) { + blacklisted = true; + break; + } + } + } + } catch (Exception ignored) { + } + this.targetBlacklisted = blacklisted; + + // 计算生效速度:如果被黑名单则为 0,否则进行正常计算(使用工具类从菜单直接计算 product with cap,最多 8 张) + if (this.targetBlacklisted) { + this.effectiveSpeed = 0; + } else { + this.effectiveSpeed = (int) PowerUtils.computeProductWithCapFromMenu(this, 8); + } + + // 如果在客户端,刷新界面 + if (isClientSide()) { + if (Minecraft.getInstance().screen instanceof EntitySpeedTickerScreen screen) { + screen.refreshGui(); + } + } + } + + // 当任意槽位发生变化时调用 + @Override + public void onSlotChange(net.minecraft.world.inventory.Slot slot) { + super.onSlotChange(slot); + // 客户端重新统计卡数量并刷新界面 + if (isClientSide()) { + this.entitySpeedCardCount = this.getUpgrades().getInstalledUpgrades(ModItems.ENTITY_SPEED_CARD.get()); + this.energyCardCount = this.getUpgrades().getInstalledUpgrades(AEItems.ENERGY_CARD); + // 立即在客户端计算生效速度以便界面即时反馈(使用与服务端相同的工具方法,最多 8 张卡) + this.effectiveSpeed = (int) PowerUtils.computeProductWithCapFromMenu(this, 8); + if (Minecraft.getInstance().screen instanceof EntitySpeedTickerScreen screen) { + screen.refreshGui(); + } + } + } + + @Override + public void broadcastChanges() { + // 遍历所有槽位,清理未启用但有物品显示的 OptionalFakeSlot + for (Object o : this.slots) { + if (o instanceof OptionalFakeSlot fs) { + if (!fs.isSlotEnabled() && !fs.getDisplayStack().isEmpty()) { + fs.clearStack(); + } + } + } + // 调用标准的同步方法,通知监听者数据已更新 + this.standardDetectAndSendChanges(); + } +} \ No newline at end of file diff --git a/src/main/java/com/extendedae_plus/ae/parts/EntitySpeedTickerPart.java b/src/main/java/com/extendedae_plus/ae/parts/EntitySpeedTickerPart.java new file mode 100644 index 0000000..0f6655f --- /dev/null +++ b/src/main/java/com/extendedae_plus/ae/parts/EntitySpeedTickerPart.java @@ -0,0 +1,309 @@ +package com.extendedae_plus.ae.parts; + +import appeng.api.config.Actionable; +import appeng.api.config.PowerMultiplier; +import appeng.api.networking.GridFlags; +import appeng.api.networking.IGridNode; +import appeng.api.networking.ticking.IGridTickable; +import appeng.api.networking.ticking.TickRateModulation; +import appeng.api.networking.ticking.TickingRequest; +import appeng.api.parts.IPartCollisionHelper; +import appeng.api.parts.IPartItem; +import appeng.api.parts.IPartModel; +import appeng.api.upgrades.IUpgradeableObject; +import appeng.core.definitions.AEItems; +import appeng.items.parts.PartModels; +import appeng.menu.MenuOpener; +import appeng.menu.locator.MenuLocators; +import appeng.parts.PartModel; +import appeng.parts.automation.UpgradeablePart; +import com.extendedae_plus.ExtendedAEPlus; +import com.extendedae_plus.ae.menu.EntitySpeedTickerMenu; +import com.extendedae_plus.config.ModConfigs; +import com.extendedae_plus.init.ModItems; +import com.extendedae_plus.init.ModMenuTypes; +import com.extendedae_plus.util.ConfigParsingUtils; +import com.extendedae_plus.util.PowerUtils; +import net.minecraft.core.BlockPos; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.network.chat.Component; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.MenuProvider; +import net.minecraft.world.entity.player.Inventory; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.inventory.AbstractContainerMenu; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.entity.BlockEntityTicker; +import net.minecraft.world.level.block.entity.BlockEntityType; +import net.minecraft.world.phys.Vec3; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; +import java.util.regex.Pattern; + +/** + * EntitySpeedTickerPart 是一个可升级的 AE2 部件

+ * 该部件可以加速目标方块实体的 tick 速率,消耗 AE 网络能量,并支持加速卡升级

+ * 功能受Crazy AE2 Addons启发 + */ +public class EntitySpeedTickerPart extends UpgradeablePart implements IGridTickable, MenuProvider, IUpgradeableObject { + public static final ResourceLocation MODEL_BASE = ResourceLocation.fromNamespaceAndPath( + ExtendedAEPlus.MODID, "part/entity_speed_ticker_part"); + @PartModels + public static final PartModel MODELS_OFF; + @PartModels + public static final PartModel MODELS_ON; + @PartModels + public static final PartModel MODELS_HAS_CHANNEL; + + static { + MODELS_OFF = new PartModel(MODEL_BASE, ResourceLocation.fromNamespaceAndPath(ExtendedAEPlus.MODID, "part/entity_speed_ticker_off")); + MODELS_ON = new PartModel(MODEL_BASE, ResourceLocation.fromNamespaceAndPath(ExtendedAEPlus.MODID, "part/entity_speed_ticker_on")); + MODELS_HAS_CHANNEL = new PartModel(MODEL_BASE, ResourceLocation.fromNamespaceAndPath(ExtendedAEPlus.MODID, "part/entity_speed_ticker_has_channel")); + } + + // 当前打开的菜单实例(如果有) + public EntitySpeedTickerMenu menu; + // 控制是否启用加速(默认启用) + private boolean accelerateEnabled = true; + + /** + * 构造函数,初始化部件并设置网络节点属性 + * + * @param partItem 部件物品 + */ + public EntitySpeedTickerPart(IPartItem partItem) { + super(partItem); + // 设置网络节点属性:需要通道、空闲功耗为1,并注册为 IGridTickable 服务 + this.getMainNode() + .setFlags(GridFlags.REQUIRE_CHANNEL) + .setIdlePowerUsage(1) + .addService(IGridTickable.class, this); + } + + public boolean getAccelerateEnabled() { + return this.accelerateEnabled; + } + + public void setAccelerateEnabled(boolean accelerateEnabled) { + this.accelerateEnabled = accelerateEnabled; + } + + /** + * 获取当前状态下的静态模型(用于渲染) + * + * @return 当前状态的模型 + */ + public IPartModel getStaticModels() { + if (this.isActive() && this.isPowered()) { + return MODELS_HAS_CHANNEL; + } else if (this.isPowered()) { + return MODELS_ON; + } else { + return MODELS_OFF; + } + } + + + + /** + * 当玩家激活部件(右键)时调用,打开自定义菜单 + * + * @param player 玩家 + * @param pos 点击位置 + * @return 总是返回 true,表示激活成功 + */ + @Override + public final boolean onUseWithoutItem(Player player, Vec3 pos) { + if (!isClientSide()) { + MenuOpener.open(ModMenuTypes.ENTITY_TICKER_MENU.get(), player, MenuLocators.forPart(this)); + } + return true; + } + + /** + * 定义部件的碰撞箱(用于物理碰撞和渲染) + * + * @param bch 碰撞辅助器 + */ + @Override + public void getBoxes(IPartCollisionHelper bch) { + bch.addBox(2, 2, 14, 14, 14, 16); + bch.addBox(5, 5, 12, 11, 11, 14); + } + + /** + * 获取定时请求,决定本部件多久 tick 一次 + * + * @param iGridNode 网络节点 + * @return TickingRequest 对象 + */ + @Override + public TickingRequest getTickingRequest(IGridNode iGridNode) { + // 每 1 tick 执行一次 + return new TickingRequest(1, 1, false); + } + + /** + * 当升级卡数量发生变化时调用,通知菜单更新 + */ + @Override + public void upgradesChanged() { + if (this.menu != null) { + // 使用 AE2 风格:当升级发生变化时让菜单广播变化(槽/数据会被同步),客户端会基于槽内容重新计算并刷新界面 + this.menu.broadcastChanges(); + } + } + + /** + * 网络定时回调,每次 tick 时调用 + * + * @param iGridNode 网络节点 + * @param ticksSinceLastCall 距离上次调用经过的 tick 数 + * @return TickRateModulation.IDLE 表示继续保持当前 tick 速率 + */ + @Override + public TickRateModulation tickingRequest(IGridNode iGridNode, int ticksSinceLastCall) { + // 如果部件的加速开关被关闭,则不进行加速(提前返回) + if (!this.getAccelerateEnabled()) { + return TickRateModulation.IDLE; + } + + // 获取目标方块实体(本部件朝向的方块) + BlockEntity target = getLevel().getBlockEntity(getBlockEntity().getBlockPos().relative(getSide())); + // 仅在目标存在且部件处于激活状态时执行加速 + if (target != null && isActive()) { + ticker(target); + } + return TickRateModulation.IDLE; + } + + /** + * 以指定速度对目标方块实体进行 tick 操作 + * + * @param blockEntity 需要被 tick 的方块实体 + * @param 方块实体类型 + */ + private void ticker(@NotNull T blockEntity) { + if (this.getGridNode() == null + || this.getMainNode() == null + || this.getMainNode().getGrid() == null) { + return; + } + + // 获取方块实体的位置 + BlockPos pos = blockEntity.getBlockPos(); + if (blockEntity.getLevel() == null) return; + + + // 检查黑名单(支持通配符/正则) + String blockId = BuiltInRegistries.BLOCK.getKey(blockEntity.getBlockState().getBlock()).toString(); + + // 使用工具类的缓存接口(工具类内部负责懒加载/线程安全) + List compiledBlacklist = ConfigParsingUtils.getCachedBlacklist(ModConfigs.ENTITY_TICKER_BLACK_LIST.get()); + for (Pattern p : compiledBlacklist) { + if (p.matcher(blockId).matches()) return; + } + + // 获取该方块实体的 Ticker + @SuppressWarnings("unchecked") + BlockEntityTicker blockEntityTicker = this.getLevel() + .getBlockState(pos) + .getTicker(this.getLevel(), (BlockEntityType) blockEntity.getType()); + if (blockEntityTicker == null) return; + + // 使用集中定义的 CardDef 列表,支持以后添加等级或改倍率而无需修改此逻辑 + int energyCardCount = getUpgrades().getInstalledUpgrades(AEItems.ENERGY_CARD); + + // 使用已注册的单一 Item 计算已安装卡数量(总计,用于能耗计算) + int entitySpeedCardCount = getUpgrades().getInstalledUpgrades(ModItems.ENTITY_SPEED_CARD.get()); + + // 使用工具方法从槽位直接计算乘积并应用 cap(最多 8 张卡) + long product = PowerUtils.computeProductWithCapFromStacks(this.getUpgrades(), 8); + + // 如果没有任何实体加速卡,则不进行加速且不消耗额外能量(只保留部件的被动功耗) + if (entitySpeedCardCount <= 0) return; + + // 计算本次 tick 所需能量:使用工具类根据 product 计算最终能耗 + double requiredPower = PowerUtils.computeFinalPowerForProduct(product, energyCardCount); + + int speed = (int) product; + + double multiplier = 1.0; + for (ConfigParsingUtils.MultiplierEntry me : ConfigParsingUtils.getCachedMultiplierEntries(ModConfigs.ENTITY_TICKER_MULTIPLIERS.get())) { + if (me.pattern.matcher(blockId).matches()) { + multiplier = Math.max(multiplier, me.multiplier); + } + } + + requiredPower *= multiplier; + + // 先模拟提取以检查网络中是否有足够能量,再真正抽取 + double simulated = getMainNode().getGrid().getEnergyService() + .extractAEPower(requiredPower, Actionable.SIMULATE, PowerMultiplier.CONFIG); + if (simulated < requiredPower) return; + + double extractedPower = getMainNode().getGrid().getEnergyService() + .extractAEPower(requiredPower, Actionable.MODULATE, PowerMultiplier.CONFIG); + if (extractedPower < requiredPower) return; + + // 计算加速倍数:基于 2 的次方,并把 8 张映射到最大 1024x(2^10) + // 已由 product 计算得到 speed;上面已在没有卡时提前返回 + + // 执行 tick 操作 + for (int i = 0; i < speed - 1; i++) { + blockEntityTicker.tick( + blockEntity.getLevel(), + blockEntity.getBlockPos(), + blockEntity.getBlockState(), + blockEntity + ); + } + } + + /** + * 判断部件是否有自定义名称 + * + * @return 是否有自定义名称 + */ + @Override + public boolean hasCustomName() { + return super.hasCustomName(); + } + + /** + * 获取部件的显示名称 + * + * @return 显示名称 + */ + @Override + public @NotNull Component getDisplayName() { + return super.getDisplayName(); + } + + /** + * 创建自定义菜单(GUI) + * + * @param containerId 容器ID + * @param playerInventory 玩家背包 + * @param player 玩家 + * @return 菜单实例 + */ + @Override + public @Nullable AbstractContainerMenu createMenu(int containerId, + @NotNull Inventory playerInventory, + @NotNull Player player) { + return new EntitySpeedTickerMenu(containerId, playerInventory, this); + } + + /** + * 获取可用的升级卡槽数量 + * + * @return 升级卡槽数量 + */ + @Override + protected int getUpgradeSlots() { + return 8; + } +} \ No newline at end of file diff --git a/src/main/java/com/extendedae_plus/ae/screen/EntitySpeedTickerScreen.java b/src/main/java/com/extendedae_plus/ae/screen/EntitySpeedTickerScreen.java new file mode 100644 index 0000000..32421e2 --- /dev/null +++ b/src/main/java/com/extendedae_plus/ae/screen/EntitySpeedTickerScreen.java @@ -0,0 +1,136 @@ +package com.extendedae_plus.ae.screen; + +import appeng.api.config.Settings; +import appeng.api.config.YesNo; +import appeng.client.gui.Icon; +import appeng.client.gui.implementations.UpgradeableScreen; +import appeng.client.gui.style.ScreenStyle; +import appeng.client.gui.widgets.CommonButtons; +import appeng.client.gui.widgets.SettingToggleButton; +import appeng.util.Platform; +import com.extendedae_plus.ae.menu.EntitySpeedTickerMenu; +import com.extendedae_plus.network.ToggleEntityTickerC2SPacket; +import com.extendedae_plus.util.PowerUtils; +import net.minecraft.client.Minecraft; +import net.minecraft.network.chat.Component; +import net.minecraft.world.entity.player.Inventory; + +import java.util.List; + +public class EntitySpeedTickerScreen extends UpgradeableScreen { + private boolean eap$entitySpeedTickerEnabled = false; + private SettingToggleButton eap$entitySpeedTickerToggle; + + public EntitySpeedTickerScreen( + EntitySpeedTickerMenu menu, Inventory playerInventory, Component title, ScreenStyle style) { + super((C) menu, playerInventory, title, style); + this.addToLeftToolbar(CommonButtons.togglePowerUnit()); + + try{ + this.eap$entitySpeedTickerEnabled = menu.getAccelerateEnabled(); + }catch (Exception ignored){} + + // 使用 SettingToggleButton 的外观(原版图标),但自定义悬停描述为“智能阻挡” + // 不做本地切换,点击仅发送自定义C2S,显示由@GuiSync回传 + eap$entitySpeedTickerToggle = new SettingToggleButton<>( + Settings.BLOCKING_MODE, + this.eap$entitySpeedTickerEnabled ? YesNo.YES : YesNo.NO, + (btn, backwards) -> { + // 不做本地切换,点击仅发送自定义C2S,显示由@GuiSync回传 + var conn = Minecraft.getInstance().getConnection(); + if (conn != null) conn.send(ToggleEntityTickerC2SPacket.INSTANCE); + } + ) { + @Override + public List getTooltipMessage() { + // 如果目标在黑名单中,直接显示已禁用的提示 + try { + if (menu != null && menu.targetBlacklisted) { + var title = Component.literal("实体加速"); + var stateLine = Component.literal("已禁用(目标在黑名单)"); + return List.of(title, stateLine); + } + } catch (Exception ignored) {} + + boolean enabled = eap$entitySpeedTickerEnabled; + var title = Component.literal("实体加速"); + var stateLine = enabled + ? Component.literal("已启用: 将加速目标方块实体的tick") + : Component.literal("已关闭: 不会对目标方块实体进行加速"); + return List.of(title, stateLine); + } + + @Override + protected Icon getIcon() { + try { + if (menu != null && menu.targetBlacklisted) { + // 黑名单时显示禁用图标 + return Icon.INVALID; + } + } catch (Exception ignored) {} + + // 根据当前值显示不同图标(可按需替换 Icon 常量) + if (this.getCurrentValue() == YesNo.YES) { + return Icon.VALID; + } else { + return Icon.INVALID; + } + } + }; + // 初始化后立刻对齐当前@GuiSync状态,避免首帧显示不一致 + eap$entitySpeedTickerToggle.set(this.eap$entitySpeedTickerEnabled ? YesNo.YES : YesNo.NO); + + this.addToLeftToolbar(eap$entitySpeedTickerToggle); + } + + @Override + protected void updateBeforeRender() { + super.updateBeforeRender(); + + if (this.eap$entitySpeedTickerToggle != null) { + boolean desired = this.eap$entitySpeedTickerEnabled; + if (this.menu != null) { + desired = this.menu.getAccelerateEnabled(); + } + + this.eap$entitySpeedTickerEnabled = desired; + // 如果目标在黑名单中,禁用切换并强制显示为关闭 + if (this.menu != null && this.menu.targetBlacklisted) { + this.eap$entitySpeedTickerToggle.set(YesNo.NO); + this.eap$entitySpeedTickerToggle.active = false; + } else { + this.eap$entitySpeedTickerToggle.set(desired ? YesNo.YES : YesNo.NO); + this.eap$entitySpeedTickerToggle.active = true; + } + } + textData(); + } + + public void refreshGui() { + textData(); + } + + private void textData() { + // 如果目标被黑名单禁止,则显示禁用状态并把数值显示为 0 + if (getMenu().targetBlacklisted) { + setTextContent("enable", Component.translatable("screen.extendedae_plus.entity_speed_ticker.enable")); + setTextContent("speed", Component.translatable("screen.extendedae_plus.entity_speed_ticker.speed", 0)); + setTextContent("energy", Component.translatable("screen.extendedae_plus.entity_speed_ticker.energy", Platform.formatPower(0.0, false))); + setTextContent("power_ratio", Component.translatable("screen.extendedae_plus.entity_speed_ticker.power_ratio", PowerUtils.formatPercentage(0.0))); + setTextContent("multiplier", Component.translatable("screen.extendedae_plus.entity_speed_ticker.multiplier", String.format("%.2fx", 0.0))); + return; + } + + int energyCardCount = getMenu().energyCardCount; + double multiplier = getMenu().multiplier; + int effectiveSpeed = getMenu().effectiveSpeed; + + double finalPower = PowerUtils.computeFinalPowerForProduct(effectiveSpeed, energyCardCount); + double remainingRatio = PowerUtils.getRemainingRatio(energyCardCount); + + 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))); + setTextContent("multiplier", Component.translatable("screen.extendedae_plus.entity_speed_ticker.multiplier", String.format("%.2fx", multiplier))); + } +} \ No newline at end of file diff --git a/src/main/java/com/extendedae_plus/client/ClientProxy.java b/src/main/java/com/extendedae_plus/client/ClientProxy.java index 5a7d020..9c373ee 100644 --- a/src/main/java/com/extendedae_plus/client/ClientProxy.java +++ b/src/main/java/com/extendedae_plus/client/ClientProxy.java @@ -1,18 +1,21 @@ package com.extendedae_plus.client; import appeng.client.render.crafting.CraftingCubeModel; +import appeng.init.client.InitScreens; import com.extendedae_plus.ExtendedAEPlus; +import com.extendedae_plus.ae.definitions.upgrades.EntitySpeedCardItem; +import com.extendedae_plus.ae.menu.EntitySpeedTickerMenu; +import com.extendedae_plus.ae.screen.EntitySpeedTickerScreen; import com.extendedae_plus.client.render.crafting.EPlusCraftingCubeModelProvider; -import com.extendedae_plus.client.screen.GlobalProviderModesScreen; -import com.extendedae_plus.init.ModMenuTypes; import com.extendedae_plus.content.crafting.EPlusCraftingUnitType; import com.extendedae_plus.hooks.BuiltInModelHooks; -import net.neoforged.fml.event.lifecycle.FMLClientSetupEvent; -import net.minecraft.client.gui.screens.MenuScreens; -import net.neoforged.neoforge.client.event.RegisterMenuScreensEvent; -import net.neoforged.fml.common.EventBusSubscriber; +import com.extendedae_plus.init.ModItems; +import com.extendedae_plus.init.ModMenuTypes; +import net.minecraft.client.renderer.item.ItemProperties; import net.neoforged.api.distmarker.Dist; import net.neoforged.bus.api.SubscribeEvent; +import net.neoforged.fml.common.EventBusSubscriber; +import net.neoforged.neoforge.client.event.RegisterMenuScreensEvent; /** * 客户端模型注册,将 formed 模型注册为内置模型。 @@ -26,6 +29,10 @@ public final class ClientProxy { public static void init() { if (REGISTERED) return; REGISTERED = true; + // 注册 Item property,用于根据 ItemStack 的 NBT exponent 切换模型 + ItemProperties.register(ModItems.ENTITY_SPEED_CARD.get(), ExtendedAEPlus.id("mult"), + (stack, world, entity, seed) -> (float) EntitySpeedCardItem.readMultiplier(stack)); + // 注册四种形成态模型为内置模型 BuiltInModelHooks.addBuiltInModel( ExtendedAEPlus.id("block/crafting/4x_accelerator_formed_v2"), @@ -46,16 +53,7 @@ public final class ClientProxy { BuiltInModelHooks.addBuiltInModel( ExtendedAEPlus.id("block/crafting/1024x_accelerator_formed_v2"), new CraftingCubeModel(new EPlusCraftingCubeModelProvider(EPlusCraftingUnitType.ACCELERATOR_1024x))); - } - /** - * 客户端设置阶段:延迟执行需要访问注册对象的客户端注册。 - */ - public static void onClientSetup(final FMLClientSetupEvent event) { - event.enqueueWork(() -> { - // 确保在首次资源加载前完成内置模型注册(REGISTERED 保护避免重复) - init(); - }); } @SubscribeEvent @@ -75,5 +73,10 @@ public final class ClientProxy { } } ); + + /** + * 注册由 AE2 InitScreens 所需的屏幕资源映射(用于内置 JSON 屏幕注册) + */ + InitScreens.register(event, ModMenuTypes.ENTITY_TICKER_MENU.get(), EntitySpeedTickerScreen::new, "/screens/entity_speed_ticker.json"); } } diff --git a/src/main/java/com/extendedae_plus/client/ui/ProviderSelectScreen.java b/src/main/java/com/extendedae_plus/client/ui/ProviderSelectScreen.java index 11426b6..f0704e0 100644 --- a/src/main/java/com/extendedae_plus/client/ui/ProviderSelectScreen.java +++ b/src/main/java/com/extendedae_plus/client/ui/ProviderSelectScreen.java @@ -1,6 +1,5 @@ package com.extendedae_plus.client.ui; -import com.extendedae_plus.network.ModNetwork; import com.extendedae_plus.network.UploadEncodedPatternToProviderC2SPacket; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.components.Button; diff --git a/src/main/java/com/extendedae_plus/config/ModConfigs.java b/src/main/java/com/extendedae_plus/config/ModConfigs.java index d74f914..5abe719 100644 --- a/src/main/java/com/extendedae_plus/config/ModConfigs.java +++ b/src/main/java/com/extendedae_plus/config/ModConfigs.java @@ -2,6 +2,8 @@ package com.extendedae_plus.config; import net.neoforged.neoforge.common.ModConfigSpec; +import java.util.List; + public final class ModConfigs { public static final ModConfigSpec COMMON_SPEC; public static final ModConfigSpec.IntValue PAGE_MULTIPLIER; @@ -12,7 +14,9 @@ public final class ModConfigs { public static final ModConfigSpec.BooleanValue PATTERN_TERMINAL_SHOW_SLOTS_DEFAULT; public static final ModConfigSpec.IntValue SMART_SCALING_MAX_MULTIPLIER; public static final ModConfigSpec.IntValue CRAFTING_PAUSE_THRESHOLD; - + public static final ModConfigSpec.IntValue ENTITY_TICKER_COST; + public static final ModConfigSpec.ConfigValue> ENTITY_TICKER_BLACK_LIST; + public static final ModConfigSpec.ConfigValue> ENTITY_TICKER_MULTIPLIERS; static { ModConfigSpec.Builder builder = new ModConfigSpec.Builder(); @@ -22,7 +26,8 @@ public final class ModConfigs { .comment( "扩展样板供应器总槽位容量的倍率", "基础为36,每页仍显示36格,倍率会增加总页数/总容量", - "建议范围 1-16") + "建议范围 1-16" + ) .defineInRange("pageMultiplier", 1, 1, 64); // 是否显示样板编码玩家(通用) @@ -59,7 +64,8 @@ public final class ModConfigs { "智能倍增时是否对样板供应器轮询分配", "仅多个供应器有相同样板时生效,开启后请求会均分到所有可用供应器,关闭则全部分配给单一供应器", "注意:所有相关供应器需开启智能倍增,否则可能失效", - "默认: true") + "默认: true" + ) .define("providerRoundRobinEnable", true); // 智能倍增的最大倍数(以单次样板产出为单位)。 @@ -67,7 +73,8 @@ public final class ModConfigs { SMART_SCALING_MAX_MULTIPLIER = builder .comment( "智能倍增的最大倍数(0 表示不限制)", - "此倍数是针对单次样板产出的放大倍数上限,用于限制一次推送中按倍增缩放的规模") + "此倍数是针对单次样板产出的放大倍数上限,用于限制一次推送中按倍增缩放的规模" + ) .defineInRange("smartScalingMaxMultiplier", 0, 0, 1048576); builder.pop(); // pop smart @@ -79,19 +86,55 @@ public final class ModConfigs { WIRELESS_MAX_RANGE = builder .comment( "无线收发器最大连接距离(单位:方块)", - "从端与主端的直线距离需小于等于该值才会建立连接。") + "从端与主端的直线距离需小于等于该值才会建立连接。" + ) .defineInRange("wirelessMaxRange", 256.0D, 1.0D, 4096.0D); // 是否允许跨维度连接(忽略维度差异进行频道传输)。 WIRELESS_CROSS_DIM_ENABLE = builder .comment( "是否允许无线收发器跨维度建立连接", - "开启后,从端可连接到不同维度的主端(忽略距离限制)") + "开启后,从端可连接到不同维度的主端(忽略距离限制)" + ) .define("wirelessCrossDimEnable", true); builder.pop(); // pop wireless -// builder.pop(); // pop extendedae_plus + builder.push("entitySpeedTicker"); + + ENTITY_TICKER_COST = builder + .comment( + "实体加速器能量消耗基础值" + ) + .defineInRange("entityTickerCost", 512, 0 , Integer.MAX_VALUE); + + + ENTITY_TICKER_BLACK_LIST = builder + .comment( + "实体加速器黑名单:匹配的方块将不会被加速。支持通配符/正则(例如:minecraft:*)", + "格式:全名或通配符/正则字符串,例如 'minecraft:chest'、'minecraft:*'、'modid:.*_fluid'" + ) + .defineListAllowEmpty( + List.of("entityTickerBlackList"), // 路径 + List::of, // 默认值 + () -> "", // 新元素默认值(空字符串,供配置编辑器使用) + obj -> obj instanceof String // 验证每个元素是字符串 + ); + + ENTITY_TICKER_MULTIPLIERS = builder + .comment( + "额外消耗倍率配置:为某些方块设置额外能量倍率,格式 'modid:blockid multiplier',例如 'minecraft:chest 2x'", + "支持通配符/正则匹配(例如 'minecraft:* 2x' 会对整个命名空间生效)。" + ) + .defineListAllowEmpty( + List.of("entityTickerMultipliers"), // 路径 + List::of, // 默认值 + () -> "", // 新元素默认值(空字符串,供配置编辑器使用) + obj -> obj instanceof String // 验证每个元素是字符串 + ); + + builder.pop(); + COMMON_SPEC = builder.build(); } diff --git a/src/main/java/com/extendedae_plus/init/ModCreativeTabs.java b/src/main/java/com/extendedae_plus/init/ModCreativeTabs.java index f07a83b..f6c2a3c 100644 --- a/src/main/java/com/extendedae_plus/init/ModCreativeTabs.java +++ b/src/main/java/com/extendedae_plus/init/ModCreativeTabs.java @@ -26,8 +26,12 @@ public final class ModCreativeTabs { output.accept(ModItems.ACCELERATOR_64x.get()); output.accept(ModItems.ACCELERATOR_256x.get()); output.accept(ModItems.ACCELERATOR_1024x.get()); - -// output.accept(ModItems.INFINITY_BIGINTEGER_CELL_ITEM.get()); + output.accept(ModItems.ENTITY_TICKER_PART_ITEM.get()); + // 放入四个预设的 stacks(x2,x4,x8,x16),使用 ModItems 工厂创建 + output.accept(ModItems.createEntitySpeedCardStack(2)); + output.accept(ModItems.createEntitySpeedCardStack(4)); + output.accept(ModItems.createEntitySpeedCardStack(8)); + output.accept(ModItems.createEntitySpeedCardStack(16)); }) .build()); } diff --git a/src/main/java/com/extendedae_plus/init/ModItems.java b/src/main/java/com/extendedae_plus/init/ModItems.java index 71a6220..1f2dcc6 100644 --- a/src/main/java/com/extendedae_plus/init/ModItems.java +++ b/src/main/java/com/extendedae_plus/init/ModItems.java @@ -1,8 +1,14 @@ package com.extendedae_plus.init; +import appeng.api.parts.IPart; +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.EntitySpeedTickerPartItem; import net.minecraft.world.item.BlockItem; import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; import net.neoforged.neoforge.registries.DeferredItem; import net.neoforged.neoforge.registries.DeferredRegister; @@ -47,8 +53,34 @@ public final class ModItems { () -> new BlockItem(ModBlocks.ACCELERATOR_1024x.get(), new Item.Properties()) ); -// public static final DeferredItem INFINITY_BIGINTEGER_CELL_ITEM = ITEMS.register( -// "infinity_biginteger_cell", InfinityBigIntegerCellItem::new -// ); + public static final DeferredItem ENTITY_TICKER_PART_ITEM = ITEMS.register( + "entity_speed_ticker", + () -> new EntitySpeedTickerPartItem(new Item.Properties()) + ); + // AE Upgrade Cards: 实体加速卡(四个等级:x2,x4,x8,x16) + // 单一实体加速卡 Item(不同等级由 ItemStack.nbt 存储) + public static final DeferredItem ENTITY_SPEED_CARD = ITEMS.register( + "entity_speed_card", + () -> new EntitySpeedCardItem(new Item.Properties()) + ); + + /** + * 为 PartItem 注册 AE2 部件模型。 + * 在客户端进行模型/几何体注册时调用。 + */ + public static void registerPartModels() { + PartModels.registerModels( + PartModelsHelper.createModels( + ENTITY_TICKER_PART_ITEM.get().getPartClass().asSubclass(IPart.class) + ) + ); + } + + /** + * 工厂:创建带 multiplier 的实体加速卡 ItemStack(2/4/8/16) + */ + public static ItemStack createEntitySpeedCardStack(int multiplier) { + return EntitySpeedCardItem.withMultiplier(multiplier); + } } diff --git a/src/main/java/com/extendedae_plus/init/ModMenuTypes.java b/src/main/java/com/extendedae_plus/init/ModMenuTypes.java index 960ece9..c42986b 100644 --- a/src/main/java/com/extendedae_plus/init/ModMenuTypes.java +++ b/src/main/java/com/extendedae_plus/init/ModMenuTypes.java @@ -1,12 +1,15 @@ package com.extendedae_plus.init; +import appeng.menu.implementations.MenuTypeBuilder; import com.extendedae_plus.ExtendedAEPlus; +import com.extendedae_plus.ae.menu.EntitySpeedTickerMenu; +import com.extendedae_plus.ae.parts.EntitySpeedTickerPart; import com.extendedae_plus.menu.NetworkPatternControllerMenu; -import net.minecraft.world.inventory.MenuType; import net.minecraft.core.registries.Registries; +import net.minecraft.world.inventory.MenuType; +import net.neoforged.neoforge.common.extensions.IMenuTypeExtension; import net.neoforged.neoforge.registries.DeferredHolder; import net.neoforged.neoforge.registries.DeferredRegister; -import net.neoforged.neoforge.common.extensions.IMenuTypeExtension; public final class ModMenuTypes { private ModMenuTypes() {} @@ -16,5 +19,11 @@ public final class ModMenuTypes { public static final DeferredHolder, MenuType> NETWORK_PATTERN_CONTROLLER = MENUS.register("network_pattern_controller", - () -> IMenuTypeExtension.create((id, inv, buf) -> new NetworkPatternControllerMenu(id, inv, buf))); + () -> IMenuTypeExtension.create(NetworkPatternControllerMenu::new)); + + public static final DeferredHolder, MenuType> ENTITY_TICKER_MENU = + MENUS.register("entity_speed_ticker", + () -> MenuTypeBuilder + .create(EntitySpeedTickerMenu::new, EntitySpeedTickerPart.class) + .build("entity_speed_ticker")); } diff --git a/src/main/java/com/extendedae_plus/network/ModNetwork.java b/src/main/java/com/extendedae_plus/init/ModNetwork.java similarity index 94% rename from src/main/java/com/extendedae_plus/network/ModNetwork.java rename to src/main/java/com/extendedae_plus/init/ModNetwork.java index 6a57257..4d618f5 100644 --- a/src/main/java/com/extendedae_plus/network/ModNetwork.java +++ b/src/main/java/com/extendedae_plus/init/ModNetwork.java @@ -1,12 +1,14 @@ -package com.extendedae_plus.network; +package com.extendedae_plus.init; import com.extendedae_plus.ExtendedAEPlus; +import com.extendedae_plus.network.*; import net.neoforged.neoforge.network.event.RegisterPayloadHandlersEvent; public class ModNetwork { // 在 Mod 构造中通过 modEventBus.addListener(ModNetwork::registerPayloadHandlers) 注册 public static void registerPayloadHandlers(final RegisterPayloadHandlersEvent event) { var registrar = event.registrar(ExtendedAEPlus.MODID); + registrar.playToServer(ToggleEntityTickerC2SPacket.TYPE, ToggleEntityTickerC2SPacket.STREAM_CODEC, ToggleEntityTickerC2SPacket::handle); registrar.playToServer(ToggleAdvancedBlockingC2SPacket.TYPE, ToggleAdvancedBlockingC2SPacket.STREAM_CODEC, ToggleAdvancedBlockingC2SPacket::handle); registrar.playToServer(ToggleSmartDoublingC2SPacket.TYPE, ToggleSmartDoublingC2SPacket.STREAM_CODEC, ToggleSmartDoublingC2SPacket::handle); registrar.playToServer(ScalePatternsC2SPacket.TYPE, ScalePatternsC2SPacket.STREAM_CODEC, ScalePatternsC2SPacket::handle); diff --git a/src/main/java/com/extendedae_plus/init/UpgradeCards.java b/src/main/java/com/extendedae_plus/init/UpgradeCards.java new file mode 100644 index 0000000..9a8de67 --- /dev/null +++ b/src/main/java/com/extendedae_plus/init/UpgradeCards.java @@ -0,0 +1,17 @@ +package com.extendedae_plus.init; + +import appeng.api.upgrades.Upgrades; +import appeng.core.definitions.AEItems; +import net.neoforged.fml.event.lifecycle.FMLCommonSetupEvent; + + +public class UpgradeCards { + public UpgradeCards(final FMLCommonSetupEvent event) { + event.enqueueWork(() -> { + // 现有:把 Entity Ticker 的部件注册为处理 SPEED/ENERGY 卡的宿主 + 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"); + }); + } +} \ No newline at end of file diff --git a/src/main/java/com/extendedae_plus/integration/jei/ExtendedAEJeiPlugin.java b/src/main/java/com/extendedae_plus/integration/jei/ExtendedAEJeiPlugin.java index 8c249a2..eecb9dd 100644 --- a/src/main/java/com/extendedae_plus/integration/jei/ExtendedAEJeiPlugin.java +++ b/src/main/java/com/extendedae_plus/integration/jei/ExtendedAEJeiPlugin.java @@ -1,10 +1,17 @@ package com.extendedae_plus.integration.jei; import com.extendedae_plus.ExtendedAEPlus; +import com.extendedae_plus.ae.definitions.upgrades.EntitySpeedCardItem; import mezz.jei.api.IModPlugin; import mezz.jei.api.JeiPlugin; +import mezz.jei.api.constants.VanillaTypes; +import mezz.jei.api.ingredients.subtypes.ISubtypeInterpreter; +import mezz.jei.api.ingredients.subtypes.UidContext; +import mezz.jei.api.registration.ISubtypeRegistration; import mezz.jei.api.runtime.IJeiRuntime; import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.ItemStack; +import org.jetbrains.annotations.NotNull; @JeiPlugin public class ExtendedAEJeiPlugin implements IModPlugin { @@ -19,4 +26,28 @@ public class ExtendedAEJeiPlugin implements IModPlugin { public void onRuntimeAvailable(IJeiRuntime jeiRuntime) { JeiRuntimeProxy.setRuntime(jeiRuntime); } + + + @Override + public void registerItemSubtypes(ISubtypeRegistration registration) { + // Register NBT-based subtype interpreter so JEI treats different multipliers as distinct items + registration.registerSubtypeInterpreter( + VanillaTypes.ITEM_STACK, + com.extendedae_plus.init.ModItems.ENTITY_SPEED_CARD.get(), + new ISubtypeInterpreter() { + @Override + public @NotNull Object getSubtypeData(@NotNull ItemStack ingredient, @NotNull UidContext context) { + // 返回你想让 JEI 区分子类型的数据,这里用 multiplier + return EntitySpeedCardItem.readMultiplier(ingredient); + } + + @Override + public @NotNull String getLegacyStringSubtypeInfo(@NotNull ItemStack ingredient, @NotNull UidContext context) { + // 返回同样的值给旧接口兼容 + return String.valueOf(EntitySpeedCardItem.readMultiplier(ingredient)); + } + } + ); + + } } diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/client/gui/AEBaseScreenMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/client/gui/AEBaseScreenMixin.java index bf5f561..b423345 100644 --- a/src/main/java/com/extendedae_plus/mixin/ae2/client/gui/AEBaseScreenMixin.java +++ b/src/main/java/com/extendedae_plus/mixin/ae2/client/gui/AEBaseScreenMixin.java @@ -17,7 +17,6 @@ import com.extendedae_plus.api.ExPatternPageAccessor; import com.extendedae_plus.content.ClientPatternHighlightStore; import com.extendedae_plus.network.CraftingMonitorJumpC2SPacket; import com.extendedae_plus.network.CraftingMonitorOpenProviderC2SPacket; -import com.extendedae_plus.network.ModNetwork; import com.extendedae_plus.util.GuiUtil; import com.glodblock.github.extendedae.client.gui.GuiExPatternProvider; import com.mojang.logging.LogUtils; diff --git a/src/main/java/com/extendedae_plus/network/ToggleEntityTickerC2SPacket.java b/src/main/java/com/extendedae_plus/network/ToggleEntityTickerC2SPacket.java new file mode 100644 index 0000000..0407f95 --- /dev/null +++ b/src/main/java/com/extendedae_plus/network/ToggleEntityTickerC2SPacket.java @@ -0,0 +1,50 @@ +package com.extendedae_plus.network; + +import com.extendedae_plus.ExtendedAEPlus; +import com.extendedae_plus.ae.menu.EntitySpeedTickerMenu; +import com.extendedae_plus.ae.parts.EntitySpeedTickerPart; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.network.codec.StreamCodec; +import net.minecraft.network.protocol.common.custom.CustomPacketPayload; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.level.ServerPlayer; +import net.neoforged.neoforge.network.handling.IPayloadContext; + +/** + * C2S: Toggle the accelerateEnabled flag on the EntitySpeedTickerPart bound to the open menu. + */ +public class ToggleEntityTickerC2SPacket implements CustomPacketPayload { + public static final CustomPacketPayload.Type TYPE = new CustomPacketPayload.Type<>( + ResourceLocation.fromNamespaceAndPath(ExtendedAEPlus.MODID, "toggle_entity_ticker")); + + public static final ToggleEntityTickerC2SPacket INSTANCE = new ToggleEntityTickerC2SPacket(); + + public static final StreamCodec STREAM_CODEC = + StreamCodec.unit(INSTANCE); + + private ToggleEntityTickerC2SPacket() {} + + @Override + public CustomPacketPayload.Type type() { + return TYPE; + } + + public static void handle(final ToggleEntityTickerC2SPacket msg, final IPayloadContext ctx) { + ctx.enqueueWork(() -> { + if (!(ctx.player() instanceof ServerPlayer player)) return; + + if (!(player.containerMenu instanceof EntitySpeedTickerMenu menu)) return; + + EntitySpeedTickerPart part = menu.getHost(); + if (part == null) return; + + // 切换部件上的状态,并把新状态同步到菜单字段,随后广播以通知客户端 + boolean current = part.getAccelerateEnabled(); + boolean next = !current; + part.setAccelerateEnabled(next); + // 确保菜单上的字段也被更新,这样 @GuiSync 会把状态发回客户端 + menu.setAccelerateEnabled(next); + menu.broadcastChanges(); + }); + } +} \ No newline at end of file diff --git a/src/main/java/com/extendedae_plus/util/ConfigParsingUtils.java b/src/main/java/com/extendedae_plus/util/ConfigParsingUtils.java new file mode 100644 index 0000000..c1f2395 --- /dev/null +++ b/src/main/java/com/extendedae_plus/util/ConfigParsingUtils.java @@ -0,0 +1,172 @@ +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; +/** + * 配置解析工具类:用于解析黑名单与倍率配置的字符串 + */ +public final class ConfigParsingUtils { + private ConfigParsingUtils() {} + + public static final class MultiplierEntry { + public final Pattern pattern; + public final double multiplier; + + public MultiplierEntry(Pattern pattern, double multiplier) { + this.pattern = pattern; + this.multiplier = multiplier; + } + } + + /** + * 编译用户提供的匹配串。支持简单的 glob 语法('*' 和 '?')以及完整的正则表达式。 + * 规则: + * - 如果字符串包含 ".*" 或其他正则元字符,优先尝试按正则编译(若失败则回退到 glob 转换)。 + * - 否则若包含 '*' 或 '?',将按 glob 语法转换为正则。 + * - 否则按字面量匹配处理。 + */ + public static Pattern compilePattern(String raw) { + if (raw == null) throw new IllegalArgumentException("pattern is null"); + raw = raw.trim(); + if (raw.isEmpty()) throw new IllegalArgumentException("pattern is empty"); + + // If it looks like regex (contains '.*' or regex metachar), try regex first + if (raw.contains(".*") || raw.matches(".*[\\[\\(\\+\\{\\\\].*")) { + try { + return Pattern.compile("^" + raw + "$"); + } catch (PatternSyntaxException ignored) { + // fallback to glob below + } + } + + // If contains glob chars, convert to regex + if (raw.contains("*") || raw.contains("?")) { + StringBuilder sb = new StringBuilder(); + sb.append('^'); + for (char c : raw.toCharArray()) { + switch (c) { + case '*': sb.append(".*"); break; + case '?': sb.append('.'); break; + default: + // escape regex special chars + if (".\\+[]{}()^$|".indexOf(c) >= 0) { + sb.append('\\'); + } + sb.append(c); + } + } + sb.append('$'); + return Pattern.compile(sb.toString()); + } + + // Otherwise treat as a literal (match exact) + return Pattern.compile("^" + Pattern.quote(raw) + "$"); + } + + /** + * Parse multiplier entries like 'modid:block 2x' into MultiplierEntry objects. + * Accepts values with optional trailing 'x' (case-insensitive). + */ + public static MultiplierEntry parseMultiplierEntry(String entry) { + if (entry == null) return null; + String[] parts = entry.trim().split("\\s+"); + if (parts.length < 2) return null; + String key = parts[0]; + String val = parts[1].toLowerCase(); + if (val.endsWith("x")) val = val.substring(0, val.length() - 1); + double m; + try { + m = Double.parseDouble(val); + } catch (NumberFormatException ex) { + return null; + } + try { + Pattern p = compilePattern(key); + return new MultiplierEntry(p, m); + } catch (IllegalArgumentException e) { + return null; + } + } + + public static List compilePatterns(List raw) { + List out = new ArrayList<>(); + if (raw == null) return out; + for (String s : raw) { + if (s == null || s.isBlank()) continue; + try { out.add(compilePattern(s)); } catch (IllegalArgumentException ignored) {} + } + return out; + } + + public static List parseMultiplierList(List raw) { + List out = new ArrayList<>(); + if (raw == null) return out; + for (String s : raw) { + MultiplierEntry me = parseMultiplierEntry(s); + if (me != null) out.add(me); + } + return out; + } + + // ------------------ 全局缓存与接口 ------------------ + private static volatile List cachedBlacklist = null; + private static volatile List cachedMultiplierEntries = null; + private static final Object CACHE_LOCK = new Object(); + + /** + * 获取已解析并缓存的黑名单(线程安全、懒加载)。 + */ + public static List getCachedBlacklist(List source) { + if (cachedBlacklist == null) { + synchronized (CACHE_LOCK) { + if (cachedBlacklist == null) { + cachedBlacklist = compilePatterns(source); + } + } + } else { + synchronized (CACHE_LOCK) { + List newCachedBlackList = compilePatterns(source); + if (!cachedBlacklist.equals(newCachedBlackList)) { + cachedBlacklist = newCachedBlackList; + } + } + } + return Collections.unmodifiableList(cachedBlacklist); + } + + /** + * 获取已解析并缓存的倍率列表(线程安全、懒加载)。 + */ + public static List getCachedMultiplierEntries(List source) { + if (cachedMultiplierEntries == null) { + synchronized (CACHE_LOCK) { + if (cachedMultiplierEntries == null) { + cachedMultiplierEntries = parseMultiplierList(source); + } + } + }else { + synchronized (CACHE_LOCK) { + List newCachedMultiplierEntries = parseMultiplierList(source); + if (!cachedMultiplierEntries.equals(newCachedMultiplierEntries)) { + cachedMultiplierEntries = newCachedMultiplierEntries; + } + } + } + return Collections.unmodifiableList(cachedMultiplierEntries); + } + + /** + * 清空缓存,下一次获取时将重新从提供的源解析(或调用方可以重新调用 getter)。 + */ + public static void reload() { + synchronized (CACHE_LOCK) { + cachedBlacklist = null; + cachedMultiplierEntries = null; + } + } +} + + diff --git a/src/main/java/com/extendedae_plus/util/PowerUtils.java b/src/main/java/com/extendedae_plus/util/PowerUtils.java new file mode 100644 index 0000000..4da3f2c --- /dev/null +++ b/src/main/java/com/extendedae_plus/util/PowerUtils.java @@ -0,0 +1,162 @@ +package com.extendedae_plus.util; + +import com.extendedae_plus.ae.definitions.upgrades.EntitySpeedCardItem; +import com.extendedae_plus.config.ModConfigs; + + +/** + * 用于计算实体加速器的能耗与加速倍率的工具类 + */ +public final class PowerUtils { + private PowerUtils() {} + // ---- 重构后的 API ---- + /** + * 将 card multipliers(按插槽序)计算乘积并应用 cap 规则(见 capForHighestMultiplier) + * @param multipliers iterable of per-card multipliers + * @param maxCards 最多计入的卡数 + * @return 被 cap 约束后的乘积 + */ + public static long computeProductWithCap(Iterable multipliers, int maxCards) { + long product = 1L; + int considered = 0; + int highest = 1; + for (Integer m : multipliers) { + if (m == null) continue; + if (considered >= maxCards) break; + int mult = m.intValue(); + if (mult <= 0) mult = 1; + product *= mult; + highest = Math.max(highest, mult); + considered++; + } + long cap = capForHighestMultiplier(highest); + return Math.min(product, cap); + } + + /** + * 根据最高单卡 multiplier 返回 cap 值 + */ + public static long capForHighestMultiplier(int highestMultiplier) { + if (highestMultiplier >= 16) return 1024L; + if (highestMultiplier >= 8) return 256L; + if (highestMultiplier >= 4) return 64L; + if (highestMultiplier >= 2) return 8L; + return 1L; + } + + /** + * 从菜单对象读取前 maxCards 个加速卡的 multiplier 并计算 product with cap + */ + public static long computeProductWithCapFromMenu(appeng.menu.implementations.UpgradeableMenu menu, int maxCards) { + java.util.List list = new java.util.ArrayList<>(); + int considered = 0; + for (var stack : menu.getUpgrades()) { + if (considered >= maxCards) break; + if (stack != null && !stack.isEmpty() && stack.getItem() instanceof EntitySpeedCardItem) { + int multVal = EntitySpeedCardItem.readMultiplier(stack); + int count = Math.min(stack.getCount(), maxCards - considered); + for (int i = 0; i < count; i++) { + list.add(multVal); + considered++; + if (considered >= maxCards) break; + } + } + } + return computeProductWithCap(list, maxCards); + } + + /** + * 从一组 ItemStack(升级槽)直接计算 product with cap(最多 maxCards) + */ + public static long computeProductWithCapFromStacks(Iterable stacks, int maxCards) { + java.util.List list = new java.util.ArrayList<>(); + int considered = 0; + for (var stack : stacks) { + if (considered >= maxCards) break; + if (stack != null && !stack.isEmpty() && stack.getItem() instanceof EntitySpeedCardItem) { + int multVal = EntitySpeedCardItem.readMultiplier(stack); + int count = Math.min(stack.getCount(), maxCards - considered); + for (int i = 0; i < count; i++) { + list.add(multVal); + considered++; + if (considered >= maxCards) break; + } + } + } + return computeProductWithCap(list, maxCards); + } + + /** + * 计算最终消耗:把 product 转换为等效卡数(log2)并调用 getFinalPower + */ + public static double computeFinalPowerForProduct(long product, int energyCardCount) { + if (product <= 1L) return 0.0; + double base = ModConfigs.ENTITY_TICKER_COST.getAsInt(); + + // 计算以2为底的对数(用于分档与公式) + double log2 = Math.log(product) / Math.log(2.0); + + // 分档:product==2 为一档;4..256 为中档;512..1024 为高档 + double raw; + if (product == 2L) { + // 轻量档:线性小幅增长 + raw = base * 4; + } else if (product <= 256L) { + // 中档:增长放缓(使用 1.5 * log2) + raw = base * Math.pow(2.0, 1.5 * log2) * 2; + } else { + // 高档:增长较快(使用 2.5 * log2) + raw = base * Math.pow(2.0, 2.5 * log2); + } + + double reduction = getReductionPercent(energyCardCount); + return raw * (1.0 - reduction) / 8.0; + } + + /* ----------------- legacy helpers (restored) ----------------- */ + public static double getGrowthFactor(int speedCardCount) { + if (speedCardCount <= 0) return 1.0; + if (speedCardCount == 1) return 2.0; + if (speedCardCount <= 6) return Math.pow(2.0, 2.0 * speedCardCount); + return Math.pow(2.0, 3.0 * speedCardCount); + } + + public static double getRawPower(int speedCardCount) { + double base = ModConfigs.ENTITY_TICKER_COST.getAsInt(); + return base * getGrowthFactor(speedCardCount); + } + + public static double getReductionPercent(int energyCardCount) { + if (energyCardCount <= 0) return 0.0; + if (energyCardCount == 1) return 0.1; + if (energyCardCount >= 8) return 0.5; + return 0.5 * (1.0 - Math.pow(0.7, energyCardCount)); + } + + public static double getFinalPower(int speedCardCount, int energyCardCount) { + double raw = getRawPower(speedCardCount); + double reduction = getReductionPercent(energyCardCount); + return raw * (1.0 - reduction) / 4.0; + } + + /** + * 返回能源卡减免后剩余的功耗比率(例如 1 张能源卡 -> 0.9) + * @param energyCardCount 能源卡数量 + * @return 剩余功耗比率 + */ + public static double getRemainingRatio(int energyCardCount) { + return 1.0 - getReductionPercent(energyCardCount); + } + + /** + * 将剩余功耗比率格式化为百分比字符串(例如 0.9 -> "90%") + */ + public static String formatPercentage(double ratio) { + double pct = ratio * 100.0; + // 如果为整数则无小数 + if (Math.abs(pct - Math.round(pct)) < 1e-9) { + return String.format("%d%%", Math.round(pct)); + } + return String.format("%.2f%%", pct); + } +} diff --git a/src/main/resources/assets/ae2/screens/entity_speed_ticker.json b/src/main/resources/assets/ae2/screens/entity_speed_ticker.json new file mode 100644 index 0000000..e30da1a --- /dev/null +++ b/src/main/resources/assets/ae2/screens/entity_speed_ticker.json @@ -0,0 +1,78 @@ +{ + "$schema": "schema.json", + "includes": [ + "common/common.json", + "common/player_inventory.json" + ], + "background": { + "texture": "guis/blank_background.png", + "srcRect": [ + 0, + 0, + 176, + 205 + ] + }, + "text": { + "dialog_title": { + "text": { + "translate": "item.extendedae_plus.entity_speed_ticker" + }, + "position": { + "left": 8, + "top": 6 + } + }, + "enable": { + "position": { + "top": 20, + "left": 88 + }, + "align": "CENTER" + }, + "speed": { + "position": { + "top": 35, + "left": 88 + }, + "align": "CENTER" + }, + "energy": { + "position": { + "top": 50, + "left": 88 + }, + "align": "CENTER" + }, + "power_ratio": { + "position": { + "top": 65, + "left": 88 + }, + "align": "CENTER" + } + , + "multiplier": { + "position": { + "top": 80, + "left": 88 + }, + "align": "CENTER" + } + }, + "widgets": { + "toolbox": { + "right": -2, + "bottom": 50, + "width": 68, + "height": 68 + } + }, + "slots": { + "TOOLBOX": { + "bottom": 42, + "right": -10, + "grid": "BREAK_AFTER_3COLS" + } + } +} \ No newline at end of file diff --git a/src/main/resources/assets/ae2/textures/guis/blank_background.png b/src/main/resources/assets/ae2/textures/guis/blank_background.png new file mode 100644 index 0000000000000000000000000000000000000000..daf4474cd4be274c4c9b5cdc74492b30c075de09 GIT binary patch literal 1776 zcmeAS@N?(olHy`uVBq!ia0y~yU<5K5893O0R7}x|G$5xr)7d$|)7e>}peR2rGbfdS zL1SX=L|c!;4l+mMgO@5zRVouGJP}}h)74eRTeLf2;TF-TQiGjexM!KDXzKMfWgk4) ze{@xI_vUqc>zWjPFh0_=(A?$G`QMvMH_680-SOSk{kzK>F03-V`lg$CRhs0?6sI%d zVMh;W|KUiKaCA{kd$saS65qVXujTCa-8)(_?fXvq;}e2RBp(QOKK3!!dBpqZrmy$& zJ^nIjqMnL&>hnxqG#%-WnI2pBIHoM(!OTSE=`+>&gu9>0O{<;}$}_d1&0^6Csi)kP z(?!ok&$$}aT>9>0yo64ccI3LtGVVg&9*VqzMp7#lv2hzlnFjf!WY|A^C|~?1v*_fL zRZSC<8B148kg@+E|L5H`9bf;Qo(EYj{7t7Z3 zjNdAIZ(db$cgT?5vEyCVC%J;1%sqB5Cn;J^n!hYSi}8K>7hl$dCt?yY#nrFhJpRIX zA~5{4`GJFq0=mK9b{*f%URC}3yU+6d@>3=iZSk4S9s`UOwj^(N7a$D;Kb?2i11Zh| zkH}&M20djEW~^9hUj`IpFY)wsWq--cFUYIQe=<=RC?r|p8d2h$pPQSSSHj?2l$uzQ znxasiS(2gP?&%v4-pD7;z`%0S)5S5Q;?~={u|cyFL>eA8+pjJXWs>GhkuQ`N5P#TO z=zq>bU*^_DzeOj%3wGF@U;DUo*+boh54UvGw>6v&sNni!H}SB6#lM{s#B&2X9Mr=3 z>h#Wj{=C`vT2DHIJky5TZ*8=7Zl!JR?C4ncdJbob1#`iNW(Ec(1`Y-Vh6XAzsAm$~ zMN~Gjhf$+TNlB?K@k7pripQ_7yuK=l;s2VF&F{Gn8@Sxw=6Se#QO72oCEXI`rAS-o1P_}KgZnZ*Z8 z85}@`01JZvGNVD5+29__=by@JciKJ5>a(L z?`5H(s`{FdkLK)Jm%KMlKk1wSPxq|DTe`m6ul~;6xclzA!{XtquipRtGnjAEy4U+` z)93?xtugEhd%D$)W~CxA18n6PRYETRebJe=j~P?xkq${G3$f3Z*?oH^DdjX=RUj} z{=3lcmDs(Q;-XyEdD-XoJ+I7+?gBD@>N>po&uvgwzs_=-h`)9F{iCi9DDjOFG7a2j z_xk4U>ejz5!@@A*#KGH*%kLc8eSYEXNc~Fg_lm)}4}E7|-j@4m?a`gj3}mAl#SPjY z-{7iQww+chj{x|;FEI8-<54CU4e*OAo zTFPuNr<=Q>l375oFj(a1YGVey4_mo`WfzIyk54^AFuxpE$4yr$kXBDuKbLh*2~7a1 ClaMn2 literal 0 HcmV?d00001 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 2fa9a07..1699f08 100644 --- a/src/main/resources/assets/extendedae_plus/lang/zh_cn.json +++ b/src/main/resources/assets/extendedae_plus/lang/zh_cn.json @@ -28,6 +28,25 @@ "item.extendedae_plus.64x_crafting_accelerator": "64x并行处理单元", "item.extendedae_plus.256x_crafting_accelerator": "256x并行处理单元", "item.extendedae_plus.1024x_crafting_accelerator": "1024x并行处理单元", + + "item.extendedae_plus.entity_speed_ticker": "实体加速器", + "screen.extendedae_plus.entity_speed_ticker.enable": "§c§l机器已被禁用", + "screen.extendedae_plus.entity_speed_ticker.energy": "能耗: %s FE/t", + "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", + + "item.extendedae_plus.entity_speed_ticker.tip.requirement": "需要放入实体加速卡以启用加速", + "item.extendedae_plus.entity_speed_ticker.tip.max": "最高可达 1024x 加速", + "item.extendedae_plus.entity_speed_ticker.tip.energy": "加速将消耗 AE 网络能量,网络能量不足时无法加速", + + "item.extendedae_plus.entity_speed_card": "实体加速卡", + "item.extendedae_plus.entity_speed_card.x2": "实体加速卡 (x2)", + "item.extendedae_plus.entity_speed_card.x4": "实体加速卡 (x4)", + "item.extendedae_plus.entity_speed_card.x8": "实体加速卡 (x8)", + "item.extendedae_plus.entity_speed_card.x16": "实体加速卡 (x16)", + "tooltip.extendedae_plus.entity_speed_card.multiplier": "乘数: %s", + "tooltip.extendedae_plus.entity_speed_card.max": "最大生效: %s 倍", "item.extendedae_plus.infinity_biginteger_cell": "§4吞§c噬§6万§e籁§a的§b寂§d静", "tooltip.extendedae_plus.infinity_biginteger_cell.summon1": "§6九重献祭, 终得虚空回响§r——觐见§8虚空之主Iava§r, 赐汝此物", "tooltip.extendedae_plus.infinity_biginteger_cell.summon2": "§b——§4方§d寸§c之§e间§a, §6自§b有§5千§9寰", @@ -62,6 +81,12 @@ "extendedae_plus.configuration.wirelessMaxRange_with_range": "无线最大距离 (1-4096)", "extendedae_plus.configuration.wirelessCrossDimEnable": "无线收发器允许跨维度连接", + "extendedae_plus.configuration.entitySpeedTicker": "实体加速器", + "extendedae_plus.configuration.entityTickerCost": "实体加速器能量消耗基础值", + "extendedae_plus.configuration.entityTickerBlackList": "实体加速器黑名单", + "extendedae_plus.configuration.entityTickerMultipliers": "实体加速器额外消耗倍率", + "extendedae_pluscraftingPauseThreshold": "AE合成计算暂停检查阈值", + "extendedae_plus.configuration.state_on": "开", "extendedae_plus.configuration.state_off": "关", "block.extendedae_plus.network_pattern_controller": "样板供应器状态控制器", diff --git a/src/main/resources/assets/extendedae_plus/models/item/entity_speed_card.json b/src/main/resources/assets/extendedae_plus/models/item/entity_speed_card.json new file mode 100644 index 0000000..b33694f --- /dev/null +++ b/src/main/resources/assets/extendedae_plus/models/item/entity_speed_card.json @@ -0,0 +1,14 @@ +{ + "parent": "item/generated", + "overrides": [ + { "predicate": { "extendedae_plus:mult": 2.0 }, "model": "extendedae_plus:item/entity_speed_card_x2" }, + { "predicate": { "extendedae_plus:mult": 4.0 }, "model": "extendedae_plus:item/entity_speed_card_x4" }, + { "predicate": { "extendedae_plus:mult": 8.0 }, "model": "extendedae_plus:item/entity_speed_card_x8" }, + { "predicate": { "extendedae_plus:mult": 16.0 }, "model": "extendedae_plus:item/entity_speed_card_x16" } + ], + "textures": { + "layer0": "extendedae_plus:item/entity_speed_card_x2" + } +} + + diff --git a/src/main/resources/assets/extendedae_plus/models/item/entity_speed_card_x16.json b/src/main/resources/assets/extendedae_plus/models/item/entity_speed_card_x16.json new file mode 100644 index 0000000..b1cd950 --- /dev/null +++ b/src/main/resources/assets/extendedae_plus/models/item/entity_speed_card_x16.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { "layer0": "extendedae_plus:item/entity_speed_card_x16" } +} + + diff --git a/src/main/resources/assets/extendedae_plus/models/item/entity_speed_card_x2.json b/src/main/resources/assets/extendedae_plus/models/item/entity_speed_card_x2.json new file mode 100644 index 0000000..1107187 --- /dev/null +++ b/src/main/resources/assets/extendedae_plus/models/item/entity_speed_card_x2.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { "layer0": "extendedae_plus:item/entity_speed_card_x2" } +} + + diff --git a/src/main/resources/assets/extendedae_plus/models/item/entity_speed_card_x4.json b/src/main/resources/assets/extendedae_plus/models/item/entity_speed_card_x4.json new file mode 100644 index 0000000..dc0dfcf --- /dev/null +++ b/src/main/resources/assets/extendedae_plus/models/item/entity_speed_card_x4.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { "layer0": "extendedae_plus:item/entity_speed_card_x4" } +} + + diff --git a/src/main/resources/assets/extendedae_plus/models/item/entity_speed_card_x8.json b/src/main/resources/assets/extendedae_plus/models/item/entity_speed_card_x8.json new file mode 100644 index 0000000..0b75b61 --- /dev/null +++ b/src/main/resources/assets/extendedae_plus/models/item/entity_speed_card_x8.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { "layer0": "extendedae_plus:item/entity_speed_card_x8" } +} + + diff --git a/src/main/resources/assets/extendedae_plus/models/item/entity_speed_ticker.json b/src/main/resources/assets/extendedae_plus/models/item/entity_speed_ticker.json new file mode 100644 index 0000000..51789c2 --- /dev/null +++ b/src/main/resources/assets/extendedae_plus/models/item/entity_speed_ticker.json @@ -0,0 +1,7 @@ +{ + "parent": "ae2:item/cable_interface", + "textures": { + "front": "extendedae_plus:part/entity_speed_ticker_font", + "back": "extendedae_plus:part/entity_speed_ticker_back" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/extendedae_plus/models/part/entity_speed_ticker_has_channel.json b/src/main/resources/assets/extendedae_plus/models/part/entity_speed_ticker_has_channel.json new file mode 100644 index 0000000..22f76cb --- /dev/null +++ b/src/main/resources/assets/extendedae_plus/models/part/entity_speed_ticker_has_channel.json @@ -0,0 +1,3 @@ +{ + "parent": "ae2:part/interface_has_channel" +} diff --git a/src/main/resources/assets/extendedae_plus/models/part/entity_speed_ticker_off.json b/src/main/resources/assets/extendedae_plus/models/part/entity_speed_ticker_off.json new file mode 100644 index 0000000..a31e532 --- /dev/null +++ b/src/main/resources/assets/extendedae_plus/models/part/entity_speed_ticker_off.json @@ -0,0 +1,3 @@ +{ + "parent": "ae2:part/interface_off" +} diff --git a/src/main/resources/assets/extendedae_plus/models/part/entity_speed_ticker_on.json b/src/main/resources/assets/extendedae_plus/models/part/entity_speed_ticker_on.json new file mode 100644 index 0000000..9cfa323 --- /dev/null +++ b/src/main/resources/assets/extendedae_plus/models/part/entity_speed_ticker_on.json @@ -0,0 +1,3 @@ +{ + "parent": "ae2:part/interface_on" +} diff --git a/src/main/resources/assets/extendedae_plus/models/part/entity_speed_ticker_part.json b/src/main/resources/assets/extendedae_plus/models/part/entity_speed_ticker_part.json new file mode 100644 index 0000000..df7a2d4 --- /dev/null +++ b/src/main/resources/assets/extendedae_plus/models/part/entity_speed_ticker_part.json @@ -0,0 +1,9 @@ +{ + "parent": "ae2:part/interface_base", + "textures": { + "front": "extendedae_plus:part/entity_speed_ticker_font", + "sides": "extendedae_plus:part/entity_speed_ticker_sides", + "back": "extendedae_plus:part/entity_speed_ticker_back", + "particle": "extendedae_plus:part/entity_speed_ticker_back" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/extendedae_plus/textures/item/entity_speed_card_x16.png b/src/main/resources/assets/extendedae_plus/textures/item/entity_speed_card_x16.png new file mode 100644 index 0000000000000000000000000000000000000000..adfcc54a61cacbeaf5785fe622850a3a8549da53 GIT binary patch literal 2485 zcmb_e3vd%v6kQ9{R>0PlVGx+QE~u?`vv2dy+K8d)Pnr@iS{UW?vip(-l5EIso3>Pb z1*d>8phcljlmN9TgG{Roey9Z%{7|L92!6)a(kW#`aXO_3hKOcm>Fz!u0x zC7%4sAbPm6$iY|6?=k-fmxtWUaa`;*qW+*ZOk>{EzixQBW zX0wyoVi1ug6-)B~s7ZrKWiUDodW|uYR0Fk+0cwV2Y1YKCfCEM{`k`>Rpy1-BT63ep z@ST||k>vo#FriRL71F50pqpV$CX>RUR?`?kOBH?@meYP|+yH|WNqo=~kUgTGR2ZRC zES1d^mfD|#FA$^kOVK*viZSIdz_2Qy~vBxinAt~dE3CId%WTup^(ZEErtb;Xhtd`U0?*m2- zfPSb=6g;kqtDss3P;)@b>A+v0*fRo@;SIq8&$+~)58{M9KIleF!0)EWSQ)u&(JKaV zV609PQDL)j1%63}ejXKA%@odAIYT#s% z9u3U5ihQZkS!I5|%LS3g9Dha?Sim~avPir(zNDENwq)iBS7U~Q-fDq7XG_HZvuab_<$YLjQKsR8<{~|qF z7QO`f-3afT4E5iW`FFnTSNZp3u3B0DUowocU-mnUx!Qb@XIJd4xRH?|ykRSsu~!AY ziM?!)AJ-{}UnPA7WfcSw_g;b3;>cg#R{O~$$CMF=CQR-a({cHrp&^`pCO7@HWjQHN zc7|(e1}~hQNZ);A*`1cfo#8O}=t=L^l`k}eM_N*Ltoe0_=f2+;J*pZub>7(e4I%ee zznp(9Ft)zE{iw|?rCr*5Y2WEh^FPUJ%3Qg1{jeilj}`84B)xGgZYd->FZ@I_?tE$D zG3}hRj{_>eXYwPCa8s_eeb0ey<~yDS!)j|jY&~>( zLmwe0RedIzOIJ-AH@W6?&(Yp5v&dt8$DLtumVd!LvxGWg(S{eLHnF|8wyffnp2EH} zCl16VeJ*Y@w2KUS{rA*+YO5Bck6yN-K4asG9gU+_&~p-lO$?&1=3uvS+DXXM6U)+ugmewc4y%JcI)Rz800?%l24-JZ7#cZW49 zJ`ggYKr_-LR7C$!Fnk5&C|c;0_-K3-lBTAnS^0=&4DGi(E;BW!o7>yp?tXrs&*%Ht z?_7RrN^;N00g(uT^t6~wY498k4>Y1H{Cy$+kFVgVo5ws|M3C;iqo;%E^ zXGj^=Ni-w46o3^nIfdWlfoKFtNc4LEGnbRlOwP``jo2@>`!JMejo37em9%=2I0tXe z@p4mgQqq~6xr~9u5=Ws4ei{MBm(wlMek0b#OT)E%o50XEh&0!TjgbY>3~MTy zBzQU0prCMuB$cSnpwQ_vby}7FWmHKjH3X?7C<>kZd7IjnL?Ii z#Guvo7+jtpty}EK6U-Rl2Ofe_kaA3IK$Z#OJXv06n>fo5oRf2LZb^h#Dv0$s1W6Db z!V{>2kN+V6b8EE*H6H85 zp*vNZ3#*m3xJ3!L8O~xdVlZX}&$F}=FeC*?Ev`~!0-OcvOx(aJRXEV-G$hB7YL&{? zx!)u(S#o9N{q0@O3Jm1d?-P0r1|J{uL$9hLv7zc3MIXF29Ob&*SM@)NBh)3(y_Uxf?)Cq?( zPF@D5E4&EKXU+|&y>R+AJxCsfAfa^@)0lK~{?{dq6Q2kDzD(Sps{Q0BJ$FPw`OI8p ze8vzE5u1DU*#1HN#^&`=M__T$Ju0r2)mXBRzutdx9-5zCc|W(Nc){plQC+v}*>;a< zK6iFjN?z9KsXoo!6JlU?^Oe@mzrXT9ORIU8ODru4=~0hVg)E7lAGw4NjgIJoIGl6F zo>Z4651KG@uXZqU=5X_uC9ci9dFb)tZbvT0ymp{GA-2AMH&wiUadzRsdzWV+YJ0De z9ifgmf0{PuNX3B(<*&zFJ|1^-P5#hHw!ZsL=G={Jjlb*K(QCBjRQ865ll{(?zqH5F zx_bTTlTnqwZJ)fedcvVEMtt_-ZqL-yRPSE!TU`?Q%Tc6kRL#=Z-g}tb>c+-3Kh@=_ zs#eyoI3HbJxp?hpm!r4UPTGDpBSrc|{-!g1ciKFD`rv5R6^lsM^TZ{dC|M!Pgd|)p< za%6&f_lLU<&*KXhetIo=<#h8Fb$5Km5N_#`^7o1~KNPISiz8JYbwl*!h@lFCd+mPkXbAD?)vFOI>g!$UmnbR-ujqA?E zAY-=;IF*+;FJ<_}&E=Cn-m}J6nNd)%MPIP1s87-OugBFE9jeolqmgShje9R$eXoA7 zF07thc_?gsfWA9v#t@HkTEr8lx>-&I&S66sP7R2^>{Ael05tJ99ZCeqcl6y+CT} s`Hcw;#w)c+NqM)*jv*}(Yt{EdYu1(2-2DS^@?Q|kxD?a2v9ojk0;E1Ung9R* literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/extendedae_plus/textures/item/entity_speed_card_x2.png.mcmeta b/src/main/resources/assets/extendedae_plus/textures/item/entity_speed_card_x2.png.mcmeta new file mode 100644 index 0000000..c488c5a --- /dev/null +++ b/src/main/resources/assets/extendedae_plus/textures/item/entity_speed_card_x2.png.mcmeta @@ -0,0 +1,15 @@ +{ + "animation": { + "interpolate": true, + "frames": [ + { + "index": 0, + "time": 32 + }, + { + "index": 1, + "time": 16 + } + ] + } +} \ No newline at end of file diff --git a/src/main/resources/assets/extendedae_plus/textures/item/entity_speed_card_x4.png b/src/main/resources/assets/extendedae_plus/textures/item/entity_speed_card_x4.png new file mode 100644 index 0000000000000000000000000000000000000000..ab8775fce0e9022cc82cdf2fc5979b44aa293f23 GIT binary patch literal 2501 zcmb_e4Nwzj8eRnv{G}7XKoyr24?DWqzifWCNf8Mpw1xtuMW;IYW%mmNNH%5{61elf zcb2o-dgl*dr$2gV)k@pxOpluzE9#wt1LAoooUP741-(19GGf*0%-wwnA!j}NGo8z1 zv)_LEeDC|b@B4i3mxiLk{Hc@DCSe#hwZLvGLH9&-;qh_k_sNEPUFeqJvoBRJY_cYF zjl-JX(qdTLPIu`Nb%|rKAj%#ckYp#+1wB55#xP5E&0D0)c=o!0BXvImMXG=8z4RB@u#D>bxoll3r!bm<1bDM8Dgo zx@9jOvIv}VjcO$j)zKI{zNlHR63G+F7!?FQiqX-bn8H9wjN*JX{_3!ENu*#k^gyqw zAS@Hb`YL2qRx0H0FdcpUEe9yK4oB3-W4U-dQ5TAuSBt`kB;>JZrL@imsS>EjHGUE1 z)gqd6LecnyTt5V=>@St&>aj`{jZwx~gO11NmASo=98jKmXaTeV6K=jWOKDc1Ie}xp1x1>XfC_#PEQx|k_Im&d*zE!3kn(xU z2|SucAy=-J{m3z5$Awuq972ItQGr*41vV>zV%E9cl3$fGZS5Jlf{})Wkc&nRW8XQvhiQ9tbt)gNv|hq6Awreujfd>8V#f<0we8| zoMw(Ujj?&b??xL9R6lGr#7aUIMH66v$-t8wVnEWgo+p8xH;{m5(2k*jo-?~bY7PrT zu)7shy1K~zcoD9OjH=yuSQ7#eLt3{IVyMVaA|li7$K>Njd5)|PR6u0(dx}5mU6EaC z0Qg}}Im+D+ii`R#@(QT^e(Ed`X$H^+lH;6!lz`qznjwoSz#DlQLR!ypuCdS`p^n7J z2t4XG{FoSj3iU`@#0uaohp2N>#7__A$LVsE<^Klr$jos?y6$V@@4o8F4>AwJ;#ph1+?qOX-?71qQ*KPW+w;zrZ2Ta$eJHm1G<|v- z<~Wv|79*TFNqyw_ch03-1mC&&cKS10tEyr&1BvK6a$vZ5UEj49Ch6Ubw&Qo!b?-Hn zX|Gu}U0HGbFjd?_ab{!g;uXJJasKaRd{)f1Y(`pf5fd-t?mnyEcP4JLHV7o1&->@7{6Ys6E}Wo9g_J52|pH>sNwfg$d zmEcDs>hxOQ-7N)JAp zzj*|=UeB0&`0BC2bLZB!Z`_dDo;JP1-(U9O{4_F;o!2z6d%<9e&eOiJx3{;(v7l*U Z_PB+^yK^ct8p40H3i1kVt&3J}_z(4|b&>!8 literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/extendedae_plus/textures/item/entity_speed_card_x4.png.mcmeta b/src/main/resources/assets/extendedae_plus/textures/item/entity_speed_card_x4.png.mcmeta new file mode 100644 index 0000000..c488c5a --- /dev/null +++ b/src/main/resources/assets/extendedae_plus/textures/item/entity_speed_card_x4.png.mcmeta @@ -0,0 +1,15 @@ +{ + "animation": { + "interpolate": true, + "frames": [ + { + "index": 0, + "time": 32 + }, + { + "index": 1, + "time": 16 + } + ] + } +} \ No newline at end of file diff --git a/src/main/resources/assets/extendedae_plus/textures/item/entity_speed_card_x8.png b/src/main/resources/assets/extendedae_plus/textures/item/entity_speed_card_x8.png new file mode 100644 index 0000000000000000000000000000000000000000..dbfdae5b48127a7c29b6c395ce4b413f8542b4f9 GIT binary patch literal 2476 zcmb_e4Nw&48D1cOe-V?=gc^hE+9aax?ce=wJ&|xIhaNeRiCR<3ce~%g3U|BgF5Der zLJu62mikjFsH75U@D!I!w3>*JY z5h_Db#TfESCIs;%d*E1TKko zVyVeRyOeBLBIQ=8a7k5up-@#S*hC_8E~`87UhC!)MO8b@wZ1RjOx zrFJ4K5)fbJa^u;u3UQl(AqARdaf{7hv3e|K&N>@sY1Ty1EX6P+W8)c`r>*$Nk3iv6 z(aRS&=8ptJS9YRA*AVzsr1%W~_20D^c6etQYoKm5dMT3h11Fd!n_%kOHX!sL2(o0COsl z%;}MA6h2#pK$q1*SuPvxlzUV%&YBG-e8y7AFUkQem zXr(!u2Z*Ed9kNgn>1<^Fu**eRKpg*z>M@zkP-HEnMX<6Y%Nb3i6`BEQGICbnwK66K zf>E}oRS7LLPpcO*{BWLqQ+#EFspDSso zb)k{_WnjLIZD>IohQ5wF_`A<|@s@)^D@-J>4gEAq(vKnX;o-_rcyFq-Vu13-mY z#i(}QX)fxU#A~4P+oiKWpcz1$NsjXXQUpd1>L<&gqFGEdL_Ov>?`Z0GNk?X6coW)f zOrvJ}A<{?6B9s7sF+@8jMf~t&zMn6LRen90yH?i!FBvMbUk*Ery4!rwXLszasFBfI zXv2;SW3LKy5qsG{KdO_8UM1nY=esa0{(#ewRhYZ}MAM0dspV;Z&FtVV@9Fr{&i1rR z%<+knooV5$2NEBvKKvlRrzklqKEWAo&))2;JYDO~6DRF=%shBIZd2v;lo?}El6G}n z>Fyai_re;D-?R71rI~x1e}6oIJ5~LBUmtgBXw&TD{nlv-*DeS4pZ#|3I~mRLSnTtJ7mxP+dP!(jLwch-&3u}8V(rJ1LUEbz^)8k4+yl#R zk3A%HVi_5KXgj}d`5^Nvf@|<^OHHaxt(#y3wEaK7>mEOF@x$Hn#tKaRbK>s07kAe* zA8V}0uj)O}+O=@&>b09+hYf$m3!csnhc^~o z`-|}Qw$(?6_ISAXvvo%Y@z7eekKZ=t+O;A2(dRNxQq$9jBhRi$F0gJ^I$up5bk8~$ zxG%5w)mMl^;?I-KK>6ZU>QM2DeQz!H{IR+#q4!{zn9x+T@j~JwL&ZN^mr!onp!nww z%zvTZdq-UjJ)x=d z*rA?*i&x_Y({FZN9r~BtI_0KowxxOgg8rJ%K3Tl5B|U#p{|(2^F<488x2$=%aia8e zW$AC4Hhejo{PdN7SFU?4pQ}#)399PU`X9IdZ9;o;;v*}z zFK<;qN|HOL=ElyC-aXfHvbOtV>Fvi>H%@O#Oj*_aL8m$2{qZNs)RA9&Xq%j3PFm18 zHevOtwC$(5i@5RWGd^Fr?~60eIZtvatsNa(LzAYe4=%ZJy{O82A@lsd`cI`bJ`JQs-@6+cU;X;T#}RZ2sJ%4P#rIy*CaUVOKE z?5^!|{-70vD5(mmK$TjQK&z5~+%zZ!QN^K{LPhw92r8;XAtezYA#kHgZADO|Hq73a zFRdM40Ev_CZg=Omzj^P?@4Yv(e{kTQn^xbtI+MxV)ZdpM693)l_xkI^??>OdOvK*} zmA)NuCUfJ>>9;HMje~0i_%(lcEEy|&+M!`NOIYagY_(hw=uBqwUDXPq6FiYzUi5>U z{Km<7S@Ky<9@Pu5Q0e94e&6nhZ{IyIOm|OEo5^?eNSmvUASm;MNY!#Fh@EOquJJnJ zI(@9jQVo(!NPOMFrWmopPuu5C{;@1PQ5vAYR{O$n%&+ zekJk4KuQ^j8%`!US(sY)p0?MoYGSGCS1+6%}5hqC)4ToW=DW1WmZBlQq zv|-c_SU4STOxNG(1K7VBME7NbpUOE&@i$b zqzlb$g4QB9WWG1EB9u|b3!^d-%jTC!kt>y;Seva-aQcHdApzz6`J60F&H6rbxJQu7 zT>^AZ)d0qt4v2;kP#HB1o!S zfkm(dFw?M&7{b_fn{4++zQ}M=YG+NYVuI0y9>*>;0Co*egik}jRf!FVt1@VKo+S*G zrNw!B!%`R(LMCF4Whq^2N6-sXNt#_b88se`r9v%D)*qTG6dwhpzm4%7D~vA=j|9F5 zu_{ZcTAbSa+ePIP1{k`I^~Uo-zsgHvd0#{=t{Y;skLs5IO8 z=NnfgU#g{~LulOvnrf3Xx$)RvVu3pgWu10!u7x3YP1QTJz&ZxxAYcTZOY{>0S$U zgmlD3kFN65EF14Q($0?14~qE zQT8kfP1~T1v5vV6gHRP?0I^2uyAzjE1UR%1)0n0~LObO$AhnpNw_>;NVqn^W&=n>E zOEWxTiTwuQuv0Em96X5FH~>U!L8xgKa1k;9u}obzHA7Sqty3-wQtTns1)i!>fJ{vl z@lv^<#?*kiW?|cXKi9SFl-k~|>7gzzH1uMe#@}+mBU|01DjCy-uVxI`Oi;H9@_rs-rOgL?mV$A%A8;Gu~omg zjhd6$?hOy!`Ry~G8kA->{B&*jr%m5{sqdM`EI~uICb{;>?bxJe*H!M+|9}T_nfL5MruSUW^RN8yLgxIl&J90d_ZRUi zUBQ@tWZ(HWf3=Vted3*+f8qM&lRtR*z|h{g-yAq|v1|YBm*(Dn`lVU?!r4RLgFo-H zZ#g!SAG>2s=0~~B#hKaJFa3S>I`-tKhTTW@bietB`CT7+`Ma~TM~@$ym^=2f(~lor zcyYtLK<;0+=j?0aCw8trxQo8}&xLn>x9iB z-MzcBrv`!#d}O0WO$fmcVq&7nDTD}#mmn9#XmUyj@#5i&sGl^*2TlA$;NSD;orB%m zIW=*Tndz?j)$jkP|D)=QbH_fmclV9ECnhHL&K{}F!~c7V?^W-H-<$sVCWpVPJ4cRZ z6BF0GxA;y@Jofkv0RBVVSj-peM?;ykDqJN(SGuhZpeH5{-QMkRc|zxg&`WWvP6Tl4>KU5F|0Ch8I*kUwE$V-(pZq9Sc*75JZBI;1K)9;A4Wg zR7K%@?T&#kc&nPrdEN;vYi(_WEdrBi^I0XExx3!PJ5{*tzHjj+gZ-rQqR_^Ca7A8V-+%w>xgKD0wO%5 zFpH4yV}X2(8TYm3k(9&rwM4EIAyusJrXq=8x|%zd3zRI|dh1(*3a@OLUsLxw{K57X`P6(6`%F7x)1hW=xj|G6oF#!N~Z zBdL6Wlwbi0XuyziNkk&4l`YCW95p3C)NNopbf5>>mt_hi6*^j(`u2}wj7-|AIvokB z4o^AsP@QH~GfI<|!8@I1EPI({t+v&mGnL6`Cu9prbwHa2`k~J}&IyR`K@#*rXke8LzCrE$g>yKVu~ibt}$}5oZT6QYkeym+QiT z!nP5%8As6c9oQL3kj6eyOfiPJ9GA<*(pOM#;j}Mo*a(Qs^ z;Bat2ga<&#n2!V@F5)!RqzviyIaK%e^HokxG zuCM*&^$Wkgd;j$F`_A0=#N=O3zVuA%$@5SA{l^Q>->~bn^VoNP`RGGG+W*t6_Qt1P zt^DlfL!bQ0GL2KmEoxzM~&}bHkc`A3t~D^j!@6+ahB8l$F-10@t1mQAV>mARYQ?_K%YCO*4%)k_`Q5E~Md+1Xip@!joZ zcWs|tr$h;+DM=NoMCpej@gdh$LrEnB1rnhqDGf-iQkDK!8k#>)N~r`57EK$e3UBYu zXQ#E}3m|dQ-R;i2`Mr<%z4vAw>fCVm^^G?-CK8G3JK8f{@PA|cz3x-+`=#ki6#kYL z+xx;q;?pbQZ$sjVLn}e}jMvi}^=9w2n4eEl$G3U1lrMsGBGG(jsYux%j|7|ddxf<4 zk7Lh^g6E{gO==cri!FS>Yaa`E_t=IWHa5siN4)b6p}AxMK%PfbDCKj7&?=?HGOh*B z@o-8M$|BKVT3i<^6ne9rLW>`8!AugwFqQ<}OzMWMYqIfGLBf)n!cvM5L`;id3mZc9 zBZ9iXajmXQTU8mnrNx0LDq5-3$jC@?L{9oae~Or<8DmHi0tpn379v_gg>dx@LWYMd z@QRV=7las*+Wt_K7Qxht5BXvZwh&h11QAP>XfZ{SIQFS5=&+hxaVW@@l{+lOb3D%r zQ3$f6CJSN8W^3Z}!shce=`d;?28*h`%_|-Dj23ySi--PDzsjivxb-hXeisL8wh%7K+Go7f=|_Tqep(-U6cWOii?CwFI0-e$eClxf$|w&e$fj zv6d8%0GFKf$Yt&Tz%(E{TgnY0L|COyxw zTn#%4XAY7ynIK0~97Iiqk*c~T<1#ljo6oGz_-rVqRJ^{DUXITIqqY~8MjY&7&XJ+I z8X}5fBa>1cnYzr0Vyo14$qd?kfd?5*b8}H+s~kYIvCCx}Ye=>=7s96yWJ}aU)Rr8q zxvnA0iYUzLZSiw{ko6sixhx9tURwYUrqVdO(jq7=5BA1H%?(usbqT{KMdIJ;eBUDT zv(sNIco1Sq6k@hmW&PbyxxfHTRb{n0-szQit~cXB)WV7(+T5AG%V*+37U!qc?uEd( z(Q2jX=G&@IIZr63q(xc91r~FYJ7RU%n`eXT3uOg%WPn4kuE5%gSm?Xa2o3nUeu(fQ zIgwh(^pFl;*>G7_UCHGpQYEY)*9K;qCZmYkwquyYl&R{@IR0_NB}>7Uif7X8>J6`k zlnu~AKZpL46902oOpX=CWQ>?&*vPRJ6Pc2!K)Ml^xQsipUE9OCu9%ROJy^C>=t1&T zxgwFIG`BKU{IACu<`;%}Fo#q{8c^t=JP6aG8~AyF7K=HLl`|_fTyUzLsg{gF88=BrT~f%D7Z~&Z*S1)JIEQ^Z(nf6(>Y3SFD-lw+`BJT=TCkME?b;4<6v=dLSD{SSn#%#td5dAy6M=fM6p3okLUhDU znITP9;4}c$8p@u*ux@J1ah$rbG>tMzmJyZ}ilFJMurrJxjt$}{j-p`7>c!G9GaRV5 zaN4(Jq?-U}gNevcG?yB1-k==Ui>1TGgTlc95o!XUq8P{~L_^fjRaI9ss3c0gSO#XY zOC%e)lEe_v6$#=cae$VYhE>IoP5a|mS94ODv%1WO3b|5OW?@-!&J_hHR33tRPyATB zaluRN&c+MSC z?%Uh1`So4a-=fWl@rJSY5??&tbc?=w^W+PIU73GQw!1IBe$UkTf#J7adGoC;C;bC_ z?V~SU`|{ooAGpE&-i1|(_22r;wXZ+ddXV@2?CYOQJlz;=d1~!rpTE!Ve&f{W_C|C0 zL^FD+>*$d^-~aRd;}2|E-7vITOzb#QxJ}&o3-5`}XAU=>TG>0eF@L!I_&2`r-d{#n z9h&SrIFUW|*xAV=gKIjPe)r20D_1OiEvK!RnCxEmV9UTwj|hj~IsKc&!JGC>H2*ex z{)3?xe}C?lp)Gg+D6=Mz9>4e9cQ5Wf_G<+BndoPk%6e-L_+A+J2t-?v!`-(4MKjL9%M! zlH?zs%P;xd()WLS=7!}b&X@M4n&c%vJ$m-g3)AOr{r2#)%UU-IuT1}; Date: Sun, 21 Sep 2025 02:00:10 +0800 Subject: [PATCH 09/43] =?UTF-8?q?feat:=20=E5=AE=9E=E4=BD=93=E5=8A=A0?= =?UTF-8?q?=E9=80=9F=E5=99=A8=E4=BD=BF=E7=94=A81.21=E7=89=88=E6=9C=AC?= =?UTF-8?q?=E6=9D=90=E8=B4=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ae2/textures/guis/blank_background.png | Bin 1776 -> 2432 bytes .../part/entity_speed_ticker_back.png | Bin 4001 -> 2236 bytes .../part/entity_speed_ticker_font.png | Bin 3846 -> 1823 bytes .../part/entity_speed_ticker_sides.png | Bin 4023 -> 2234 bytes 4 files changed, 0 insertions(+), 0 deletions(-) diff --git a/src/main/resources/assets/ae2/textures/guis/blank_background.png b/src/main/resources/assets/ae2/textures/guis/blank_background.png index daf4474cd4be274c4c9b5cdc74492b30c075de09..eee919cb8072bca5be8504f69325215c7e4e9a49 100644 GIT binary patch literal 2432 zcmeHJeN5D57%u}I=(y1_?d@?~ba9iJEZHAx zo~G~H{@&;LJ)h~0^0H?V=OxcW5G1j*#8!bIF>o1!#LtC~=as$n@R1;wY)}!za6-Rh z-pVwjAV}P0!BMGI+E+82vcnm`Y-ioca z*a^E_1R8{rRt2nWEpu?KjU3Hm1SNr__Dl2UiJR6`-L05p*p(q{qrN{iSsaLl4@BPtz z9X~!e*8g!@pLFQd+x`3KNMzK^zOW13F)ISd?4)|@=?y1R_nco>7vDd}PsBu|ui#~W zS>t;y%Sh7x$t~}0o+jdk+uG%A0ex%e?d=WL#1f0Dk@!XbJ-Ifg`Am;#(wDJY|3@k< LF0=KntlRbnc^Rh) literal 1776 zcmeAS@N?(olHy`uVBq!ia0y~yU<5K5893O0R7}x|G$5xr)7d$|)7e>}peR2rGbfdS zL1SX=L|c!;4l+mMgO@5zRVouGJP}}h)74eRTeLf2;TF-TQiGjexM!KDXzKMfWgk4) ze{@xI_vUqc>zWjPFh0_=(A?$G`QMvMH_680-SOSk{kzK>F03-V`lg$CRhs0?6sI%d zVMh;W|KUiKaCA{kd$saS65qVXujTCa-8)(_?fXvq;}e2RBp(QOKK3!!dBpqZrmy$& zJ^nIjqMnL&>hnxqG#%-WnI2pBIHoM(!OTSE=`+>&gu9>0O{<;}$}_d1&0^6Csi)kP z(?!ok&$$}aT>9>0yo64ccI3LtGVVg&9*VqzMp7#lv2hzlnFjf!WY|A^C|~?1v*_fL zRZSC<8B148kg@+E|L5H`9bf;Qo(EYj{7t7Z3 zjNdAIZ(db$cgT?5vEyCVC%J;1%sqB5Cn;J^n!hYSi}8K>7hl$dCt?yY#nrFhJpRIX zA~5{4`GJFq0=mK9b{*f%URC}3yU+6d@>3=iZSk4S9s`UOwj^(N7a$D;Kb?2i11Zh| zkH}&M20djEW~^9hUj`IpFY)wsWq--cFUYIQe=<=RC?r|p8d2h$pPQSSSHj?2l$uzQ znxasiS(2gP?&%v4-pD7;z`%0S)5S5Q;?~={u|cyFL>eA8+pjJXWs>GhkuQ`N5P#TO z=zq>bU*^_DzeOj%3wGF@U;DUo*+boh54UvGw>6v&sNni!H}SB6#lM{s#B&2X9Mr=3 z>h#Wj{=C`vT2DHIJky5TZ*8=7Zl!JR?C4ncdJbob1#`iNW(Ec(1`Y-Vh6XAzsAm$~ zMN~Gjhf$+TNlB?K@k7pripQ_7yuK=l;s2VF&F{Gn8@Sxw=6Se#QO72oCEXI`rAS-o1P_}KgZnZ*Z8 z85}@`01JZvGNVD5+29__=by@JciKJ5>a(L z?`5H(s`{FdkLK)Jm%KMlKk1wSPxq|DTe`m6ul~;6xclzA!{XtquipRtGnjAEy4U+` z)93?xtugEhd%D$)W~CxA18n6PRYETRebJe=j~P?xkq${G3$f3Z*?oH^DdjX=RUj} z{=3lcmDs(Q;-XyEdD-XoJ+I7+?gBD@>N>po&uvgwzs_=-h`)9F{iCi9DDjOFG7a2j z_xk4U>ejz5!@@A*#KGH*%kLc8eSYEXNc~Fg_lm)}4}E7|-j@4m?a`gj3}mAl#SPjY z-{7iQww+chj{x|;FEI8-<54CU4e*OAo zTFPuNr<=Q>l375oFj(a1YGVey4_mo`WfzIyk54^AFuxpE$4yr$kXBDuKbLh*2~7a1 ClaMn2 diff --git a/src/main/resources/assets/extendedae_plus/textures/part/entity_speed_ticker_back.png b/src/main/resources/assets/extendedae_plus/textures/part/entity_speed_ticker_back.png index d74b5fe1375c96fc3122ac8ff9bd97268ad20d15..504ab1bf531ce48a029af21dd946646aeb74daaf 100644 GIT binary patch delta 877 zcmZ1|zejL_1ryt}jaF5RljE37CvRcQocxqYelj1Iz+@q2VF6QJ14~^)!w^GrD?<}2 zWAn*L%*xzG23AIvR>r2<1{RYon3XtS{K>t{a+4cbBqpzBmQpb_F-$VDFtE@~O|`Vt zO)@hv)=e}sHPlT?voJ6+Pccb0vox6el37c|(mXXOImz5Y*WAp+T-VIpBvm)bz`{h= z)Yvl7)YLpV)ilv;vN?;2N?NLgQIdses;-5pWty&qrHO&Ag}Eijg~As>Eak-(VIKj zK3l|5;#j@$n@~yqzP(CQ3l_A5guh_7$jN68`zEyGH;ba+x(yx^C)_%w`a_WA{W+&M zstSifyUn6^v52RXb2-kqo>YD;_VML22JiPjuf10;qI=0?>Z(;Y^0r$%5Aa$z#mObW ztJ3`0XUk&}(@rmCs_)oy(2`w%sb<6W(#fU5t4w^4S{_gP+*+TwTFAKP{Tv2&_kIDv zMZ4BHFa7&EQ~%)e_2xDfGunPXZdPXyId1)GSKf^?X~q|$HgEqv;qPnBqaWYxziPXu zf9rjQjxE;~*7tXx7G|iMGOaM@P-R(_Yg#c|Xy*#I{uQf>(u&=3CvCo5zgliGP)$x~ zQN}Wn!(7?kQ-U%WmWE`{X*tEPe%{)Os{gJPpV!`4#Up%qUfFevKHhCwmA@ZOn|(X> zz3%4952AuwIuw)?S4$piJa~Nm`k((^*Y;msIFltlL)_D4hj~x41B3qg@Sfz$-0u$Q zhb-Cp&OpNK;OfuTjB+#2N^WswT$HWiDIjR}-j4CZ@3SfaXLICQ&gR_no}%J;_<)4) zF^h_M?6O>CJq=B6M*??;IF?-AB3{?}zxVZ5t1ma+Ubk$&HcQ3R;Mj%;9gz>^J$G~8 hu$4blfA*XG2Qxnt+w+Q!PHzSv@O1TaS?83{1OQm8PmBNn literal 4001 zcmc&%eQX>@6+cU;X;T#}RZ2sJ%4P#rIy*CaUVOKE z?5^!|{-70vD5(mmK$TjQK&z5~+%zZ!QN^K{LPhw92r8;XAtezYA#kHgZADO|Hq73a zFRdM40Ev_CZg=Omzj^P?@4Yv(e{kTQn^xbtI+MxV)ZdpM693)l_xkI^??>OdOvK*} zmA)NuCUfJ>>9;HMje~0i_%(lcEEy|&+M!`NOIYagY_(hw=uBqwUDXPq6FiYzUi5>U z{Km<7S@Ky<9@Pu5Q0e94e&6nhZ{IyIOm|OEo5^?eNSmvUASm;MNY!#Fh@EOquJJnJ zI(@9jQVo(!NPOMFrWmopPuu5C{;@1PQ5vAYR{O$n%&+ zekJk4KuQ^j8%`!US(sY)p0?MoYGSGCS1+6%}5hqC)4ToW=DW1WmZBlQq zv|-c_SU4STOxNG(1K7VBME7NbpUOE&@i$b zqzlb$g4QB9WWG1EB9u|b3!^d-%jTC!kt>y;Seva-aQcHdApzz6`J60F&H6rbxJQu7 zT>^AZ)d0qt4v2;kP#HB1o!S zfkm(dFw?M&7{b_fn{4++zQ}M=YG+NYVuI0y9>*>;0Co*egik}jRf!FVt1@VKo+S*G zrNw!B!%`R(LMCF4Whq^2N6-sXNt#_b88se`r9v%D)*qTG6dwhpzm4%7D~vA=j|9F5 zu_{ZcTAbSa+ePIP1{k`I^~Uo-zsgHvd0#{=t{Y;skLs5IO8 z=NnfgU#g{~LulOvnrf3Xx$)RvVu3pgWu10!u7x3YP1QTJz&ZxxAYcTZOY{>0S$U zgmlD3kFN65EF14Q($0?14~qE zQT8kfP1~T1v5vV6gHRP?0I^2uyAzjE1UR%1)0n0~LObO$AhnpNw_>;NVqn^W&=n>E zOEWxTiTwuQuv0Em96X5FH~>U!L8xgKa1k;9u}obzHA7Sqty3-wQtTns1)i!>fJ{vl z@lv^<#?*kiW?|cXKi9SFl-k~|>7gzzH1uMe#@}+mBU|01DjCy-uVxI`Oi;H9@_rs-rOgL?mV$A%A8;Gu~omg zjhd6$?hOy!`Ry~G8kA->{B&*jr%m5{sqdM`EI~uICb{;>?bxJe*H!M+|9}T_nfL5MruSUW^RN8yLgxIl&J90d_ZRUi zUBQ@tWZ(HWf3=Vted3*+f8qM&lRtR*z|h{g-yAq|v1|YBm*(Dn`lVU?!r4RLgFo-H zZ#g!SAG>2s=0~~B#hKaJFa3S>I`-tKhTTW@bietB`CT7+`Ma~TM~@$ym^=2f(~lor zcyYtLK<;0+=j?0aCw8trxQo8}&xLn>x9iBxyg+z5|dXmOQ|F$S|lY}nkDO|rJ9=Rnpm2c z=~|i^8|#`FS{S66SXf#ZCZ|k!qYW*_=gH+0-B@F*(^nH_6b_K-bb7q|VaFNH@(q(KIwOn#%WfCxx2(~+vJteQCn5>u4srCn(MY5a#@$SJ}5 zPVOqUvciXRR=a(yb6MG*#k*kZv?hiJNB1#aC~te1l`bZB#_z`N%C+hp7t5EOc)i!< zj#7u~uQ=vS)aUnL-fI#A2H{b>#v_^zS`tNosSaRgDv$+9SVsvIkL*TIN7W_ zU*xU3DXAtH$lJ8Su_WnjIXCl!A2ZKAf4}6~ubk3IaL!Fo`tKKXP0A6DTPd9JRz SycmHY!QkoY=d#Wzp$PzhEZXP* literal 3846 zcmc&%Ym6jC9pCl9z;PD@y$cc(n!cRzK}}axS3kLd?Cu=i!p*VS9l~BBfqM0Bdo$B> z-MzcBrv`!#d}O0WO$fmcVq&7nDTD}#mmn9#XmUyj@#5i&sGl^*2TlA$;NSD;orB%m zIW=*Tndz?j)$jkP|D)=QbH_fmclV9ECnhHL&K{}F!~c7V?^W-H-<$sVCWpVPJ4cRZ z6BF0GxA;y@Jofkv0RBVVSj-peM?;ykDqJN(SGuhZpeH5{-QMkRc|zxg&`WWvP6Tl4>KU5F|0Ch8I*kUwE$V-(pZq9Sc*75JZBI;1K)9;A4Wg zR7K%@?T&#kc&nPrdEN;vYi(_WEdrBi^I0XExx3!PJ5{*tzHjj+gZ-rQqR_^Ca7A8V-+%w>xgKD0wO%5 zFpH4yV}X2(8TYm3k(9&rwM4EIAyusJrXq=8x|%zd3zRI|dh1(*3a@OLUsLxw{K57X`P6(6`%F7x)1hW=xj|G6oF#!N~Z zBdL6Wlwbi0XuyziNkk&4l`YCW95p3C)NNopbf5>>mt_hi6*^j(`u2}wj7-|AIvokB z4o^AsP@QH~GfI<|!8@I1EPI({t+v&mGnL6`Cu9prbwHa2`k~J}&IyR`K@#*rXke8LzCrE$g>yKVu~ibt}$}5oZT6QYkeym+QiT z!nP5%8As6c9oQL3kj6eyOfiPJ9GA<*(pOM#;j}Mo*a(Qs^ z;Bat2ga<&#n2!V@F5)!RqzviyIaK%e^HokxG zuCM*&^$Wkgd;j$F`_A0=#N=O3zVuA%$@5SA{l^Q>->~bn^VoNP`RGGG+W*t6_Qt1P zt^DlfL!bQ0GL2KmEoxzM~&}bHkc`A3t~D^j!t{a+4cbBqpzBmQqPcF-tQ{Of}UtOtehV zO-wW~*G)7@HPbaQH@2`evoK6avoxLjl37b7)x_A+!Z0~m*C^RMQ8(G##7H+W&D2EK zJT=YO$jHzlEh)`(vN?;2N?NLgQIdses;-5pWty&qrHO&Ag}Eijg~ROI;)L6pJ(?15;D; zM2pD~jmBnHrUsKAat2LK<1{1Avj;dO5kd8kGjH-eUfcQ)LUIud42-8dT^vI!dUGe+ zXWw=ZIZ&@|BeC$bQhiHZbC(Y%2TyED|Iw_^yt_(YMD0rV-BrRDdLkqxV|vrVSe{o| zHcyyal5}@HzOdxA!u^|Fza92if4TSmUi?K9-s+9-5`4TR`uxwGcVXyZShvpZ&v}Ns zS6tFRc9s>Dm!CaRzt?8|eiuzw5gvlhRa8{dxQs2g8)0kWklI z%kJE%Ym|MHbylUXs`hu+{rz=8p`xCXDmJjY1iH*x#-aG*<6r62NFf%+Yu}d6Je%gB z>3O+CATq&++aXXSCdMz&i+7G}!p@jQQyx7k@(L7rvFsh=xo@8Ao)&Rmq(wmP zQBJHOb8?jb?W?z>qq7B_1XvC->{z|^y}$ttp5yb(I+ghgcD>&9Z)vU9WQ$kXWn#%b z)l!YcWqt47y(*M(l<4D>d0Y6A!C_izNvwItk=oBcWe(hyy8I33-6xgj$~L=sz2uF( zcP(m@jmdKI;Vst0LT|nL;wH) literal 4023 zcmc&%eQX>@6+ahB8l$F-10@t1mQAV>mARYQ?_K%YCO*4%)k_`Q5E~Md+1Xip@!joZ zcWs|tr$h;+DM=NoMCpej@gdh$LrEnB1rnhqDGf-iQkDK!8k#>)N~r`57EK$e3UBYu zXQ#E}3m|dQ-R;i2`Mr<%z4vAw>fCVm^^G?-CK8G3JK8f{@PA|cz3x-+`=#ki6#kYL z+xx;q;?pbQZ$sjVLn}e}jMvi}^=9w2n4eEl$G3U1lrMsGBGG(jsYux%j|7|ddxf<4 zk7Lh^g6E{gO==cri!FS>Yaa`E_t=IWHa5siN4)b6p}AxMK%PfbDCKj7&?=?HGOh*B z@o-8M$|BKVT3i<^6ne9rLW>`8!AugwFqQ<}OzMWMYqIfGLBf)n!cvM5L`;id3mZc9 zBZ9iXajmXQTU8mnrNx0LDq5-3$jC@?L{9oae~Or<8DmHi0tpn379v_gg>dx@LWYMd z@QRV=7las*+Wt_K7Qxht5BXvZwh&h11QAP>XfZ{SIQFS5=&+hxaVW@@l{+lOb3D%r zQ3$f6CJSN8W^3Z}!shce=`d;?28*h`%_|-Dj23ySi--PDzsjivxb-hXeisL8wh%7K+Go7f=|_Tqep(-U6cWOii?CwFI0-e$eClxf$|w&e$fj zv6d8%0GFKf$Yt&Tz%(E{TgnY0L|COyxw zTn#%4XAY7ynIK0~97Iiqk*c~T<1#ljo6oGz_-rVqRJ^{DUXITIqqY~8MjY&7&XJ+I z8X}5fBa>1cnYzr0Vyo14$qd?kfd?5*b8}H+s~kYIvCCx}Ye=>=7s96yWJ}aU)Rr8q zxvnA0iYUzLZSiw{ko6sixhx9tURwYUrqVdO(jq7=5BA1H%?(usbqT{KMdIJ;eBUDT zv(sNIco1Sq6k@hmW&PbyxxfHTRb{n0-szQit~cXB)WV7(+T5AG%V*+37U!qc?uEd( z(Q2jX=G&@IIZr63q(xc91r~FYJ7RU%n`eXT3uOg%WPn4kuE5%gSm?Xa2o3nUeu(fQ zIgwh(^pFl;*>G7_UCHGpQYEY)*9K;qCZmYkwquyYl&R{@IR0_NB}>7Uif7X8>J6`k zlnu~AKZpL46902oOpX=CWQ>?&*vPRJ6Pc2!K)Ml^xQsipUE9OCu9%ROJy^C>=t1&T zxgwFIG`BKU{IACu<`;%}Fo#q{8c^t=JP6aG8~AyF7K=HLl`|_fTyUzLsg{gF88=BrT~f%D7Z~&Z*S1)JIEQ^Z(nf6(>Y3SFD-lw+`BJT=TCkME?b;4<6v=dLSD{SSn#%#td5dAy6M=fM6p3okLUhDU znITP9;4}c$8p@u*ux@J1ah$rbG>tMzmJyZ}ilFJMurrJxjt$}{j-p`7>c!G9GaRV5 zaN4(Jq?-U}gNevcG?yB1-k==Ui>1TGgTlc95o!XUq8P{~L_^fjRaI9ss3c0gSO#XY zOC%e)lEe_v6$#=cae$VYhE>IoP5a|mS94ODv%1WO3b|5OW?@-!&J_hHR33tRPyATB zaluRN&c+MSC z?%Uh1`So4a-=fWl@rJSY5??&tbc?=w^W+PIU73GQw!1IBe$UkTf#J7adGoC;C;bC_ z?V~SU`|{ooAGpE&-i1|(_22r;wXZ+ddXV@2?CYOQJlz;=d1~!rpTE!Ve&f{W_C|C0 zL^FD+>*$d^-~aRd;}2|E-7vITOzb#QxJ}&o3-5`}XAU=>TG>0eF@L!I_&2`r-d{#n z9h&SrIFUW|*xAV=gKIjPe)r20D_1OiEvK!RnCxEmV9UTwj|hj~IsKc&!JGC>H2*ex z{)3?xe}C?lp)Gg+D6=Mz9>4e9cQ5Wf_G<+BndoPk%6e-L_+A+J2t-?v!`-(4MKjL9%M! zlH?zs%P;xd()WLS=7!}b&X@M4n&c%vJ$m-g3)AOr{r2#)%UU-IuT1}; Date: Sun, 21 Sep 2025 13:44:32 +0800 Subject: [PATCH 10/43] =?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=E3=80=81=E5=AE=9E=E4=BD=93?= =?UTF-8?q?=E5=8A=A0=E9=80=9F=E5=8D=A1=E9=85=8D=E6=96=B9=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../upgrades/EntitySpeedCardItem.java | 34 +++++++++--------- .../extendedae_plus/init/ModCreativeTabs.java | 14 ++++---- .../com/extendedae_plus/init/ModItems.java | 17 +-------- .../recipe/entity_speed_card_x16.json | 22 +++++++----- .../recipe/entity_speed_card_x2.json | 11 +++--- .../recipe/entity_speed_card_x4.json | 36 +++++++++---------- .../recipe/entity_speed_card_x8.json | 24 +++++++------ .../recipe/entity_speed_ticker.json | 17 +++++---- .../recipe/infinity_biginteger_cell.json | 18 ---------- 9 files changed, 85 insertions(+), 108 deletions(-) delete mode 100644 src/main/resources/data/extendedae_plus/recipe/infinity_biginteger_cell.json diff --git a/src/main/java/com/extendedae_plus/ae/definitions/upgrades/EntitySpeedCardItem.java b/src/main/java/com/extendedae_plus/ae/definitions/upgrades/EntitySpeedCardItem.java index fe253ae..190aabd 100644 --- a/src/main/java/com/extendedae_plus/ae/definitions/upgrades/EntitySpeedCardItem.java +++ b/src/main/java/com/extendedae_plus/ae/definitions/upgrades/EntitySpeedCardItem.java @@ -25,30 +25,30 @@ public class EntitySpeedCardItem extends UpgradeCardItem { super(props); } - public static ItemStack withMultiplier(int multiplier) { + public static ItemStack withMultiplier(byte multiplier) { ItemStack stack = new ItemStack(ModItems.ENTITY_SPEED_CARD.get()); CompoundTag tag = new CompoundTag(); - tag.putInt(NBT_MULTIPLIER, multiplier); + tag.putByte(NBT_MULTIPLIER, multiplier); stack.set(DataComponents.CUSTOM_DATA, CustomData.of(tag)); return stack; } - public static int readMultiplier(ItemStack stack) { - if (stack == null || stack.isEmpty()) return 1; + public static byte readMultiplier(ItemStack stack) { + if (stack == null || stack.isEmpty()) return (byte) 1; CustomData customData = stack.get(DataComponents.CUSTOM_DATA); - if (customData == null || !customData.copyTag().contains(NBT_MULTIPLIER)) return 1; - return customData.copyTag().getInt(NBT_MULTIPLIER); + if (customData == null || !customData.copyTag().contains(NBT_MULTIPLIER)) return (byte)1; + return customData.copyTag().getByte(NBT_MULTIPLIER); } @Override public @NotNull Component getName(@NotNull ItemStack stack) { - int mult = readMultiplier(stack); + byte mult = readMultiplier(stack); String key; switch (mult) { - case 2 -> key = "item." + com.extendedae_plus.ExtendedAEPlus.MODID + ".entity_speed_card.x2"; - case 4 -> key = "item." + com.extendedae_plus.ExtendedAEPlus.MODID + ".entity_speed_card.x4"; - case 8 -> key = "item." + com.extendedae_plus.ExtendedAEPlus.MODID + ".entity_speed_card.x8"; - case 16 -> key = "item." + com.extendedae_plus.ExtendedAEPlus.MODID + ".entity_speed_card.x16"; + case (byte)2 -> key = "item." + com.extendedae_plus.ExtendedAEPlus.MODID + ".entity_speed_card.x2"; + case (byte)4 -> key = "item." + com.extendedae_plus.ExtendedAEPlus.MODID + ".entity_speed_card.x4"; + case (byte)8 -> key = "item." + com.extendedae_plus.ExtendedAEPlus.MODID + ".entity_speed_card.x8"; + case (byte)16 -> key = "item." + com.extendedae_plus.ExtendedAEPlus.MODID + ".entity_speed_card.x16"; default -> key = "item." + com.extendedae_plus.ExtendedAEPlus.MODID + ".entity_speed_card.x1"; } return Component.translatable(key); @@ -56,13 +56,13 @@ public class EntitySpeedCardItem extends UpgradeCardItem { public List getTooltipLines(ItemStack stack) { - int mult = readMultiplier(stack); - long cap = 1L; + byte mult = readMultiplier(stack); + int cap = 1; switch (mult) { - case 16 -> cap = 1024L; - case 8 -> cap = 256L; - case 4 -> cap = 64L; - case 2 -> cap = 8L; + case (byte)16 -> cap = 1024; + case (byte)8 -> cap = 256; + case (byte)4 -> cap = 64; + case (byte)2 -> cap = 8; } MutableComponent line1 = Component.translatable("tooltip." + com.extendedae_plus.ExtendedAEPlus.MODID + ".entity_speed_card.multiplier", "x" + mult); MutableComponent line2 = Component.translatable("tooltip." + com.extendedae_plus.ExtendedAEPlus.MODID + ".entity_speed_card.max", cap); diff --git a/src/main/java/com/extendedae_plus/init/ModCreativeTabs.java b/src/main/java/com/extendedae_plus/init/ModCreativeTabs.java index f6c2a3c..10d9144 100644 --- a/src/main/java/com/extendedae_plus/init/ModCreativeTabs.java +++ b/src/main/java/com/extendedae_plus/init/ModCreativeTabs.java @@ -8,11 +8,8 @@ import net.neoforged.neoforge.registries.DeferredHolder; import net.neoforged.neoforge.registries.DeferredRegister; public final class ModCreativeTabs { - private ModCreativeTabs() {} - public static final DeferredRegister TABS = DeferredRegister.create(Registries.CREATIVE_MODE_TAB, ExtendedAEPlus.MODID); - public static final DeferredHolder MAIN = TABS.register("main", () -> CreativeModeTab.builder() .title(Component.translatable("itemGroup." + ExtendedAEPlus.MODID + ".main")) @@ -28,10 +25,13 @@ public final class ModCreativeTabs { output.accept(ModItems.ACCELERATOR_1024x.get()); output.accept(ModItems.ENTITY_TICKER_PART_ITEM.get()); // 放入四个预设的 stacks(x2,x4,x8,x16),使用 ModItems 工厂创建 - output.accept(ModItems.createEntitySpeedCardStack(2)); - output.accept(ModItems.createEntitySpeedCardStack(4)); - output.accept(ModItems.createEntitySpeedCardStack(8)); - output.accept(ModItems.createEntitySpeedCardStack(16)); + output.accept(ModItems.createEntitySpeedCardStack((byte) 2)); + output.accept(ModItems.createEntitySpeedCardStack((byte) 4)); + output.accept(ModItems.createEntitySpeedCardStack((byte) 8)); + output.accept(ModItems.createEntitySpeedCardStack((byte) 16)); }) .build()); + + private ModCreativeTabs() { + } } diff --git a/src/main/java/com/extendedae_plus/init/ModItems.java b/src/main/java/com/extendedae_plus/init/ModItems.java index 1f2dcc6..e985c1e 100644 --- a/src/main/java/com/extendedae_plus/init/ModItems.java +++ b/src/main/java/com/extendedae_plus/init/ModItems.java @@ -1,8 +1,5 @@ package com.extendedae_plus.init; -import appeng.api.parts.IPart; -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.EntitySpeedTickerPartItem; @@ -65,22 +62,10 @@ public final class ModItems { () -> new EntitySpeedCardItem(new Item.Properties()) ); - /** - * 为 PartItem 注册 AE2 部件模型。 - * 在客户端进行模型/几何体注册时调用。 - */ - public static void registerPartModels() { - PartModels.registerModels( - PartModelsHelper.createModels( - ENTITY_TICKER_PART_ITEM.get().getPartClass().asSubclass(IPart.class) - ) - ); - } - /** * 工厂:创建带 multiplier 的实体加速卡 ItemStack(2/4/8/16) */ - public static ItemStack createEntitySpeedCardStack(int multiplier) { + public static ItemStack createEntitySpeedCardStack(byte multiplier) { return EntitySpeedCardItem.withMultiplier(multiplier); } } diff --git a/src/main/resources/data/extendedae_plus/recipe/entity_speed_card_x16.json b/src/main/resources/data/extendedae_plus/recipe/entity_speed_card_x16.json index 821378b..838b694 100644 --- a/src/main/resources/data/extendedae_plus/recipe/entity_speed_card_x16.json +++ b/src/main/resources/data/extendedae_plus/recipe/entity_speed_card_x16.json @@ -7,11 +7,14 @@ ], "key": { "S": { - "type": "forge:partial_nbt", - "item": "extendedae_plus:entity_speed_card", - "nbt": { - "EAS:mult": 8 - } + "type": "neoforge:components", + "items": "extendedae_plus:entity_speed_card", + "components": { + "minecraft:custom_data": { + "EAS:mult": 8 + } + }, + "strict": true }, "A": { "item": "minecraft:nether_star" @@ -27,11 +30,12 @@ } }, "result": { - "type": "forge:partial_nbt", - "item": "extendedae_plus:entity_speed_card", + "id": "extendedae_plus:entity_speed_card", "count": 1, - "nbt": { - "EAS:mult": 16 + "components": { + "minecraft:custom_data": { + "EAS:mult": 16 + } } } } \ No newline at end of file diff --git a/src/main/resources/data/extendedae_plus/recipe/entity_speed_card_x2.json b/src/main/resources/data/extendedae_plus/recipe/entity_speed_card_x2.json index 6c4466a..2d59638 100644 --- a/src/main/resources/data/extendedae_plus/recipe/entity_speed_card_x2.json +++ b/src/main/resources/data/extendedae_plus/recipe/entity_speed_card_x2.json @@ -20,11 +20,12 @@ } }, "result": { - "type": "forge:partial_nbt", - "item": "extendedae_plus:entity_speed_card", + "id": "extendedae_plus:entity_speed_card", "count": 1, - "nbt": { - "EAS:mult": 2 + "components": { + "minecraft:custom_data": { + "EAS:mult": 2 + } } } -} \ No newline at end of file +} diff --git a/src/main/resources/data/extendedae_plus/recipe/entity_speed_card_x4.json b/src/main/resources/data/extendedae_plus/recipe/entity_speed_card_x4.json index 91defa0..fc759ed 100644 --- a/src/main/resources/data/extendedae_plus/recipe/entity_speed_card_x4.json +++ b/src/main/resources/data/extendedae_plus/recipe/entity_speed_card_x4.json @@ -7,28 +7,26 @@ ], "key": { "S": { - "type": "forge:partial_nbt", - "item": "extendedae_plus:entity_speed_card", - "nbt": { - "EAS:mult": 2 - } + "type": "neoforge:components", + "items": "extendedae_plus:entity_speed_card", + "components": { + "minecraft:custom_data": { + "EAS:mult": 2 + } + }, + "strict": true }, - "B": { - "item": "extendedae_plus:256x_crafting_accelerator" - }, - "Q": { - "item": "ae2:spatial_cell_component_16" - }, - "X": { - "item": "ae2:dense_energy_cell" - } + "B": { "item": "extendedae_plus:256x_crafting_accelerator" }, + "Q": { "item": "ae2:spatial_cell_component_2" }, + "X": { "item": "ae2:dense_energy_cell" } }, "result": { - "type": "forge:partial_nbt", - "item": "extendedae_plus:entity_speed_card", + "id": "extendedae_plus:entity_speed_card", "count": 1, - "nbt": { - "EAS:mult": 4 + "components": { + "minecraft:custom_data": { + "EAS:mult": 4 + } } } -} \ No newline at end of file +} diff --git a/src/main/resources/data/extendedae_plus/recipe/entity_speed_card_x8.json b/src/main/resources/data/extendedae_plus/recipe/entity_speed_card_x8.json index 73384f0..9aa57b3 100644 --- a/src/main/resources/data/extendedae_plus/recipe/entity_speed_card_x8.json +++ b/src/main/resources/data/extendedae_plus/recipe/entity_speed_card_x8.json @@ -7,28 +7,32 @@ ], "key": { "S": { - "type": "forge:partial_nbt", - "item": "extendedae_plus:entity_speed_card", - "nbt": { - "EAS:mult": 4 - } + "type": "neoforge:components", + "items": "extendedae_plus:entity_speed_card", + "components": { + "minecraft:custom_data": { + "EAS:mult": 4 + } + }, + "strict": true }, "B": { "item": "extendedae_plus:1024x_crafting_accelerator" }, "Q": { - "item": "ae2:spatial_cell_component_128" + "item": "ae2:spatial_cell_component_16" }, "X": { "item": "minecraft:nether_star" } }, "result": { - "type": "forge:partial_nbt", - "item": "extendedae_plus:entity_speed_card", + "id": "extendedae_plus:entity_speed_card", "count": 1, - "nbt": { - "EAS:mult": 8 + "components": { + "minecraft:custom_data": { + "EAS:mult": 8 + } } } } \ No newline at end of file diff --git a/src/main/resources/data/extendedae_plus/recipe/entity_speed_ticker.json b/src/main/resources/data/extendedae_plus/recipe/entity_speed_ticker.json index 076d943..4c5e8ef 100644 --- a/src/main/resources/data/extendedae_plus/recipe/entity_speed_ticker.json +++ b/src/main/resources/data/extendedae_plus/recipe/entity_speed_ticker.json @@ -7,11 +7,14 @@ ], "key": { "S": { - "type": "forge:partial_nbt", - "item": "extendedae_plus:entity_speed_card", - "nbt": { - "EAS:mult": 2 - } + "type": "neoforge:components", + "items": "extendedae_plus:entity_speed_card", + "components": { + "minecraft:custom_data": { + "EAS:mult": 2 + } + }, + "strict": true }, "Z": { "item": "ae2:dense_energy_cell" @@ -23,11 +26,11 @@ "item": "minecraft:nether_star" }, "I": { - "item": "expatternprovider:ex_io_port" + "item": "extendedae:ex_io_port" } }, "result": { - "item": "extendedae_plus:entity_speed_ticker", + "id": "extendedae_plus:entity_speed_ticker", "count": 1 } } \ No newline at end of file diff --git a/src/main/resources/data/extendedae_plus/recipe/infinity_biginteger_cell.json b/src/main/resources/data/extendedae_plus/recipe/infinity_biginteger_cell.json deleted file mode 100644 index 1949a53..0000000 --- a/src/main/resources/data/extendedae_plus/recipe/infinity_biginteger_cell.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "type": "minecraft:crafting_shapeless", - "ingredients": [ - { "item": "minecraft:turtle_helmet" }, - { "item": "minecraft:dragon_head" }, - { "item": "minecraft:totem_of_undying" }, - { "item": "minecraft:echo_shard" }, - { "item": "ae2:cell_component_256k" }, - { "item": "minecraft:heart_of_the_sea" }, - { "item": "minecraft:nether_star" }, - { "item": "minecraft:netherite_block" }, - { "item": "minecraft:enchanted_golden_apple" } - ], - "result": { - "id": "extendedae_plus:infinity_biginteger_cell", - "count": 1 - } -} \ No newline at end of file From 47c56a91a4824cf057cf2102185da09690cbe3e9 Mon Sep 17 00:00:00 2001 From: C-H716 <1536152356@qq.com> Date: Sun, 21 Sep 2025 14:46:13 +0800 Subject: [PATCH 11/43] =?UTF-8?q?feat:=20=E6=8C=89zh=5Fcn.json=E9=A1=BA?= =?UTF-8?q?=E5=BA=8F=E8=A1=A5=E5=85=A8=E5=B9=B6=E6=95=B4=E7=90=86en=5Fus.j?= =?UTF-8?q?son=E6=96=87=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../assets/extendedae_plus/lang/en_us.json | 110 ++++++++++++------ .../assets/extendedae_plus/lang/zh_cn.json | 66 +++++------ 2 files changed, 108 insertions(+), 68 deletions(-) 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 4072712..9a16f0c 100644 --- a/src/main/resources/assets/extendedae_plus/lang/en_us.json +++ b/src/main/resources/assets/extendedae_plus/lang/en_us.json @@ -1,51 +1,95 @@ { - "gui.expatternprovider.toggle_slots": "Toggle Slots", + "gui.expatternprovider.toggle_slots": "Toggle Slot Display", "gui.expatternprovider.hide_slots": "Hide Slots", "gui.expatternprovider.show_slots": "Show Slots", "gui.expatternprovider.clear_selection": "Clear Selection", - "gui.extendedae_plus.pattern_provider.select": "Select Pattern Provider", - "gui.extendedae_plus.pattern_provider.selected": "Pattern Provider Selected", - "gui.extendedae_plus.pattern_provider.unselected": "Click to select this pattern provider", - "message.extendedae_plus.no_provider_selected": "Please select a pattern provider first", - "message.extendedae_plus.invalid_pattern": "Please hold a valid encoded pattern", - "message.extendedae_plus.upload_success": "Pattern uploaded successfully", - "message.extendedae_plus.upload_failed": "Failed to upload pattern - no empty slots", - "message.extendedae_plus.uploading_pattern": "Uploading pattern...", - "gui.extendedae_plus.group_header.choice": "Toggle Group Choice", - "gui.extendedae_plus.group_header.choiceable": "Group is choiceable", - "gui.extendedae_plus.group_header.not_choiceable": "Group is not choiceable", - "itemGroup.extendedae_plus.main": "ExtendedAE Plus", + "gui.extendedae_plus.global.toggle_blocking": "Toggle Blocking Mode", + "gui.extendedae_plus.global.toggle_adv_blocking": "Toggle Advanced Blocking", + "gui.extendedae_plus.global.toggle_smart_doubling": "Toggle Smart Doubling", + "gui.extendedae_plus.global.all_on": "All On", + "gui.extendedae_plus.global.all_off": "All Off", - "config.jade.plugin_extendedae_plus.wireless_transceiver_info": "Wireless Transceiver Info", + "itemGroup.extendedae_plus.main": "ExtendedAE Plus", + "item.extendedae_plus.wireless_transceiver": "Wireless Transceiver", + "item.extendedae_plus.4x_crafting_accelerator": "4x Crafting Accelerator", + "item.extendedae_plus.16x_crafting_accelerator": "16x Crafting Accelerator", + "item.extendedae_plus.64x_crafting_accelerator": "64x Crafting Accelerator", + "item.extendedae_plus.256x_crafting_accelerator": "256x Crafting Accelerator", + "item.extendedae_plus.1024x_crafting_accelerator": "1024x Crafting Accelerator", + "item.extendedae_plus.network_pattern_controller": "Pattern Provider Status Controller", + "item.extendedae_plus.entity_speed_card": "Entity Speed Card", + "item.extendedae_plus.entity_speed_card.x2": "Entity Speed Card (x2)", + "item.extendedae_plus.entity_speed_card.x4": "Entity Speed Card (x4)", + "item.extendedae_plus.entity_speed_card.x8": "Entity Speed Card (x8)", + "item.extendedae_plus.entity_speed_card.x16": "Entity Speed Card (x16)", + "tooltip.extendedae_plus.entity_speed_card.multiplier": "Multiplier: %s", + "tooltip.extendedae_plus.entity_speed_card.max": "Maximum Effect: %s Times", + + "block.extendedae_plus.wireless_transceiver": "Wireless Transceiver", + "block.extendedae_plus.4x_crafting_accelerator": "4x Crafting Accelerator", + "block.extendedae_plus.16x_crafting_accelerator": "16x Crafting Accelerator", + "block.extendedae_plus.64x_crafting_accelerator": "64x Crafting Accelerator", + "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 Provider Status Controller", + + "extendedae_plus.upload_to_matrix": "Upload to Assembly Matrix", + "extendedae_plus.upload_to_matrix.success": "Pattern Uploaded to Assembly Matrix", + "extendedae_plus.upload_to_matrix.fail_not_crafting": "Only Crafting Patterns Can Be Uploaded, Processing Patterns Will Be Ignored", + "extendedae_plus.upload_to_matrix.fail_no_matrix": "No Formed Assembly Matrix Found in the Current Network", + "extendedae_plus.upload_to_matrix.fail_full": "Assembly Matrix Pattern Storage Is Full or Cannot Be Inserted", + "extendedae_plus.screen.reload_mapping": "Reload Mapping", + "extendedae_plus.screen.add_mapping": "Add Mapping", + "extendedae_plus.screen.cn_name": "Chinese Name", + "extendedae_plus.button.choose_provider": "Upload Pattern", + "extendedae_plus.pattern.hovertext.player": "Encoded by %s", + + "item.extendedae_plus.entity_speed_ticker": "Entity Accelerator", + "screen.extendedae_plus.entity_speed_ticker.enable": "§c§lMachine Has Been Disabled", + "screen.extendedae_plus.entity_speed_ticker.energy": "Energy Consumption: %s FE/t", + "screen.extendedae_plus.entity_speed_ticker.power_ratio": "Power Ratio: %s", + "screen.extendedae_plus.entity_speed_ticker.speed": "Current Acceleration Multiplier: %d", + "screen.extendedae_plus.entity_speed_ticker.multiplier": "Additional Consumption Multiplier: %s", + + "item.extendedae_plus.entity_speed_ticker.tip.requirement": "Requires Entity Speed Card to Enable Acceleration", + "item.extendedae_plus.entity_speed_ticker.tip.max": "Up to 1024x Acceleration", + "item.extendedae_plus.entity_speed_ticker.tip.energy": "Acceleration Consumes AE Network Energy; Insufficient Network Energy Prevents Acceleration", + + "config.jade.plugin_extendedae_plus.wireless_transceiver_info": "Wireless Transceiver Information", "config.jade.plugin_extendedae_plus.wt_frequency": "Show Frequency", "config.jade.plugin_extendedae_plus.wt_master_mode": "Show Master/Slave Mode", - "config.jade.plugin_extendedae_plus.wt_master_location": "Show Master Position", - "config.jade.plugin_extendedae_plus.wt_locked": "Show Locked State", - "config.jade.plugin_extendedae_plus.wt_network_usable": "Show Network Online State", + "config.jade.plugin_extendedae_plus.wt_master_location": "Show Master Node Location", + "config.jade.plugin_extendedae_plus.wt_locked": "Show Lock Status", + "config.jade.plugin_extendedae_plus.wt_network_usable": "Show Network Online Status", "extendedae_plus.tooltip.frequency": "Frequency: %d", "extendedae_plus.tooltip.master_mode": "Mode: %s", - "extendedae_plus.tooltip.locked": "Locked: %s", - "item.extendedae_plus.infinity_biginteger_cell": "§4De§cvou§6rer §eof §aCo§bsmic §dSilence", - "tooltip.extendedae_plus.infinity_biginteger_cell.summon1": "§6Through ninefold sacrifice, the Void echoes§r—§8Iava, Lord of the Void§r, bestows upon thee this artifact", - "tooltip.extendedae_plus.infinity_biginteger_cell.summon2": "§b—§4A §dUni§cverse §eWith§ain §6A §bSin§5gle §9Point", + "extendedae_plus.tooltip.locked": "Lock Status: %s", - "screen.extendedae_plus.title": "ExtendedAE Plus Config", + "screen.extendedae_plus.title": "ExtendedAE Plus Configuration", "config.extendedae_plus": "ExtendedAE Plus", + "extendedae_plus.configuration.general": "General", - "extendedae_plus.configuration.pageMultiplier": "Pattern Provider Page Multiplier", - "extendedae_plus.configuration.pageMultiplier_with_range": "Pattern Provider Page Multiplier (1-64)", - "extendedae_plus.configuration.showEncoderPatternPlayer": "Show Encoder Player", - "extendedae_plus.configuration.providerRoundRobinEnable": "Enable Provider Round-Robin", + "extendedae_plus.configuration.pageMultiplier": "Pattern Provider Slot Multiplier", + "extendedae_plus.configuration.pageMultiplier_with_range": "Pattern Provider Slot Multiplier (1-64)", + "extendedae_plus.configuration.showEncoderPatternPlayer": "Show Pattern Encoder Player", "extendedae_plus.configuration.patternTerminalShowSlotsDefault": "Pattern Terminal Show Slots by Default", + "extendedae_plus.configuration.craftingPauseThreshold": "AE Crafting Calculation Pause Threshold", - "extendedae_plus.configuration.smartScaling": "Smart Scaling", - "extendedae_plus.configuration.smartScalingMaxMultiplier": "Smart Scaling Max Multiplier", - "extendedae_plus.configuration.smartScalingMaxMultiplier_with_range": "Smart Scaling Max Multiplier (0 = Unlimited)", + "extendedae_plus.configuration.smartScaling": "Smart Pattern Scaling", + "extendedae_plus.configuration.providerRoundRobinEnable": "Enable Pattern Provider Round-Robin Allocation", + "extendedae_plus.configuration.smartScalingMaxMultiplier": "Smart Scaling Maximum Multiplier", + "extendedae_plus.configuration.smartScalingMaxMultiplier_with_range": "Smart Scaling Maximum Multiplier (0 for Unlimited)", + + "extendedae_plus.configuration.wireless": "Wireless Connector Configuration", + "extendedae_plus.configuration.wirelessMaxRange": "Wireless Maximum Range", + "extendedae_plus.configuration.wirelessMaxRange_with_range": "Wireless Maximum Range (1-4096)", + "extendedae_plus.configuration.wirelessCrossDimEnable": "Allow Wireless Transceiver Cross-Dimension Connection", + + "extendedae_plus.configuration.entitySpeedTicker": "Entity Accelerator", + "extendedae_plus.configuration.entityTickerCost": "Entity Accelerator Base Energy Consumption", + "extendedae_plus.configuration.entityTickerBlackList": "Entity Accelerator Blacklist", + "extendedae_plus.configuration.entityTickerMultipliers": "Entity Accelerator Additional Consumption Multiplier", - "extendedae_plus.configuration.wireless": "WirelessTransceiverBlock", - "extendedae_plus.configuration.wirelessMaxRange": "Wireless Max Range", - "extendedae_plus.configuration.wirelessMaxRange_with_range": "Wireless Max Range (1-4096)", - "extendedae_plus.configuration.wirelessCrossDimEnable": "Allow Wireless Cross-Dimension", "extendedae_plus.configuration.state_on": "On", "extendedae_plus.configuration.state_off": "Off" } \ 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 1699f08..e894f81 100644 --- a/src/main/resources/assets/extendedae_plus/lang/zh_cn.json +++ b/src/main/resources/assets/extendedae_plus/lang/zh_cn.json @@ -3,9 +3,36 @@ "gui.expatternprovider.hide_slots": "隐藏槽位", "gui.expatternprovider.show_slots": "显示槽位", "gui.expatternprovider.clear_selection": "取消选择", - "block.extendedae_plus.wireless_transceiver": "无线收发器", - "item.extendedae_plus.wireless_transceiver": "无线收发器", + "gui.extendedae_plus.global.toggle_blocking": "切换阻挡模式", + "gui.extendedae_plus.global.toggle_adv_blocking": "切换高级阻挡", + "gui.extendedae_plus.global.toggle_smart_doubling": "切换智能翻倍", + "gui.extendedae_plus.global.all_on": "全部开启", + "gui.extendedae_plus.global.all_off": "全部关闭", + "itemGroup.extendedae_plus.main": "扩展AE Plus", + "item.extendedae_plus.wireless_transceiver": "无线收发器", + "item.extendedae_plus.4x_crafting_accelerator": "4x并行处理单元", + "item.extendedae_plus.16x_crafting_accelerator": "16x并行处理单元", + "item.extendedae_plus.64x_crafting_accelerator": "64x并行处理单元", + "item.extendedae_plus.256x_crafting_accelerator": "256x并行处理单元", + "item.extendedae_plus.1024x_crafting_accelerator": "1024x并行处理单元", + "item.extendedae_plus.network_pattern_controller": "样板供应器状态控制器", + "item.extendedae_plus.entity_speed_card": "实体加速卡", + "item.extendedae_plus.entity_speed_card.x2": "实体加速卡 (x2)", + "item.extendedae_plus.entity_speed_card.x4": "实体加速卡 (x4)", + "item.extendedae_plus.entity_speed_card.x8": "实体加速卡 (x8)", + "item.extendedae_plus.entity_speed_card.x16": "实体加速卡 (x16)", + "tooltip.extendedae_plus.entity_speed_card.multiplier": "乘数: %s", + "tooltip.extendedae_plus.entity_speed_card.max": "最大生效: %s 倍", + + "block.extendedae_plus.wireless_transceiver": "无线收发器", + "block.extendedae_plus.4x_crafting_accelerator": "4x并行处理单元", + "block.extendedae_plus.16x_crafting_accelerator": "16x并行处理单元", + "block.extendedae_plus.64x_crafting_accelerator": "64x并行处理单元", + "block.extendedae_plus.256x_crafting_accelerator": "256x并行处理单元", + "block.extendedae_plus.1024x_crafting_accelerator": "1024x并行处理单元", + "block.extendedae_plus.network_pattern_controller": "样板供应器状态控制器", + "extendedae_plus.upload_to_matrix": "上传到装配矩阵", "extendedae_plus.upload_to_matrix.success": "样板已上传到装配矩阵", "extendedae_plus.upload_to_matrix.fail_not_crafting": "仅支持上传合成样板,处理样板将被忽略", @@ -16,18 +43,6 @@ "extendedae_plus.screen.cn_name": "中文名", "extendedae_plus.button.choose_provider":"上传样板", "extendedae_plus.pattern.hovertext.player": "由 %s 编码", - - "block.extendedae_plus.4x_crafting_accelerator": "4x并行处理单元", - "block.extendedae_plus.16x_crafting_accelerator": "16x并行处理单元", - "block.extendedae_plus.64x_crafting_accelerator": "64x并行处理单元", - "block.extendedae_plus.256x_crafting_accelerator": "256x并行处理单元", - "block.extendedae_plus.1024x_crafting_accelerator": "1024x并行处理单元", - - "item.extendedae_plus.4x_crafting_accelerator": "4x并行处理单元", - "item.extendedae_plus.16x_crafting_accelerator": "16x并行处理单元", - "item.extendedae_plus.64x_crafting_accelerator": "64x并行处理单元", - "item.extendedae_plus.256x_crafting_accelerator": "256x并行处理单元", - "item.extendedae_plus.1024x_crafting_accelerator": "1024x并行处理单元", "item.extendedae_plus.entity_speed_ticker": "实体加速器", "screen.extendedae_plus.entity_speed_ticker.enable": "§c§l机器已被禁用", @@ -40,17 +55,6 @@ "item.extendedae_plus.entity_speed_ticker.tip.max": "最高可达 1024x 加速", "item.extendedae_plus.entity_speed_ticker.tip.energy": "加速将消耗 AE 网络能量,网络能量不足时无法加速", - "item.extendedae_plus.entity_speed_card": "实体加速卡", - "item.extendedae_plus.entity_speed_card.x2": "实体加速卡 (x2)", - "item.extendedae_plus.entity_speed_card.x4": "实体加速卡 (x4)", - "item.extendedae_plus.entity_speed_card.x8": "实体加速卡 (x8)", - "item.extendedae_plus.entity_speed_card.x16": "实体加速卡 (x16)", - "tooltip.extendedae_plus.entity_speed_card.multiplier": "乘数: %s", - "tooltip.extendedae_plus.entity_speed_card.max": "最大生效: %s 倍", - "item.extendedae_plus.infinity_biginteger_cell": "§4吞§c噬§6万§e籁§a的§b寂§d静", - "tooltip.extendedae_plus.infinity_biginteger_cell.summon1": "§6九重献祭, 终得虚空回响§r——觐见§8虚空之主Iava§r, 赐汝此物", - "tooltip.extendedae_plus.infinity_biginteger_cell.summon2": "§b——§4方§d寸§c之§e间§a, §6自§b有§5千§9寰", - "config.jade.plugin_extendedae_plus.wireless_transceiver_info": "无线收发器信息", "config.jade.plugin_extendedae_plus.wt_frequency": "显示频率", "config.jade.plugin_extendedae_plus.wt_master_mode": "显示主/从模式", @@ -69,7 +73,7 @@ "extendedae_plus.configuration.pageMultiplier_with_range": "扩展样板供应器槽位倍率 (1-64)", "extendedae_plus.configuration.showEncoderPatternPlayer": "显示样板编码玩家", "extendedae_plus.configuration.patternTerminalShowSlotsDefault": "样板终端默认显示槽位", - + "extendedae_plus.configuration.craftingPauseThreshold": "AE合成计算暂停检查阈值", "extendedae_plus.configuration.smartScaling": "智能样板倍增", "extendedae_plus.configuration.providerRoundRobinEnable": "启用样板供应器轮询分配", @@ -85,15 +89,7 @@ "extendedae_plus.configuration.entityTickerCost": "实体加速器能量消耗基础值", "extendedae_plus.configuration.entityTickerBlackList": "实体加速器黑名单", "extendedae_plus.configuration.entityTickerMultipliers": "实体加速器额外消耗倍率", - "extendedae_pluscraftingPauseThreshold": "AE合成计算暂停检查阈值", "extendedae_plus.configuration.state_on": "开", - "extendedae_plus.configuration.state_off": "关", - "block.extendedae_plus.network_pattern_controller": "样板供应器状态控制器", - "item.extendedae_plus.network_pattern_controller": "样板供应器状态控制器", - "gui.extendedae_plus.global.toggle_blocking": "切换阻挡模式", - "gui.extendedae_plus.global.toggle_adv_blocking": "切换高级阻挡", - "gui.extendedae_plus.global.toggle_smart_doubling": "切换智能翻倍", - "gui.extendedae_plus.global.all_on": "全部开启", - "gui.extendedae_plus.global.all_off": "全部关闭" + "extendedae_plus.configuration.state_off": "关" } \ No newline at end of file From 9966205aa20c8a46a13c36fcb782b2e602ec169e Mon Sep 17 00:00:00 2001 From: C-H716 <1536152356@qq.com> Date: Sun, 21 Sep 2025 18:16:27 +0800 Subject: [PATCH 12/43] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E6=9C=8D?= =?UTF-8?q?=E5=8A=A1=E7=AB=AF=E5=B4=A9=E6=BA=83=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 5 ++++- .../network/ProvidersListS2CPacket.java | 17 +++++++++++------ src/main/resources/extendedae_plus.mixins.json | 2 +- 3 files changed, 16 insertions(+), 8 deletions(-) diff --git a/build.gradle b/build.gradle index 7394979..3dd7ba2 100644 --- a/build.gradle +++ b/build.gradle @@ -166,11 +166,14 @@ dependencies { runtimeOnly "curse.maven:ex-pattern-provider-892005:6863556" //aea - implementation "curse.maven:advancedae-1084104:6921281" + implementation "curse.maven:advancedae-1084104:6932981" //geckolib runtimeOnly "curse.maven:geckolib-388172:7009924" + + runtimeOnly fileTree(dir: 'libs', includes: ['*.jar']) + // setup Xei (EMI/REI/JEI) using project property 'use_Xei' switch (project.findProperty('use_Xei') ?: 'emi') { case 'emi': diff --git a/src/main/java/com/extendedae_plus/network/ProvidersListS2CPacket.java b/src/main/java/com/extendedae_plus/network/ProvidersListS2CPacket.java index 0fa8184..2da91df 100644 --- a/src/main/java/com/extendedae_plus/network/ProvidersListS2CPacket.java +++ b/src/main/java/com/extendedae_plus/network/ProvidersListS2CPacket.java @@ -7,6 +7,8 @@ import net.minecraft.network.RegistryFriendlyByteBuf; import net.minecraft.network.codec.StreamCodec; import net.minecraft.network.protocol.common.custom.CustomPacketPayload; import net.minecraft.resources.ResourceLocation; +import net.neoforged.api.distmarker.Dist; +import net.neoforged.api.distmarker.OnlyIn; import net.neoforged.neoforge.network.handling.IPayloadContext; import java.util.ArrayList; @@ -58,11 +60,14 @@ public class ProvidersListS2CPacket implements CustomPacketPayload { } public static void handle(final ProvidersListS2CPacket msg, final IPayloadContext ctx) { - ctx.enqueueWork(() -> { - var mc = Minecraft.getInstance(); - if (mc == null) return; - var current = mc.screen; - mc.setScreen(new ProviderSelectScreen(current, msg.ids, msg.names, msg.emptySlots)); - }); + ctx.enqueueWork(() -> handleClient(msg)); + } + + @OnlyIn(Dist.CLIENT) + private static void handleClient(ProvidersListS2CPacket msg) { + var mc = Minecraft.getInstance(); + if (mc == null) return; + var current = mc.screen; + mc.setScreen(new ProviderSelectScreen(current, msg.ids, msg.names, msg.emptySlots)); } } diff --git a/src/main/resources/extendedae_plus.mixins.json b/src/main/resources/extendedae_plus.mixins.json index f7a85bd..034dc6d 100644 --- a/src/main/resources/extendedae_plus.mixins.json +++ b/src/main/resources/extendedae_plus.mixins.json @@ -14,7 +14,6 @@ "ae2.CraftingCalculationMixin", "ae2.CraftingCPUClusterMixin", "ae2.EncodedPatternItemMixin", - "ae2.QuartzCuttingKnifeItemMixin", "ae2.accessor.MEStorageMenuAccessor", "ae2.accessor.PatternEncodingTermMenuAccessor", "ae2.accessor.PatternProviderLogicAccessor", @@ -46,6 +45,7 @@ "accessor.ScreenAccessor", "advancedae.client.gui.AdvPatternProviderScreenMixin", "advancedae.client.gui.SmallAdvPatternProviderScreenMixin", + "ae2.QuartzCuttingKnifeItemMixin", "ae2.accessor.AEBaseScreenAccessor", "ae2.accessor.AEBaseScreenInvoker", "ae2.accessor.MEStorageScreenAccessor", From 9c7d49bb7ec9062cfb248250a50759c35f910f94 Mon Sep 17 00:00:00 2001 From: C-H716 <1536152356@qq.com> Date: Sun, 21 Sep 2025 19:37:46 +0800 Subject: [PATCH 13/43] =?UTF-8?q?feat:=20=E8=B0=83=E6=95=B4lang=E6=96=87?= =?UTF-8?q?=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/extendedae_plus/ExtendedAEPlus.java | 4 +- .../extendedae_plus/config/ModConfigs.java | 120 ++++++++---------- .../mixin/ae2/EncodedPatternItemMixin.java | 8 +- .../assets/extendedae_plus/lang/en_us.json | 25 ++-- .../assets/extendedae_plus/lang/zh_cn.json | 9 +- 5 files changed, 80 insertions(+), 86 deletions(-) diff --git a/src/main/java/com/extendedae_plus/ExtendedAEPlus.java b/src/main/java/com/extendedae_plus/ExtendedAEPlus.java index 3f6640a..5a01c42 100644 --- a/src/main/java/com/extendedae_plus/ExtendedAEPlus.java +++ b/src/main/java/com/extendedae_plus/ExtendedAEPlus.java @@ -54,7 +54,9 @@ public class ExtendedAEPlus { NeoForge.EVENT_BUS.register(this); // 注册配置:接入自定义的 ModConfigs - modContainer.registerConfig(ModConfig.Type.COMMON, ModConfigs.COMMON_SPEC); + modContainer.registerConfig(ModConfig.Type.COMMON, ModConfigs.COMMON_SPEC, "extendedae_plus-common.toml"); + modContainer.registerConfig(ModConfig.Type.CLIENT, ModConfigs.CLIENT_SPEC, "extendedae_plus-client.toml"); + modContainer.registerConfig(ModConfig.Type.SERVER, ModConfigs.SERVER_SPEC, "extendedae_plus-server.toml"); } // 便捷 ResourceLocation 工具 diff --git a/src/main/java/com/extendedae_plus/config/ModConfigs.java b/src/main/java/com/extendedae_plus/config/ModConfigs.java index 5abe719..bd6665e 100644 --- a/src/main/java/com/extendedae_plus/config/ModConfigs.java +++ b/src/main/java/com/extendedae_plus/config/ModConfigs.java @@ -5,61 +5,62 @@ import net.neoforged.neoforge.common.ModConfigSpec; import java.util.List; public final class ModConfigs { + // Common 配置 public static final ModConfigSpec COMMON_SPEC; public static final ModConfigSpec.IntValue PAGE_MULTIPLIER; - public static final ModConfigSpec.DoubleValue WIRELESS_MAX_RANGE; - public static final ModConfigSpec.BooleanValue WIRELESS_CROSS_DIM_ENABLE; - public static final ModConfigSpec.BooleanValue SHOW_ENCOD_PATTERN_PLAYER; - public static final ModConfigSpec.BooleanValue PROVIDER_ROUND_ROBIN_ENABLE; + + // Client 配置 + public static final ModConfigSpec CLIENT_SPEC; + public static final ModConfigSpec.BooleanValue SHOW_ENCODER_PATTERN_PLAYER; public static final ModConfigSpec.BooleanValue PATTERN_TERMINAL_SHOW_SLOTS_DEFAULT; + + // Server 配置 + public static final ModConfigSpec SERVER_SPEC; + public static final ModConfigSpec.BooleanValue PROVIDER_ROUND_ROBIN_ENABLE; public static final ModConfigSpec.IntValue SMART_SCALING_MAX_MULTIPLIER; public static final ModConfigSpec.IntValue CRAFTING_PAUSE_THRESHOLD; + public static final ModConfigSpec.DoubleValue WIRELESS_MAX_RANGE; + public static final ModConfigSpec.BooleanValue WIRELESS_CROSS_DIM_ENABLE; public static final ModConfigSpec.IntValue ENTITY_TICKER_COST; public static final ModConfigSpec.ConfigValue> ENTITY_TICKER_BLACK_LIST; public static final ModConfigSpec.ConfigValue> ENTITY_TICKER_MULTIPLIERS; - static { - ModConfigSpec.Builder builder = new ModConfigSpec.Builder(); - // General settings - builder.push("general"); - PAGE_MULTIPLIER = builder + static { + // Common 配置 + ModConfigSpec.Builder commonBuilder = new ModConfigSpec.Builder(); + commonBuilder.push("common"); + PAGE_MULTIPLIER = commonBuilder .comment( "扩展样板供应器总槽位容量的倍率", "基础为36,每页仍显示36格,倍率会增加总页数/总容量", "建议范围 1-16" ) .defineInRange("pageMultiplier", 1, 1, 64); + commonBuilder.pop(); + COMMON_SPEC = commonBuilder.build(); - // 是否显示样板编码玩家(通用) - SHOW_ENCOD_PATTERN_PLAYER = builder + // Client 配置 + ModConfigSpec.Builder clientBuilder = new ModConfigSpec.Builder(); + clientBuilder.push("client"); + SHOW_ENCODER_PATTERN_PLAYER = clientBuilder .comment( "是否显示样板编码玩家", "开启后将在样板 HoverText 上添加样板的编码玩家" ) .define("showEncoderPatternPlayer", true); - - // 模式访问终端(ExtendedAE 图样终端)默认是否显示槽位渲染(SlotsRow)。 - // true: 默认显示(可通过界面按钮临时隐藏);false: 默认隐藏(可通过按钮显示) - PATTERN_TERMINAL_SHOW_SLOTS_DEFAULT = builder + PATTERN_TERMINAL_SHOW_SLOTS_DEFAULT = clientBuilder .comment( "样板终端默认是否显示槽位", "影响进入界面时SlotsRow的默认可见性,仅影响客户端显示" ) .define("patternTerminalShowSlotsDefault", true); + clientBuilder.pop(); + CLIENT_SPEC = clientBuilder.build(); - CRAFTING_PAUSE_THRESHOLD = builder - .comment( - "值越大将减少AE构建合成计划过程中的 wait/notify 次数,提升吞吐但会降低调度响应性" - ) - .defineInRange("craftingPauseThreshold", 100000, 100, Integer.MAX_VALUE); - - // end general - builder.pop(); - - // Smart-scaling group - builder.push("smartScaling"); - // 智能倍增:是否在样板供应器间轮询分配请求量(开启:按 provider 均分;关闭:不拆分) - PROVIDER_ROUND_ROBIN_ENABLE = builder + // Server 配置 + ModConfigSpec.Builder serverBuilder = new ModConfigSpec.Builder(); + serverBuilder.push("ae"); + PROVIDER_ROUND_ROBIN_ENABLE = serverBuilder .comment( "智能倍增时是否对样板供应器轮询分配", "仅多个供应器有相同样板时生效,开启后请求会均分到所有可用供应器,关闭则全部分配给单一供应器", @@ -67,75 +68,64 @@ public final class ModConfigs { "默认: true" ) .define("providerRoundRobinEnable", true); - - // 智能倍增的最大倍数(以单次样板产出为单位)。 - // 0 表示不限制;>0 表示最大倍增倍数上限,例如 64 表示最多放大到 64 倍。 - SMART_SCALING_MAX_MULTIPLIER = builder + SMART_SCALING_MAX_MULTIPLIER = serverBuilder .comment( "智能倍增的最大倍数(0 表示不限制)", "此倍数是针对单次样板产出的放大倍数上限,用于限制一次推送中按倍增缩放的规模" ) .defineInRange("smartScalingMaxMultiplier", 0, 0, 1048576); + CRAFTING_PAUSE_THRESHOLD = serverBuilder + .comment( + "值越大将减少AE构建合成计划过程中的 wait/notify 次数,提升吞吐但会降低调度响应性" + ) + .defineInRange("craftingPauseThreshold", 100000, 100, Integer.MAX_VALUE); + serverBuilder.pop(); - builder.pop(); // pop smart - - // Wireless settings - builder.push("wireless"); - // 无线收发器:最大连接距离(单位:方块)。 - // 一对多从端连接主端时,将以该值作为范围限制。 - WIRELESS_MAX_RANGE = builder + serverBuilder.push("wireless"); + WIRELESS_MAX_RANGE = serverBuilder .comment( "无线收发器最大连接距离(单位:方块)", "从端与主端的直线距离需小于等于该值才会建立连接。" ) .defineInRange("wirelessMaxRange", 256.0D, 1.0D, 4096.0D); - - // 是否允许跨维度连接(忽略维度差异进行频道传输)。 - WIRELESS_CROSS_DIM_ENABLE = builder + WIRELESS_CROSS_DIM_ENABLE = serverBuilder .comment( "是否允许无线收发器跨维度建立连接", "开启后,从端可连接到不同维度的主端(忽略距离限制)" ) .define("wirelessCrossDimEnable", true); + serverBuilder.pop(); - builder.pop(); // pop wireless - - builder.push("entitySpeedTicker"); - - ENTITY_TICKER_COST = builder + serverBuilder.push("entitySpeedTicker"); + ENTITY_TICKER_COST = serverBuilder .comment( "实体加速器能量消耗基础值" ) - .defineInRange("entityTickerCost", 512, 0 , Integer.MAX_VALUE); - - - ENTITY_TICKER_BLACK_LIST = builder + .defineInRange("entityTickerCost", 512, 0, Integer.MAX_VALUE); + ENTITY_TICKER_BLACK_LIST = serverBuilder .comment( "实体加速器黑名单:匹配的方块将不会被加速。支持通配符/正则(例如:minecraft:*)", "格式:全名或通配符/正则字符串,例如 'minecraft:chest'、'minecraft:*'、'modid:.*_fluid'" ) .defineListAllowEmpty( - List.of("entityTickerBlackList"), // 路径 - List::of, // 默认值 - () -> "", // 新元素默认值(空字符串,供配置编辑器使用) - obj -> obj instanceof String // 验证每个元素是字符串 + List.of("entityTickerBlackList"), + List::of, + () -> "", + obj -> obj instanceof String ); - - ENTITY_TICKER_MULTIPLIERS = builder + ENTITY_TICKER_MULTIPLIERS = serverBuilder .comment( "额外消耗倍率配置:为某些方块设置额外能量倍率,格式 'modid:blockid multiplier',例如 'minecraft:chest 2x'", "支持通配符/正则匹配(例如 'minecraft:* 2x' 会对整个命名空间生效)。" ) .defineListAllowEmpty( - List.of("entityTickerMultipliers"), // 路径 - List::of, // 默认值 - () -> "", // 新元素默认值(空字符串,供配置编辑器使用) - obj -> obj instanceof String // 验证每个元素是字符串 + List.of("entityTickerMultipliers"), + List::of, + () -> "", + obj -> obj instanceof String ); - - builder.pop(); - - COMMON_SPEC = builder.build(); + serverBuilder.pop(); + SERVER_SPEC = serverBuilder.build(); } private ModConfigs() { diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/EncodedPatternItemMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/EncodedPatternItemMixin.java index efb4106..619368d 100644 --- a/src/main/java/com/extendedae_plus/mixin/ae2/EncodedPatternItemMixin.java +++ b/src/main/java/com/extendedae_plus/mixin/ae2/EncodedPatternItemMixin.java @@ -5,10 +5,10 @@ import com.extendedae_plus.config.ModConfigs; import net.minecraft.ChatFormatting; import net.minecraft.core.component.DataComponents; import net.minecraft.network.chat.Component; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.item.component.CustomData; -import net.minecraft.world.item.TooltipFlag; import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.TooltipFlag; +import net.minecraft.world.item.component.CustomData; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; @@ -21,7 +21,7 @@ public class EncodedPatternItemMixin { // 客户端:在 HoverText 显示样板的编码玩家 @Inject(method = "appendHoverText", at = @At("TAIL")) public void epp$appendHoverText(ItemStack stack, Item.TooltipContext context, List lines, TooltipFlag advancedTooltips, CallbackInfo ci){ - if (ModConfigs.SHOW_ENCOD_PATTERN_PLAYER.get()) { + if (ModConfigs.SHOW_ENCODER_PATTERN_PLAYER.get()) { var customData = stack.getOrDefault(DataComponents.CUSTOM_DATA, CustomData.EMPTY); var tag = customData.copyTag(); if (tag.contains("encodePlayer")) { 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 9a16f0c..babef8e 100644 --- a/src/main/resources/assets/extendedae_plus/lang/en_us.json +++ b/src/main/resources/assets/extendedae_plus/lang/en_us.json @@ -67,28 +67,29 @@ "screen.extendedae_plus.title": "ExtendedAE Plus Configuration", "config.extendedae_plus": "ExtendedAE Plus", - - "extendedae_plus.configuration.general": "General", + + "extendedae_plus.configuration.common": "Common Configuration", "extendedae_plus.configuration.pageMultiplier": "Pattern Provider Slot Multiplier", "extendedae_plus.configuration.pageMultiplier_with_range": "Pattern Provider Slot Multiplier (1-64)", + + "extendedae_plus.configuration.client": "Display Configuration", "extendedae_plus.configuration.showEncoderPatternPlayer": "Show Pattern Encoder Player", "extendedae_plus.configuration.patternTerminalShowSlotsDefault": "Pattern Terminal Show Slots by Default", - "extendedae_plus.configuration.craftingPauseThreshold": "AE Crafting Calculation Pause Threshold", + "extendedae_plus.configuration.ae": "AE2 Configuration", + "extendedae_plus.configuration.craftingPauseThreshold": "AE Crafting Pause Threshold", "extendedae_plus.configuration.smartScaling": "Smart Pattern Scaling", - "extendedae_plus.configuration.providerRoundRobinEnable": "Enable Pattern Provider Round-Robin Allocation", + "extendedae_plus.configuration.providerRoundRobinEnable": "Enable Pattern Provider Round-Robin", "extendedae_plus.configuration.smartScalingMaxMultiplier": "Smart Scaling Maximum Multiplier", - "extendedae_plus.configuration.smartScalingMaxMultiplier_with_range": "Smart Scaling Maximum Multiplier (0 for Unlimited)", - + "extendedae_plus.configuration.smartScalingMaxMultiplier_with_range": "Smart Scaling Maximum Multiplier (0 for unlimited)", "extendedae_plus.configuration.wireless": "Wireless Connector Configuration", "extendedae_plus.configuration.wirelessMaxRange": "Wireless Maximum Range", "extendedae_plus.configuration.wirelessMaxRange_with_range": "Wireless Maximum Range (1-4096)", - "extendedae_plus.configuration.wirelessCrossDimEnable": "Allow Wireless Transceiver Cross-Dimension Connection", - - "extendedae_plus.configuration.entitySpeedTicker": "Entity Accelerator", - "extendedae_plus.configuration.entityTickerCost": "Entity Accelerator Base Energy Consumption", - "extendedae_plus.configuration.entityTickerBlackList": "Entity Accelerator Blacklist", - "extendedae_plus.configuration.entityTickerMultipliers": "Entity Accelerator Additional Consumption Multiplier", + "extendedae_plus.configuration.wirelessCrossDimEnable": "Enable Wireless Cross-Dimensional Connection", + "extendedae_plus.configuration.entitySpeedTicker": "Entity Speed Ticker", + "extendedae_plus.configuration.entityTickerCost": "Entity Ticker Base Energy Cost", + "extendedae_plus.configuration.entityTickerBlackList": "Entity Ticker Blacklist", + "extendedae_plus.configuration.entityTickerMultipliers": "Entity Ticker Extra Cost Multipliers", "extendedae_plus.configuration.state_on": "On", "extendedae_plus.configuration.state_off": "Off" 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 e894f81..8592356 100644 --- a/src/main/resources/assets/extendedae_plus/lang/zh_cn.json +++ b/src/main/resources/assets/extendedae_plus/lang/zh_cn.json @@ -68,23 +68,24 @@ "screen.extendedae_plus.title": "ExtendedAE Plus 配置", "config.extendedae_plus": "ExtendedAE Plus", - "extendedae_plus.configuration.general": "通用", + "extendedae_plus.configuration.common": "通用配置", "extendedae_plus.configuration.pageMultiplier": "扩展样板供应器槽位倍率", "extendedae_plus.configuration.pageMultiplier_with_range": "扩展样板供应器槽位倍率 (1-64)", + + "extendedae_plus.configuration.client": "显示配置", "extendedae_plus.configuration.showEncoderPatternPlayer": "显示样板编码玩家", "extendedae_plus.configuration.patternTerminalShowSlotsDefault": "样板终端默认显示槽位", - "extendedae_plus.configuration.craftingPauseThreshold": "AE合成计算暂停检查阈值", + "extendedae_plus.configuration.ae": "AE2配置", + "extendedae_plus.configuration.craftingPauseThreshold": "AE合成计算暂停检查阈值", "extendedae_plus.configuration.smartScaling": "智能样板倍增", "extendedae_plus.configuration.providerRoundRobinEnable": "启用样板供应器轮询分配", "extendedae_plus.configuration.smartScalingMaxMultiplier": "智能倍增最大倍数", "extendedae_plus.configuration.smartScalingMaxMultiplier_with_range": "智能倍增最大倍数 (0为不限制)", - "extendedae_plus.configuration.wireless": "无线连接器配置", "extendedae_plus.configuration.wirelessMaxRange": "无线最大距离", "extendedae_plus.configuration.wirelessMaxRange_with_range": "无线最大距离 (1-4096)", "extendedae_plus.configuration.wirelessCrossDimEnable": "无线收发器允许跨维度连接", - "extendedae_plus.configuration.entitySpeedTicker": "实体加速器", "extendedae_plus.configuration.entityTickerCost": "实体加速器能量消耗基础值", "extendedae_plus.configuration.entityTickerBlackList": "实体加速器黑名单", From 4de90feae20f2cb19bc05196919c522a47983c97 Mon Sep 17 00:00:00 2001 From: C-H716 <1536152356@qq.com> Date: Sun, 21 Sep 2025 21:31:10 +0800 Subject: [PATCH 14/43] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E8=83=8C?= =?UTF-8?q?=E5=8C=85=E6=9C=89=E7=BD=91=E7=BB=9C=E5=B7=A5=E5=85=B7=E6=89=93?= =?UTF-8?q?=E5=BC=80=E5=AE=9E=E4=BD=93=E5=8A=A0=E9=80=9F=E5=99=A8=E7=95=8C?= =?UTF-8?q?=E9=9D=A2=E5=B4=A9=E6=BA=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../assets/ae2/screens/entity_speed_ticker.json | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) 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..aaeab6c 100644 --- a/src/main/resources/assets/ae2/screens/entity_speed_ticker.json +++ b/src/main/resources/assets/ae2/screens/entity_speed_ticker.json @@ -2,7 +2,8 @@ "$schema": "schema.json", "includes": [ "common/common.json", - "common/player_inventory.json" + "common/player_inventory.json", + "common/toolbox.json" ], "background": { "texture": "guis/blank_background.png", @@ -62,16 +63,14 @@ }, "widgets": { "toolbox": { - "right": -2, - "bottom": 50, - "width": 68, - "height": 68 + "right": 1, + "bottom": 55 } }, "slots": { "TOOLBOX": { - "bottom": 42, - "right": -10, + "right": 0, + "bottom": 50, "grid": "BREAK_AFTER_3COLS" } } From 47aa90a4388c73474bafcc549a7931439d827b7d Mon Sep 17 00:00:00 2001 From: GaLicn <133291877+GaLicn@users.noreply.github.com> Date: Wed, 24 Sep 2025 12:42:48 +0800 Subject: [PATCH 15/43] =?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 --- build.gradle | 2 +- .../modMetadata/META-INF/neoforge.mods.toml | 103 ++++++++++++++++++ gradle.properties | 2 +- .../com/extendedae_plus/ExtendedAEPlus.java | 11 +- .../content/matrix/UploadCoreBlock.java | 26 +++++ .../content/matrix/UploadCoreBlockEntity.java | 24 ++++ .../init/ModBlockEntities.java | 7 ++ .../com/extendedae_plus/init/ModBlocks.java | 12 ++ .../extendedae_plus/init/ModCreativeTabs.java | 1 + .../com/extendedae_plus/init/ModItems.java | 6 + .../util/ExtendedAEPatternUploadUtil.java | 24 +++- .../assembler_matrix_upload_core.json | 5 + .../assets/extendedae_plus/lang/en_us.json | 2 + .../assets/extendedae_plus/lang/zh_cn.json | 2 + .../block/assembler_matrix_upload_core.json | 6 + .../item/assembler_matrix_upload_core.json | 3 + .../block/assembler_matrix_upload_core.png | Bin 0 -> 225 bytes .../textures/item/channel_card.png | Bin 0 -> 455 bytes .../recipes/assembler_matrix_upload_core.json | 11 ++ 19 files changed, 243 insertions(+), 4 deletions(-) create mode 100644 build/generated/sources/modMetadata/META-INF/neoforge.mods.toml 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 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 create mode 100644 src/main/resources/assets/extendedae_plus/textures/item/channel_card.png create mode 100644 src/main/resources/data/extendedae_plus/recipes/assembler_matrix_upload_core.json diff --git a/build.gradle b/build.gradle index 3dd7ba2..cbb9f40 100644 --- a/build.gradle +++ b/build.gradle @@ -139,7 +139,7 @@ dependencies { implementation "curse.maven:curios-309927:6529130" compileOnly "curse.maven:ex-pattern-provider-892005:6863556" - compileOnly "curse.maven:applied-flux-965012:5614830" + implementation "curse.maven:applied-flux-965012:5614830" compileOnly "dev.emi:emi-neoforge:1.1.10+1.21" compileOnly "curse.maven:mega-cells-622112:6005043" compileOnly "curse.maven:jade-324717:5427817" diff --git a/build/generated/sources/modMetadata/META-INF/neoforge.mods.toml b/build/generated/sources/modMetadata/META-INF/neoforge.mods.toml new file mode 100644 index 0000000..1ad0ea3 --- /dev/null +++ b/build/generated/sources/modMetadata/META-INF/neoforge.mods.toml @@ -0,0 +1,103 @@ +# This is an example neoforge.mods.toml file. It contains the data relating to the loading mods. +# There are several mandatory fields (#mandatory), and many more that are optional (#optional). +# The overall format is standard TOML format, v0.5.0. +# Note that there are a couple of TOML lists in this file. +# Find more information on toml format here: https://github.com/toml-lang/toml + +# The name of the mod loader type to load - for regular FML @Mod mods it should be javafml +modLoader="javafml" #mandatory + +# A version range to match for said mod loader - for regular FML @Mod it will be the FML version. This is currently 2. +loaderVersion="[1,)" #mandatory + +# The license for you mod. This is mandatory metadata and allows for easier comprehension of your redistributive properties. +# Review your options at https://choosealicense.com/. All rights reserved is the default copyright stance, and is thus the default here. +license="All Rights Reserved" + +# A URL to refer people to when problems occur with this mod +#issueTrackerURL="https://change.me.to.your.issue.tracker.example.invalid/" #optional + +# A list of mods - how many allowed here is determined by the individual mod loader +[[mods]] #mandatory + +# The modid of the mod +modId="extendedae_plus" #mandatory + +# The version number of the mod +version="1.21.1-1.4.2" #mandatory + +# A display name for the mod +displayName="ExtendedAE-Plus" #mandatory + +# A URL to query for updates for this mod. See the JSON update specification https://docs.neoforged.net/docs/misc/updatechecker/ +#updateJSONURL="https://change.me.example.invalid/updates.json" #optional + +# A URL for the "homepage" for this mod, displayed in the mod UI +#displayURL="https://change.me.to.your.mods.homepage.example.invalid/" #optional + +# A file name (in the root of the mod JAR) containing a logo for display +#logoFile="examplemod.png" #optional + +# A text field displayed in the mod UI +#credits="" #optional + +# A text field displayed in the mod UI +authors="YourNameHere, OtherNameHere" #optional + +# The description text for the mod (multi line!) (#mandatory) +description='''Example mod description. +Newline characters can be used and will be replaced properly.''' + +# The [[mixins]] block allows you to declare your mixin config to FML so that it gets loaded. +[[mixins]] +config="extendedae_plus.mixins.json" + +# The [[accessTransformers]] block allows you to declare where your AT file is. +# If this block is omitted, a fallback attempt will be made to load an AT from META-INF/accesstransformer.cfg +#[[accessTransformers]] +#file="META-INF/accesstransformer.cfg" + +# The coremods config file path is not configurable and is always loaded from META-INF/coremods.json + +# A dependency - use the . to indicate dependency for a specific modid. Dependencies are optional. +[[dependencies.extendedae_plus]] #optional + # the modid of the dependency + modId="neoforge" #mandatory + # The type of the dependency. Can be one of "required", "optional", "incompatible" or "discouraged" (case insensitive). + # 'required' requires the mod to exist, 'optional' does not + # 'incompatible' will prevent the game from loading when the mod exists, and 'discouraged' will show a warning + type="required" #mandatory + # Optional field describing why the dependency is required or why it is incompatible + # reason="..." + # The version range of the dependency + versionRange="[21.1.1,)" #mandatory + # An ordering relationship for the dependency. + # BEFORE - This mod is loaded BEFORE the dependency + # AFTER - This mod is loaded AFTER the dependency + ordering="NONE" + # Side this dependency is applied on - BOTH, CLIENT, or SERVER + side="BOTH" + +# Here's another dependency +[[dependencies.extendedae_plus]] + modId="minecraft" + type="required" + # This version range declares a minimum of the current minecraft version up to but not including the next major version + versionRange="[1.21.1]" + ordering="NONE" + side="BOTH" + +# Require ExtendedAE (ExtendedAE-1.21-2.2.21-neoforge) to be present +[[dependencies.extendedae_plus]] + modId="extendedae" + type="required" + # Use a permissive range to tolerate upstream version string variations (e.g. 1.21-2.2.21-neoforge) + versionRange="*" + ordering="AFTER" + side="BOTH" + +# Features are specific properties of the game environment, that you may want to declare you require. This example declares +# that your mod requires GL version 3.2 or higher. Other features will be added. They are side aware so declaring this won't +# stop your mod loading on the server for example. +#[features.extendedae_plus] +#openGLVersion="[3.2,)" diff --git a/gradle.properties b/gradle.properties index ed7c9ca..ebafd76 100644 --- a/gradle.properties +++ b/gradle.properties @@ -32,7 +32,7 @@ mod_name=ExtendedAE-Plus # The license of the mod. Review your options at https://choosealicense.com/. All Rights Reserved is the default. mod_license=All Rights Reserved # The mod version. See https://semver.org/ -mod_version=1.21.1-1.4.0-fix +mod_version=1.21.1-1.4.2 # The group ID for the mod. It is only important when publishing as an artifact to a Maven repository. # This should match the base package used for the mod sources. # See https://maven.apache.org/guides/mini/guide-naming-conventions.html diff --git a/src/main/java/com/extendedae_plus/ExtendedAEPlus.java b/src/main/java/com/extendedae_plus/ExtendedAEPlus.java index 5a01c42..fa57422 100644 --- a/src/main/java/com/extendedae_plus/ExtendedAEPlus.java +++ b/src/main/java/com/extendedae_plus/ExtendedAEPlus.java @@ -110,8 +110,17 @@ public class ExtendedAEPlus { b256.setBlockEntity(CraftingBlockEntity.class, type, null, null); b1024.setBlockEntity(CraftingBlockEntity.class, type, null, null); LOGGER.info("Bound AE2 CraftingBlockEntity to ExtendedAE Plus accelerators."); + + // 绑定装配矩阵上传核心方块实体类型,避免 blockEntityClass 为 null 的问题 + ModBlocks.ASSEMBLER_MATRIX_UPLOAD_CORE.get().setBlockEntity( + com.extendedae_plus.content.matrix.UploadCoreBlockEntity.class, + ModBlockEntities.UPLOAD_CORE_BE.get(), + null, + null + ); + LOGGER.info("Bound UploadCoreBlockEntity to assembler matrix upload core block."); } catch (Throwable t) { - LOGGER.warn("Failed to bind CraftingBlockEntity to accelerators: {}", t.toString()); + LOGGER.warn("Failed to bind block entities: {}", t.toString()); } }); } 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..d4ec40c --- /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..8ca5ffb --- /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 9ba9b57..c3b9ed7 100644 --- a/src/main/java/com/extendedae_plus/init/ModBlockEntities.java +++ b/src/main/java/com/extendedae_plus/init/ModBlockEntities.java @@ -3,6 +3,7 @@ package com.extendedae_plus.init; import com.extendedae_plus.ExtendedAEPlus; import com.extendedae_plus.content.wireless.WirelessTransceiverBlockEntity; import com.extendedae_plus.content.controller.NetworkPatternControllerBlockEntity; +import com.extendedae_plus.content.matrix.UploadCoreBlockEntity; import appeng.blockentity.crafting.CraftingBlockEntity; import net.minecraft.world.level.block.entity.BlockEntityType; import net.minecraft.core.registries.Registries; @@ -42,4 +43,10 @@ public final class ModBlockEntities { ref.set(type); return type; }); + + // 装配矩阵上传核心方块实体 + public static final DeferredHolder, BlockEntityType> UPLOAD_CORE_BE = + BLOCK_ENTITY_TYPES.register("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 2944276..d1e116f 100644 --- a/src/main/java/com/extendedae_plus/init/ModBlocks.java +++ b/src/main/java/com/extendedae_plus/init/ModBlocks.java @@ -3,6 +3,7 @@ package com.extendedae_plus.init; import com.extendedae_plus.ExtendedAEPlus; import com.extendedae_plus.content.wireless.WirelessTransceiverBlock; import com.extendedae_plus.content.crafting.EPlusCraftingUnitType; +import com.extendedae_plus.content.matrix.UploadCoreBlock; import appeng.block.crafting.CraftingUnitBlock; import appeng.blockentity.crafting.CraftingBlockEntity; import appeng.core.definitions.AEBlockEntities; @@ -73,4 +74,15 @@ public final class ModBlocks { return new CraftingUnitBlock(EPlusCraftingUnitType.ACCELERATOR_1024x); } ); + + // 装配矩阵上传核心方块 + public static final DeferredBlock ASSEMBLER_MATRIX_UPLOAD_CORE = BLOCKS.register( + "assembler_matrix_upload_core", + () -> new UploadCoreBlock( + BlockBehaviour.Properties.of() + .mapColor(MapColor.METAL) + .strength(1.5F, 6.0F) + .requiresCorrectToolForDrops() + ) + ); } diff --git a/src/main/java/com/extendedae_plus/init/ModCreativeTabs.java b/src/main/java/com/extendedae_plus/init/ModCreativeTabs.java index 10d9144..2b5cb0b 100644 --- a/src/main/java/com/extendedae_plus/init/ModCreativeTabs.java +++ b/src/main/java/com/extendedae_plus/init/ModCreativeTabs.java @@ -23,6 +23,7 @@ public final class ModCreativeTabs { output.accept(ModItems.ACCELERATOR_64x.get()); output.accept(ModItems.ACCELERATOR_256x.get()); output.accept(ModItems.ACCELERATOR_1024x.get()); + output.accept(ModItems.ASSEMBLER_MATRIX_UPLOAD_CORE.get()); output.accept(ModItems.ENTITY_TICKER_PART_ITEM.get()); // 放入四个预设的 stacks(x2,x4,x8,x16),使用 ModItems 工厂创建 output.accept(ModItems.createEntitySpeedCardStack((byte) 2)); diff --git a/src/main/java/com/extendedae_plus/init/ModItems.java b/src/main/java/com/extendedae_plus/init/ModItems.java index e985c1e..8678005 100644 --- a/src/main/java/com/extendedae_plus/init/ModItems.java +++ b/src/main/java/com/extendedae_plus/init/ModItems.java @@ -68,4 +68,10 @@ public final class ModItems { public static ItemStack createEntitySpeedCardStack(byte multiplier) { return EntitySpeedCardItem.withMultiplier(multiplier); } + + // 装配矩阵上传核心物品 + public static final DeferredItem ASSEMBLER_MATRIX_UPLOAD_CORE = ITEMS.register( + "assembler_matrix_upload_core", + () -> new BlockItem(ModBlocks.ASSEMBLER_MATRIX_UPLOAD_CORE.get(), new Item.Properties()) + ); } diff --git a/src/main/java/com/extendedae_plus/util/ExtendedAEPatternUploadUtil.java b/src/main/java/com/extendedae_plus/util/ExtendedAEPatternUploadUtil.java index c49cf6f..020e3c0 100644 --- a/src/main/java/com/extendedae_plus/util/ExtendedAEPatternUploadUtil.java +++ b/src/main/java/com/extendedae_plus/util/ExtendedAEPatternUploadUtil.java @@ -525,7 +525,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); @@ -1180,4 +1180,26 @@ public class ExtendedAEPatternUploadUtil { } return false; } + + /** + * 判断给定矩阵集群中是否存在"装配矩阵上传核心"。 + * 要求:至少存在 1 个即可,不限制数量。 + * 传入任意属于该集群的 Tile(如 Pattern/Crafter/Frame 等)。 + */ + private static boolean clusterHasSingleUploadCore(com.glodblock.github.extendedae.common.tileentities.matrix.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++; + } + } + 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 babef8e..30340f1 100644 --- a/src/main/resources/assets/extendedae_plus/lang/en_us.json +++ b/src/main/resources/assets/extendedae_plus/lang/en_us.json @@ -17,6 +17,7 @@ "item.extendedae_plus.256x_crafting_accelerator": "256x Crafting Accelerator", "item.extendedae_plus.1024x_crafting_accelerator": "1024x Crafting Accelerator", "item.extendedae_plus.network_pattern_controller": "Pattern Provider Status Controller", + "item.extendedae_plus.assembler_matrix_upload_core": "Assembly Matrix Upload Core", "item.extendedae_plus.entity_speed_card": "Entity Speed Card", "item.extendedae_plus.entity_speed_card.x2": "Entity Speed Card (x2)", "item.extendedae_plus.entity_speed_card.x4": "Entity Speed Card (x4)", @@ -32,6 +33,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 Provider Status Controller", + "block.extendedae_plus.assembler_matrix_upload_core": "Assembly Matrix Upload Core", "extendedae_plus.upload_to_matrix": "Upload to Assembly Matrix", "extendedae_plus.upload_to_matrix.success": "Pattern Uploaded to Assembly Matrix", 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 8592356..b026489 100644 --- a/src/main/resources/assets/extendedae_plus/lang/zh_cn.json +++ b/src/main/resources/assets/extendedae_plus/lang/zh_cn.json @@ -17,6 +17,7 @@ "item.extendedae_plus.256x_crafting_accelerator": "256x并行处理单元", "item.extendedae_plus.1024x_crafting_accelerator": "1024x并行处理单元", "item.extendedae_plus.network_pattern_controller": "样板供应器状态控制器", + "item.extendedae_plus.assembler_matrix_upload_core": "装配矩阵上传核心", "item.extendedae_plus.entity_speed_card": "实体加速卡", "item.extendedae_plus.entity_speed_card.x2": "实体加速卡 (x2)", "item.extendedae_plus.entity_speed_card.x4": "实体加速卡 (x4)", @@ -32,6 +33,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": "样板已上传到装配矩阵", 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 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/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 0571efe2f908920af4ce4a59269fc6fcdfe1f027 Mon Sep 17 00:00:00 2001 From: GaLicn <133291877+GaLicn@users.noreply.github.com> Date: Wed, 24 Sep 2025 13:04:46 +0800 Subject: [PATCH 16/43] =?UTF-8?q?=E9=A2=91=E9=81=93=E5=8D=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ae/items/ChannelCardItem.java | 66 +++++ .../bridge/InterfaceWirelessLinkBridge.java | 50 ++++ .../extendedae_plus/init/ModCreativeTabs.java | 1 + .../com/extendedae_plus/init/ModItems.java | 7 + .../InterfaceLogicChannelCardMixin.java | 225 ++++++++++++++++++ .../automation/IOBusPartChannelCardMixin.java | 145 +++++++++++ .../StorageBusPartChannelCardMixin.java | 141 +++++++++++ .../endpoint/GenericNodeEndpointImpl.java | 49 ++++ .../endpoint/InterfaceNodeEndpointImpl.java | 49 ++++ .../assets/extendedae_plus/lang/en_us.json | 4 + .../assets/extendedae_plus/lang/zh_cn.json | 4 + .../models/item/channel_card.json | 6 + .../extendedae_plus/recipes/channel_card.json | 8 + .../resources/extendedae_plus.mixins.json | 3 + 14 files changed, 758 insertions(+) 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/parts/automation/IOBusPartChannelCardMixin.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/wireless/endpoint/GenericNodeEndpointImpl.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/data/extendedae_plus/recipes/channel_card.json 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..6a922c4 --- /dev/null +++ b/src/main/java/com/extendedae_plus/ae/items/ChannelCardItem.java @@ -0,0 +1,66 @@ +package com.extendedae_plus.ae.items; + +import appeng.items.materials.UpgradeCardItem; +import net.minecraft.core.component.DataComponents; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.network.chat.Component; +import net.minecraft.world.item.component.CustomData; +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.getOrDefault(DataComponents.CUSTOM_DATA, CustomData.EMPTY).copyTag(); + tag.putLong(TAG_CHANNEL, channel); + stack.set(DataComponents.CUSTOM_DATA, CustomData.of(tag)); + } + + public static long getChannel(ItemStack stack) { + CompoundTag tag = stack.getOrDefault(DataComponents.CUSTOM_DATA, CustomData.EMPTY).copyTag(); + return tag.contains(TAG_CHANNEL) ? tag.getLong(TAG_CHANNEL) : 0L; + } + + @Override + public void appendHoverText(ItemStack stack, Item.TooltipContext context, List lines, TooltipFlag flag) { + super.appendHoverText(stack, context, 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..d200f77 --- /dev/null +++ b/src/main/java/com/extendedae_plus/bridge/InterfaceWirelessLinkBridge.java @@ -0,0 +1,50 @@ +package com.extendedae_plus.bridge; + +/** + * 非 mixin 包下的桥接接口,供 mixin 进行 instanceof 检测和回调。 + */ +public interface InterfaceWirelessLinkBridge { + void eap$updateWirelessLink(); + + /** + * 获取无线连接状态(服务端返回真实状态,客户端返回同步状态) + */ + default boolean eap$isWirelessConnected() { + return false; + } + + /** + * 设置客户端的无线连接状态(仅在客户端使用) + */ + 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/init/ModCreativeTabs.java b/src/main/java/com/extendedae_plus/init/ModCreativeTabs.java index 2b5cb0b..77d059a 100644 --- a/src/main/java/com/extendedae_plus/init/ModCreativeTabs.java +++ b/src/main/java/com/extendedae_plus/init/ModCreativeTabs.java @@ -24,6 +24,7 @@ public final class ModCreativeTabs { output.accept(ModItems.ACCELERATOR_256x.get()); output.accept(ModItems.ACCELERATOR_1024x.get()); output.accept(ModItems.ASSEMBLER_MATRIX_UPLOAD_CORE.get()); + output.accept(ModItems.CHANNEL_CARD.get()); output.accept(ModItems.ENTITY_TICKER_PART_ITEM.get()); // 放入四个预设的 stacks(x2,x4,x8,x16),使用 ModItems 工厂创建 output.accept(ModItems.createEntitySpeedCardStack((byte) 2)); diff --git a/src/main/java/com/extendedae_plus/init/ModItems.java b/src/main/java/com/extendedae_plus/init/ModItems.java index 8678005..5189c54 100644 --- a/src/main/java/com/extendedae_plus/init/ModItems.java +++ b/src/main/java/com/extendedae_plus/init/ModItems.java @@ -3,6 +3,7 @@ package com.extendedae_plus.init; import com.extendedae_plus.ExtendedAEPlus; import com.extendedae_plus.ae.definitions.upgrades.EntitySpeedCardItem; import com.extendedae_plus.ae.items.EntitySpeedTickerPartItem; +import com.extendedae_plus.ae.items.ChannelCardItem; import net.minecraft.world.item.BlockItem; import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; @@ -62,6 +63,12 @@ public final class ModItems { () -> new EntitySpeedCardItem(new Item.Properties()) ); + // 频道卡:用于AE机器的无线频道连接 + public static final DeferredItem CHANNEL_CARD = ITEMS.register( + "channel_card", + () -> new ChannelCardItem(new Item.Properties()) + ); + /** * 工厂:创建带 multiplier 的实体加速卡 ItemStack(2/4/8/16) */ 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..15fd6f3 --- /dev/null +++ b/src/main/java/com/extendedae_plus/mixin/ae2/helpers/InterfaceLogicChannelCardMixin.java @@ -0,0 +1,225 @@ +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.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; + + @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 { + // InterfaceLogicChannelCardMixin 已加载 + } + + @Inject(method = "onUpgradesChanged", at = @At("TAIL"), remap = false) + private void eap$onUpgradesChangedTail(CallbackInfo ci) { + // 升级变更时重置标志并尝试初始化 + eap$lastChannel = -1; + eap$hasInitialized = false; + eap$initializeChannelLink(); + } + + @Inject(method = "gridChanged", at = @At("TAIL"), remap = false) + private void eap$afterGridChanged(CallbackInfo ci) { + // 网格状态变化时重置标志并设置延迟初始化 + 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 ignored) { + } + }); + } + } + + @Inject(method = "readFromNBT", at = @At("TAIL"), remap = false) + private void eap$afterReadNBT(net.minecraft.nbt.CompoundTag tag, net.minecraft.core.HolderLookup.Provider registries, CallbackInfo ci) { + // 从 NBT加载时重置标志 + eap$lastChannel = -1; + eap$hasInitialized = false; + // 直接尝试初始化 + eap$initializeChannelLink(); + } + + @Inject(method = "clearContent", at = @At("HEAD"), remap = false) + private void eap$onClearContent(CallbackInfo ci) { + if (eap$link != null) { + eap$link.onUnloadOrRemove(); + } + } + + @Unique + public void eap$initializeChannelLink() { + // 仅在服务端执行,避免在渲染线程/客户端触发任何初始化路径 + if (host.getBlockEntity() != null && host.getBlockEntity().getLevel() != null && host.getBlockEntity().getLevel().isClientSide) { + return; + } + + // 避免重复初始化 + if (eap$hasInitialized) { + return; + } + + // 检查网格节点是否在线(1.21.1版本使用isActive替代hasGridBooted) + if (mainNode == null || !mainNode.isActive()) { + return; + } + + try { + long channel = 0L; + boolean found = false; + for (ItemStack stack : getUpgrades()) { + 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 InterfaceNodeEndpointImpl(host, () -> this.mainNode.getNode()); + eap$link = new WirelessSlaveLink(endpoint); + } + + eap$link.setFrequency(channel); + eap$link.updateStatus(); + + if (eap$link.isConnected()) { + eap$hasInitialized = true; // 设置初始化完成标志 + } else { + // 不标记为完成,允许后续tick重试 + eap$hasInitialized = false; + // 设置一个短延迟窗口,避免每tick刷屏 + 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$updateWirelessLink() { + if (eap$link != null) { + 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 == null || !mainNode.isActive()) { + // 仍在引导,消耗计时器 + 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 ignored) { + } + }); + } catch (Throwable ignored) { + } + } + } else { + // 网格已引导完成,执行初始化 + eap$initializeChannelLink(); + } + } + } + + // eap$initializeChannelLink方法已在上面实现 +} 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..8f15d70 --- /dev/null +++ b/src/main/java/com/extendedae_plus/mixin/ae2/parts/automation/IOBusPartChannelCardMixin.java @@ -0,0 +1,145 @@ +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 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; +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 eap$link; + + @Unique + private long eap$lastChannel = -1; + + @Unique + private boolean eap$clientConnected = false; + + @Unique + private boolean eap$hasTickInitialized = false; + + @Inject(method = "upgradesChanged", at = @At("TAIL")) + private void eap$onUpgradesChanged(CallbackInfo ci) { + // 只在服务端初始化频道链接 + if (!((appeng.parts.AEBasePart)(Object)this).isClientSide()) { + eap$initializeChannelLink(); + } + } + + @Inject(method = "tickingRequest", at = @At("HEAD")) + private void eap$beforeTick(appeng.api.networking.IGridNode node, int ticksSinceLastCall, org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable cir) { + // 在第一次tick时初始化频道链接(此时网格节点已经在线) + if (!eap$hasTickInitialized && !((appeng.parts.AEBasePart)(Object)this).isClientSide()) { + eap$hasTickInitialized = true; + eap$initializeChannelLink(); + } + } + + @Inject(method = "readFromNBT", at = @At("TAIL")) + private void eap$afterReadFromNBT(CompoundTag extra, net.minecraft.core.HolderLookup.Provider registries, CallbackInfo ci) { + // 从NBT加载时重置频道缓存和tick初始化标志 + if (!((appeng.parts.AEBasePart)(Object)this).isClientSide()) { + eap$lastChannel = -1; + eap$hasTickInitialized = false; // 重置标志,允许再次初始化 + } + } + + @Unique + public void eap$initializeChannelLink() { + // 防止重复调用 + if (((appeng.parts.AEBasePart)(Object)this).isClientSide()) { + return; + } + + 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 (eap$lastChannel == channel) { + return; + } + eap$lastChannel = channel; + + ExtendedAELogger.LOGGER.debug("[服务端] IOBus 初始化频道链接: found={}, channel={}", found, channel); + + if (!found) { + // 无频道卡则断开 + if (eap$link != null) { + eap$link.setFrequency(0L); + eap$link.updateStatus(); + ExtendedAELogger.LOGGER.debug("[服务端] IOBus 断开频道链接"); + // 立即通知客户端状态变化(断开连接无需延迟) + ((appeng.parts.AEBasePart)(Object)this).getHost().markForUpdate(); + } + return; + } + + if (eap$link == null) { + var endpoint = new GenericNodeEndpointImpl( + () -> ((appeng.parts.AEBasePart)(Object)this).getHost().getBlockEntity(), + () -> ((IActionHost)(Object)this).getActionableNode() + ); + eap$link = new WirelessSlaveLink(endpoint); + ExtendedAELogger.LOGGER.debug("[服务端] IOBus 创建新的无线链接"); + } + + eap$link.setFrequency(channel); + eap$link.updateStatus(); + ExtendedAELogger.LOGGER.debug("[服务端] IOBus 设置频道: {}, 连接状态: {}", channel, eap$link.isConnected()); + + // 通知客户端状态变化 + ((appeng.parts.AEBasePart)(Object)this).getHost().markForUpdate(); + } catch (Exception e) { + ExtendedAELogger.LOGGER.error("[服务端] IOBus 初始化频道链接失败", e); + } + } + + @Override + public void eap$updateWirelessLink() { + if (eap$link != null) { + eap$link.updateStatus(); + } + } + + @Override + public boolean eap$isWirelessConnected() { + if (((appeng.parts.AEBasePart)(Object)this).isClientSide()) { + return eap$clientConnected; + } else { + return eap$link != null && eap$link.isConnected(); + } + } + + @Override + public void eap$setClientWirelessState(boolean connected) { + eap$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 new file mode 100644 index 0000000..54a0bdb --- /dev/null +++ b/src/main/java/com/extendedae_plus/mixin/ae2/parts/storagebus/StorageBusPartChannelCardMixin.java @@ -0,0 +1,141 @@ +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; +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 eap$link; + + @Unique + private long eap$lastChannel = -1; + + @Unique + private boolean eap$clientConnected = false; + + @Inject(method = "upgradesChanged", at = @At("TAIL")) + private void eap$onUpgradesChanged(CallbackInfo ci) { + // 只在服务端初始化频道链接 + if (!((appeng.parts.AEBasePart)(Object)this).isClientSide()) { + eap$initializeChannelLink(); + } + } + + @Inject(method = "onMainNodeStateChanged", at = @At("TAIL")) + private void eap$onMainNodeStateChanged(IGridNodeListener.State reason, CallbackInfo ci) { + // 在节点状态变化时(包括加载后的GRID_BOOT)重新初始化频道链接 + if (reason == IGridNodeListener.State.GRID_BOOT && !((appeng.parts.AEBasePart)(Object)this).isClientSide()) { + eap$initializeChannelLink(); + } + } + + @Inject(method = "readFromNBT", at = @At("TAIL")) + private void eap$afterReadFromNBT(CompoundTag extra, net.minecraft.core.HolderLookup.Provider registries, CallbackInfo ci) { + // 从NBT加载后也重新初始化频道链接(只在服务端) + if (!((appeng.parts.AEBasePart)(Object)this).isClientSide()) { + // 从NBT加载时重置频道缓存,强制重新初始化 + eap$lastChannel = -1; + eap$initializeChannelLink(); + } + } + + @Unique + public void eap$initializeChannelLink() { + // 防止重复调用 + if (((appeng.parts.AEBasePart)(Object)this).isClientSide()) { + return; + } + + 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 (eap$lastChannel == channel) { + return; + } + eap$lastChannel = channel; + + ExtendedAELogger.LOGGER.debug("[服务端] StorageBus 初始化频道链接: found={}, channel={}", found, channel); + + if (!found) { + if (eap$link != null) { + eap$link.setFrequency(0L); + eap$link.updateStatus(); + ExtendedAELogger.LOGGER.debug("[服务端] StorageBus 断开频道链接"); + // 通知客户端状态变化 + ((appeng.parts.AEBasePart)(Object)this).getHost().markForUpdate(); + } + return; + } + + if (eap$link == null) { + var endpoint = new GenericNodeEndpointImpl( + () -> ((appeng.parts.AEBasePart)(Object)this).getHost().getBlockEntity(), + () -> ((IActionHost)(Object)this).getActionableNode() + ); + eap$link = new WirelessSlaveLink(endpoint); + ExtendedAELogger.LOGGER.debug("[服务端] StorageBus 创建新的无线链接"); + } + + eap$link.setFrequency(channel); + eap$link.updateStatus(); + ExtendedAELogger.LOGGER.debug("[服务端] StorageBus 设置频道: {}, 连接状态: {}", channel, eap$link.isConnected()); + + // 通知客户端状态变化 + ((appeng.parts.AEBasePart)(Object)this).getHost().markForUpdate(); + } catch (Exception e) { + ExtendedAELogger.LOGGER.error("[服务端] StorageBus 初始化频道链接失败", e); + } + } + + @Override + public void eap$updateWirelessLink() { + if (eap$link != null) { + eap$link.updateStatus(); + } + } + + @Override + public boolean eap$isWirelessConnected() { + if (((appeng.parts.AEBasePart)(Object)this).isClientSide()) { + return eap$clientConnected; + } else { + return eap$link != null && eap$link.isConnected(); + } + } + + @Override + public void eap$setClientWirelessState(boolean connected) { + eap$clientConnected = connected; + } +} 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/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 30340f1..764c9ae 100644 --- a/src/main/resources/assets/extendedae_plus/lang/en_us.json +++ b/src/main/resources/assets/extendedae_plus/lang/en_us.json @@ -18,6 +18,10 @@ "item.extendedae_plus.1024x_crafting_accelerator": "1024x Crafting Accelerator", "item.extendedae_plus.network_pattern_controller": "Pattern Provider Status Controller", "item.extendedae_plus.assembler_matrix_upload_core": "Assembly Matrix Upload Core", + "item.extendedae_plus.channel_card": "Channel Card", + "item.extendedae_plus.channel_card.channel": "Channel: %d", + "item.extendedae_plus.channel_card.channel.unset": "Channel: Unset", + "item.extendedae_plus.channel_card.set": "Channel set to: %d", "item.extendedae_plus.entity_speed_card": "Entity Speed Card", "item.extendedae_plus.entity_speed_card.x2": "Entity Speed Card (x2)", "item.extendedae_plus.entity_speed_card.x4": "Entity Speed Card (x4)", 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 b026489..e7cb381 100644 --- a/src/main/resources/assets/extendedae_plus/lang/zh_cn.json +++ b/src/main/resources/assets/extendedae_plus/lang/zh_cn.json @@ -18,6 +18,10 @@ "item.extendedae_plus.1024x_crafting_accelerator": "1024x并行处理单元", "item.extendedae_plus.network_pattern_controller": "样板供应器状态控制器", "item.extendedae_plus.assembler_matrix_upload_core": "装配矩阵上传核心", + "item.extendedae_plus.channel_card": "频道卡", + "item.extendedae_plus.channel_card.channel": "频道: %d", + "item.extendedae_plus.channel_card.channel.unset": "频道: 未设置", + "item.extendedae_plus.channel_card.set": "频道已设置为: %d", "item.extendedae_plus.entity_speed_card": "实体加速卡", "item.extendedae_plus.entity_speed_card.x2": "实体加速卡 (x2)", "item.extendedae_plus.entity_speed_card.x4": "实体加速卡 (x4)", 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/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 } +} diff --git a/src/main/resources/extendedae_plus.mixins.json b/src/main/resources/extendedae_plus.mixins.json index 034dc6d..1e725ba 100644 --- a/src/main/resources/extendedae_plus.mixins.json +++ b/src/main/resources/extendedae_plus.mixins.json @@ -25,8 +25,11 @@ "ae2.autopattern.CraftingTreeNodeMixin", "ae2.autopattern.CraftingTreeProcessMixin", "ae2.autopattern.PatternProviderLogicContainsRedirectMixin", + "ae2.helpers.InterfaceLogicChannelCardMixin", "ae2.helpers.PatternProviderLogicAdvancedMixin", "ae2.helpers.PatternProviderLogicDoublingMixin", + "ae2.parts.automation.IOBusPartChannelCardMixin", + "ae2.parts.storagebus.StorageBusPartChannelCardMixin", "ae2.menu.ContainerPatternEncodingTermMenuMixin", "ae2.menu.MEStorageMenuMixin", "ae2.menu.PatternEncodingTermMenuMixin", From 56dcdad91f93bc8b265a04e2ec9dbd531b76d807 Mon Sep 17 00:00:00 2001 From: GaLicn <133291877+GaLicn@users.noreply.github.com> Date: Wed, 24 Sep 2025 15:10:43 +0800 Subject: [PATCH 17/43] =?UTF-8?q?=E9=A2=91=E9=81=93=E5=8D=A1=E9=80=BB?= =?UTF-8?q?=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../extendedae_plus/init/UpgradeCards.java | 20 +++++++++ .../helpers/InterfaceLogicTickerMixin.java | 42 +++++++++++++++++++ .../resources/extendedae_plus.mixins.json | 1 + 3 files changed, 63 insertions(+) create mode 100644 src/main/java/com/extendedae_plus/mixin/ae2/helpers/InterfaceLogicTickerMixin.java diff --git a/src/main/java/com/extendedae_plus/init/UpgradeCards.java b/src/main/java/com/extendedae_plus/init/UpgradeCards.java index 9a8de67..0816756 100644 --- a/src/main/java/com/extendedae_plus/init/UpgradeCards.java +++ b/src/main/java/com/extendedae_plus/init/UpgradeCards.java @@ -1,7 +1,10 @@ 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.neoforged.fml.event.lifecycle.FMLCommonSetupEvent; @@ -12,6 +15,23 @@ 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"); + + // 频道卡:对齐旧版注册 — AE2 接口(方块与部件) + 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); + + // 频道卡:AE2 样板供应器(方块与部件) + 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); + + // 频道卡:AE2 I/O 总线与存储总线(部件) + 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, storageGroup); }); } } \ No newline at end of file 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..86f561c --- /dev/null +++ b/src/main/java/com/extendedae_plus/mixin/ae2/helpers/InterfaceLogicTickerMixin.java @@ -0,0 +1,42 @@ +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("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) { + if (this$0 instanceof InterfaceWirelessLinkBridge bridge) { + bridge.eap$updateWirelessLink(); + } + } +} diff --git a/src/main/resources/extendedae_plus.mixins.json b/src/main/resources/extendedae_plus.mixins.json index 1e725ba..b562441 100644 --- a/src/main/resources/extendedae_plus.mixins.json +++ b/src/main/resources/extendedae_plus.mixins.json @@ -26,6 +26,7 @@ "ae2.autopattern.CraftingTreeProcessMixin", "ae2.autopattern.PatternProviderLogicContainsRedirectMixin", "ae2.helpers.InterfaceLogicChannelCardMixin", + "ae2.helpers.InterfaceLogicTickerMixin", "ae2.helpers.PatternProviderLogicAdvancedMixin", "ae2.helpers.PatternProviderLogicDoublingMixin", "ae2.parts.automation.IOBusPartChannelCardMixin", From 5bd5df3468ef702c20648f87610c91361b68f2ca Mon Sep 17 00:00:00 2001 From: GaLicn <133291877+GaLicn@users.noreply.github.com> Date: Wed, 24 Sep 2025 15:26:18 +0800 Subject: [PATCH 18/43] =?UTF-8?q?me=E6=8E=A5=E5=8F=A3=E9=A2=91=E9=81=93?= =?UTF-8?q?=E5=8D=A1=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../InterfaceLogicChannelCardMixin.java | 30 +++++++++++++++++-- .../wireless/WirelessSlaveLink.java | 16 +++++++++- 2 files changed, 42 insertions(+), 4 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 15fd6f3..41eb689 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 @@ -8,6 +8,7 @@ import com.extendedae_plus.bridge.InterfaceWirelessLinkBridge; import com.extendedae_plus.init.ModItems; import com.extendedae_plus.wireless.WirelessSlaveLink; import com.extendedae_plus.wireless.endpoint.InterfaceNodeEndpointImpl; +import com.extendedae_plus.util.ExtendedAELogger; import net.minecraft.server.level.ServerLevel; import net.minecraft.world.item.ItemStack; import org.spongepowered.asm.mixin.Mixin; @@ -50,6 +51,7 @@ public abstract class InterfaceLogicChannelCardMixin implements InterfaceWireles @Inject(method = "onUpgradesChanged", at = @At("TAIL"), remap = false) private void eap$onUpgradesChangedTail(CallbackInfo ci) { // 升级变更时重置标志并尝试初始化 + ExtendedAELogger.LOGGER.debug("[服务端] Interface onUpgradesChanged -> 重置初始化状态"); eap$lastChannel = -1; eap$hasInitialized = false; eap$initializeChannelLink(); @@ -58,6 +60,7 @@ 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,等待网格完成引导 @@ -100,8 +103,9 @@ public abstract class InterfaceLogicChannelCardMixin implements InterfaceWireles return; } - // 检查网格节点是否在线(1.21.1版本使用isActive替代hasGridBooted) - if (mainNode == null || !mainNode.isActive()) { + // 仅要求节点对象可用;不要依赖 isActive(无线连接本身会建立连接与激活节点) + if (mainNode == null || mainNode.getNode() == null) { + ExtendedAELogger.LOGGER.debug("[服务端] Interface 初始化频道链接跳过:mainNode 或其 Node 不可用"); return; } @@ -121,18 +125,36 @@ public abstract class InterfaceLogicChannelCardMixin implements InterfaceWireles if (eap$link != null) { eap$link.setFrequency(0L); eap$link.updateStatus(); + ExtendedAELogger.LOGGER.debug("[服务端] Interface 无频道卡 -> 断开无线链接"); } eap$hasInitialized = true; + // 保存一次状态 + try { host.saveChanges(); } catch (Throwable ignored) {} + // 唤醒设备以刷新客户端/邻居 + try { + mainNode.ifPresent((grid, node) -> { + try { grid.getTickManager().wakeDevice(node); } catch (Throwable ignored2) {} + }); + } catch (Throwable ignored2) {} return; } if (eap$link == null) { var endpoint = new InterfaceNodeEndpointImpl(host, () -> this.mainNode.getNode()); eap$link = new WirelessSlaveLink(endpoint); + ExtendedAELogger.LOGGER.debug("[服务端] Interface 创建新的无线链接实例"); } eap$link.setFrequency(channel); eap$link.updateStatus(); + ExtendedAELogger.LOGGER.debug("[服务端] Interface 设置频道: {} 连接状态: {}", channel, eap$link.isConnected()); + try { host.saveChanges(); } catch (Throwable ignored) {} + // 唤醒设备,加速后续 tick 以完成连接 + try { + mainNode.ifPresent((grid, node) -> { + try { grid.getTickManager().wakeDevice(node); } catch (Throwable ignored2) {} + }); + } catch (Throwable ignored2) {} if (eap$link.isConnected()) { eap$hasInitialized = true; // 设置初始化完成标志 @@ -196,7 +218,8 @@ public abstract class InterfaceLogicChannelCardMixin implements InterfaceWireles // 若尚未初始化,则持续尝试,直到网格完成引导 if (!eap$hasInitialized) { - if (mainNode == null || !mainNode.isActive()) { + // 若节点对象尚未就绪,则等待;无需等待 isActive(无线接入后会激活) + if (mainNode == null || mainNode.getNode() == null) { // 仍在引导,消耗计时器 if (eap$delayedInitTicks > 0) { eap$delayedInitTicks--; @@ -216,6 +239,7 @@ public abstract class InterfaceLogicChannelCardMixin implements InterfaceWireles } } else { // 网格已引导完成,执行初始化 + ExtendedAELogger.LOGGER.debug("[服务端] Interface 延迟初始化触发 -> 开始初始化频道链接"); eap$initializeChannelLink(); } } diff --git a/src/main/java/com/extendedae_plus/wireless/WirelessSlaveLink.java b/src/main/java/com/extendedae_plus/wireless/WirelessSlaveLink.java index cc8988a..134d014 100644 --- a/src/main/java/com/extendedae_plus/wireless/WirelessSlaveLink.java +++ b/src/main/java/com/extendedae_plus/wireless/WirelessSlaveLink.java @@ -4,6 +4,7 @@ import appeng.api.networking.GridHelper; import appeng.api.networking.IGridNode; import appeng.me.service.helpers.ConnectionWrapper; import com.extendedae_plus.config.ModConfigs; +import com.extendedae_plus.util.ExtendedAELogger; import net.minecraft.server.level.ServerLevel; import java.util.Objects; @@ -51,16 +52,19 @@ public class WirelessSlaveLink { */ public void updateStatus() { if (host.isEndpointRemoved()) { + ExtendedAELogger.LOGGER.debug("[无线] 端点已移除或无效,销毁连接"); destroyConnection(); return; } final ServerLevel level = host.getServerLevel(); if (level == null || frequency == 0L) { + ExtendedAELogger.LOGGER.debug("[无线] 环境不满足:level={}, freq={}", level, frequency); destroyConnection(); return; } IWirelessEndpoint master = WirelessMasterRegistry.get(level, frequency); + ExtendedAELogger.LOGGER.debug("[无线] 查找主站: level={}, freq={} -> {}", level.dimension(), frequency, master); shutdown = false; distance = 0.0D; @@ -68,6 +72,7 @@ public class WirelessSlaveLink { if (master != null && !master.isEndpointRemoved() && (crossDim || master.getServerLevel() == level)) { if (!crossDim) { distance = Math.sqrt(master.getBlockPos().distSqr(host.getBlockPos())); + ExtendedAELogger.LOGGER.debug("[无线] 同维度距离={}, 最大距离={}", distance, ModConfigs.WIRELESS_MAX_RANGE.get()); } double maxRange = ModConfigs.WIRELESS_MAX_RANGE.get(); if (crossDim || distance <= maxRange) { @@ -76,6 +81,8 @@ public class WirelessSlaveLink { var current = connection.getConnection(); IGridNode a = host.getGridNode(); // 从端 IGridNode b = master.getGridNode(); // 主端 + if (a == null) { ExtendedAELogger.LOGGER.debug("[无线] 从端节点为 null,无法连接"); } + if (b == null) { ExtendedAELogger.LOGGER.debug("[无线] 主端节点为 null,无法连接"); } if (a == null || b == null) { shutdown = true; } else { @@ -84,22 +91,29 @@ public class WirelessSlaveLink { var ca = current.a(); var cb = current.b(); if ((ca == a || cb == a) && (ca == b || cb == b)) { + ExtendedAELogger.LOGGER.debug("[无线] 连接已存在且目标一致,保持"); return; // 连接已正确 } // 否则先断开,再重建 + ExtendedAELogger.LOGGER.debug("[无线] 连接目标变化,先销毁再重建"); current.destroy(); connection = new ConnectionWrapper(null); } + ExtendedAELogger.LOGGER.debug("[无线] 创建连接: a={}, b={}", a, b); connection = new ConnectionWrapper(GridHelper.createConnection(a, b)); + ExtendedAELogger.LOGGER.debug("[无线] 连接创建完成: {}", connection.getConnection()); return; } - } catch (IllegalStateException ignore) { + } catch (IllegalStateException ex) { // 连接非法(如重复连接等)——落入重建/关闭逻辑 + ExtendedAELogger.LOGGER.debug("[无线] 连接创建异常: {}", ex.toString()); } } else { + ExtendedAELogger.LOGGER.debug("[无线] 超出范围:{} > {},关闭连接", distance, maxRange); shutdown = true; // 超出范围 } } else { + ExtendedAELogger.LOGGER.debug("[无线] 无可用主站或跨维度不允许,关闭连接"); shutdown = true; // 无主或主端不可用 } From 5cf2aa991f7c5452cfe8555cc85ea6708d832ff6 Mon Sep 17 00:00:00 2001 From: GaLicn <133291877+GaLicn@users.noreply.github.com> Date: Wed, 24 Sep 2025 20:52:35 +0800 Subject: [PATCH 19/43] =?UTF-8?q?=E6=A0=B7=E6=9D=BF=E4=BE=9B=E5=BA=94?= =?UTF-8?q?=E5=99=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bridge/CompatUpgradeProvider.java | 10 + .../bridge/IUpgradableMenu.java | 12 + .../compat/UpgradeSlotCompat.java | 38 +++ .../PatternProviderScreenUpgradesMixin.java | 67 +++++ .../PatternProviderLogicCompatMixin.java | 280 ++++++++++++++++++ .../PatternProviderLogicTickerMixin.java | 42 +++ .../PatternProviderMenuUpgradesMixin.java | 54 ++++ .../resources/extendedae_plus.mixins.json | 3 + 8 files changed, 506 insertions(+) create mode 100644 src/main/java/com/extendedae_plus/bridge/CompatUpgradeProvider.java create mode 100644 src/main/java/com/extendedae_plus/bridge/IUpgradableMenu.java create mode 100644 src/main/java/com/extendedae_plus/compat/UpgradeSlotCompat.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/compat/PatternProviderLogicCompatMixin.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/menu/PatternProviderMenuUpgradesMixin.java diff --git a/src/main/java/com/extendedae_plus/bridge/CompatUpgradeProvider.java b/src/main/java/com/extendedae_plus/bridge/CompatUpgradeProvider.java new file mode 100644 index 0000000..a221c7e --- /dev/null +++ b/src/main/java/com/extendedae_plus/bridge/CompatUpgradeProvider.java @@ -0,0 +1,10 @@ +package com.extendedae_plus.bridge; + +import appeng.api.upgrades.IUpgradeInventory; + +/** + * 仅用于我方在未安装 appflux 时向逻辑类暴露自带升级槽,避免与 appflux 的 IUpgradeableObject 冲突。 + */ +public interface CompatUpgradeProvider { + IUpgradeInventory eap$getCompatUpgrades(); +} 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..9599172 --- /dev/null +++ b/src/main/java/com/extendedae_plus/bridge/IUpgradableMenu.java @@ -0,0 +1,12 @@ +package com.extendedae_plus.bridge; + +import appeng.menu.ToolboxMenu; + +/** + * 提供给 AE2 菜单以暴露升级槽与工具箱面板的接口。 + * PatternProviderMenu 的 mixin 会在未安装 appflux 时实现该接口, + * 以便在界面上显示升级卡槽(用于放置频道卡)。 + */ +public interface IUpgradableMenu { + ToolboxMenu getToolbox(); +} 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..0c9f191 --- /dev/null +++ b/src/main/java/com/extendedae_plus/compat/UpgradeSlotCompat.java @@ -0,0 +1,38 @@ +package com.extendedae_plus.compat; + +import net.neoforged.fml.loading.FMLLoader; +import net.neoforged.fml.ModList; + +public final class UpgradeSlotCompat { + private static Boolean APPFLUX_LOADED; + + private UpgradeSlotCompat() {} + + private static boolean isAppfluxLoaded() { + if (APPFLUX_LOADED == null) { + try { + APPFLUX_LOADED = ModList.get().isLoaded("appflux"); + } catch (Throwable t) { + // 早期阶段或运行环境差异 + APPFLUX_LOADED = false; + } + } + return APPFLUX_LOADED; + } + + // 是否由我们提供升级槽(当未安装 appflux 时) + public static boolean shouldEnableUpgradeSlots() { + return !isAppfluxLoaded(); + } + + // 是否启用频道卡支持(两种情况下都启用) + public static boolean shouldEnableChannelCard() { + return true; + } + + // 客户端界面是否需要显示升级面板(装/不装 appflux 均显示; + // appflux 提供的升级槽会以 SlotSemantics.UPGRADE 出现在菜单中,我们只负责渲染面板) + public static boolean shouldAddUpgradePanelToScreen() { + return true; + } +} 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..d8b60fb --- /dev/null +++ b/src/main/java/com/extendedae_plus/mixin/ae2/client/gui/PatternProviderScreenUpgradesMixin.java @@ -0,0 +1,67 @@ +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 appeng.helpers.patternprovider.PatternProviderLogicHost; +import appeng.menu.AEBaseMenu; +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; + +@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; + } + try { + this.widgets.add("upgrades", new UpgradesPanel( + menu.getSlots(SlotSemantics.UPGRADE), + this::eap$getCompatibleUpgrades)); + } catch (IllegalStateException already) { + // 已存在同名面板(可能由 AE2 或其他模组添加),忽略 + com.extendedae_plus.util.ExtendedAELogger.LOGGER.debug("[样板供应器][界面] 升级面板已存在,跳过添加"); + } + if (menu instanceof AEBaseMenu base && base instanceof com.extendedae_plus.bridge.IUpgradableMenu upg && upg.getToolbox() != null && upg.getToolbox().isPresent()) { + try { + this.widgets.add("toolbox", new ToolboxPanel(style, upg.getToolbox().getName())); + } catch (IllegalStateException already) { + com.extendedae_plus.util.ExtendedAELogger.LOGGER.debug("[样板供应器][界面] 工具箱面板已存在,跳过添加"); + } + } + } + + @Unique + private List eap$getCompatibleUpgrades() { + var list = new ArrayList(); + list.add(GuiText.CompatibleUpgrades.text()); + if (menu instanceof AEBaseMenu base) { + var target = base.getTarget(); + if (target instanceof PatternProviderLogicHost host) { + list.addAll(Upgrades.getTooltipLinesForMachine(host.getTerminalIcon().getItem())); + } + } + 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/compat/PatternProviderLogicCompatMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/compat/PatternProviderLogicCompatMixin.java new file mode 100644 index 0000000..d9a530b --- /dev/null +++ b/src/main/java/com/extendedae_plus/mixin/ae2/compat/PatternProviderLogicCompatMixin.java @@ -0,0 +1,280 @@ +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.bridge.CompatUpgradeProvider; +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.util.ExtendedAELogger; +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; + +/** + * 样板供应器频道卡兼容实现: + * - 未安装 appflux 时,提供 1 个升级槽并读取频道卡; + * - 安装 appflux 时,优先从 appflux 提供的升级槽读取频道卡; + * - 建立到无线主站的网格连接。 + */ +@Mixin(value = PatternProviderLogic.class, remap = false) +public abstract class PatternProviderLogicCompatMixin implements CompatUpgradeProvider, 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; + + @Inject(method = "(Lappeng/api/networking/IManagedGridNode;Lappeng/helpers/patternprovider/PatternProviderLogicHost;I)V", + at = @At("TAIL")) + private void eap$compatInit(IManagedGridNode mainNode, PatternProviderLogicHost host, int size, CallbackInfo ci) { + try { + if (UpgradeSlotCompat.shouldEnableUpgradeSlots()) { + this.eap$compatUpgrades = UpgradeInventories.forMachine( + host.getTerminalIcon().getItem(), 1, this::eap$compatOnUpgradesChanged); + ExtendedAELogger.LOGGER.debug("[样板供应器] 初始化自带升级槽 (未安装 appflux)"); + } + } catch (Throwable t) { + ExtendedAELogger.LOGGER.error("[样板供应器] 初始化兼容升级槽失败", t); + } + } + + @Unique + private void eap$compatOnUpgradesChanged() { + try { + this.host.saveChanges(); + eap$compatLastChannel = -1; + eap$compatHasInitialized = false; + ExtendedAELogger.LOGGER.debug("[样板供应器] 升级变更 -> 触发初始化"); + eap$compatInitializeChannelLink(); + } catch (Throwable t) { + ExtendedAELogger.LOGGER.error("[样板供应器] 兼容升级变更处理失败", t); + } + } + + @Inject(method = "writeToNBT", at = @At("TAIL")) + private void eap$compatWrite(CompoundTag tag, net.minecraft.core.HolderLookup.Provider registries, CallbackInfo ci) { + try { + if (UpgradeSlotCompat.shouldEnableUpgradeSlots()) { + this.eap$compatUpgrades.writeToNBT(tag, "compat_upgrades", registries); + } + } catch (Throwable t) { + ExtendedAELogger.LOGGER.error("[样板供应器] 保存兼容升级失败", t); + } + } + + @Inject(method = "readFromNBT", at = @At("TAIL")) + private void eap$compatRead(CompoundTag tag, net.minecraft.core.HolderLookup.Provider registries, CallbackInfo ci) { + try { + if (UpgradeSlotCompat.shouldEnableUpgradeSlots()) { + this.eap$compatUpgrades.readFromNBT(tag, "compat_upgrades", registries); + eap$compatLastChannel = -1; + eap$compatHasInitialized = false; + eap$compatInitializeChannelLink(); + } + } catch (Throwable t) { + ExtendedAELogger.LOGGER.error("[样板供应器] 读取兼容升级失败", t); + } + } + + @Inject(method = "addDrops", at = @At("TAIL")) + private void eap$compatDrops(List drops, CallbackInfo ci) { + try { + if (UpgradeSlotCompat.shouldEnableUpgradeSlots()) { + for (var s : this.eap$compatUpgrades) { + if (!s.isEmpty()) drops.add(s); + } + } + } catch (Throwable t) { + ExtendedAELogger.LOGGER.error("[样板供应器] 掉落兼容升级失败", t); + } + } + + @Inject(method = "clearContent", at = @At("TAIL")) + private void eap$compatClear(CallbackInfo ci) { + try { + if (UpgradeSlotCompat.shouldEnableUpgradeSlots()) { + this.eap$compatUpgrades.clear(); + } + } catch (Throwable t) { + ExtendedAELogger.LOGGER.error("[样板供应器] 清理兼容升级失败", t); + } + } + + @Inject(method = "onMainNodeStateChanged", at = @At("TAIL")) + private void eap$compatOnNodeChange(CallbackInfo ci) { + try { + eap$compatLastChannel = -1; + eap$compatHasInitialized = false; + eap$compatDelayedInitTicks = 10; + mainNode.ifPresent((grid, node) -> { + try { grid.getTickManager().wakeDevice(node); } catch (Throwable ignored) {} + }); + } catch (Throwable t) { + ExtendedAELogger.LOGGER.error("[样板供应器] 主节点状态变更处理失败", t); + } + } + + + + @Override + public void eap$updateWirelessLink() { + if (eap$compatLink != null) { + eap$compatLink.updateStatus(); + } + } + + @Unique + public void eap$compatInitializeChannelLink() { + try { + // 客户端早退 + if (host.getBlockEntity() != null && host.getBlockEntity().getLevel() != null && host.getBlockEntity().getLevel().isClientSide) { + return; + } + if (eap$compatHasInitialized) { + return; + } + if (mainNode == null || mainNode.getNode() == null) { + ExtendedAELogger.LOGGER.debug("[样板供应器] 初始化跳过:mainNode 或 Node 不可用"); + return; + } + + long channel = 0L; + boolean found = false; + + IUpgradeInventory upgrades = null; + if (UpgradeSlotCompat.shouldEnableUpgradeSlots()) { + upgrades = this.eap$compatUpgrades; + ExtendedAELogger.LOGGER.debug("[样板供应器] 使用自带升级槽(未安装 appflux): {}", upgrades != null); + } else { + // appflux 应该注入其自身的 IUpgradeableObject 实现 + try { + if ((Object) this instanceof IUpgradeableObject uo) { + upgrades = uo.getUpgrades(); + ExtendedAELogger.LOGGER.debug("[样板供应器] 使用 appflux 提供的升级槽: {}", upgrades != null); + } + } catch (Throwable t) { + ExtendedAELogger.LOGGER.error("[样板供应器] 获取第三方升级槽失败", t); + } + } + + if (upgrades != null) { + for (ItemStack stack : upgrades) { + if (!stack.isEmpty() && stack.getItem() == ModItems.CHANNEL_CARD.get()) { + channel = ChannelCardItem.getChannel(stack); + found = true; + ExtendedAELogger.LOGGER.debug("[样板供应器] 检测到频道卡,频道={} ", channel); + break; + } + } + } + + if (!found) { + ExtendedAELogger.LOGGER.debug("[样板供应器] 未发现频道卡 -> 断开无线"); + if (eap$compatLink != null) { + eap$compatLink.setFrequency(0L); + eap$compatLink.updateStatus(); + } + eap$compatHasInitialized = true; + try { host.saveChanges(); } catch (Throwable ignored) {} + 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(); + ExtendedAELogger.LOGGER.debug("[样板供应器] 设置频道={} 连接状态={}", channel, eap$compatLink.isConnected()); + try { host.saveChanges(); } catch (Throwable ignored) {} + mainNode.ifPresent((grid, node) -> { + try { grid.getTickManager().wakeDevice(node); } catch (Throwable ignored) {} + }); + + if (eap$compatLink.isConnected()) { + eap$compatHasInitialized = true; + } else { + eap$compatHasInitialized = false; + eap$compatDelayedInitTicks = Math.max(eap$compatDelayedInitTicks, 5); + mainNode.ifPresent((grid, node) -> { + try { grid.getTickManager().wakeDevice(node); } catch (Throwable ignored) {} + }); + } + } catch (Throwable t) { + ExtendedAELogger.LOGGER.error("[样板供应器] 初始化频道链接失败", t); + } + } + + @Override + public void eap$setClientWirelessState(boolean connected) { + eap$compatClientConnected = connected; + } + + @Override + public boolean eap$isWirelessConnected() { + if (host.getBlockEntity() != null && host.getBlockEntity().getLevel() != null && host.getBlockEntity().getLevel().isClientSide) { + return eap$compatClientConnected; + } else { + return eap$compatLink != null && eap$compatLink.isConnected(); + } + } + + @Override + public boolean eap$hasTickInitialized() { + return eap$compatHasInitialized; + } + + @Override + public void eap$setTickInitialized(boolean initialized) { + eap$compatHasInitialized = initialized; + } + + // CompatUpgradeProvider 实现:仅在未安装 appflux 时由我们提供升级槽 + @Override + public IUpgradeInventory eap$getCompatUpgrades() { + return this.eap$compatUpgrades != null ? this.eap$compatUpgrades : UpgradeInventories.empty(); + } +} 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..ab20ac4 --- /dev/null +++ b/src/main/java/com/extendedae_plus/mixin/ae2/helpers/patternprovider/PatternProviderLogicTickerMixin.java @@ -0,0 +1,42 @@ +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 { + + // 访问内部类的外部引用字段(javac 生成名 this$0) + @Shadow(remap = false) + @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) { + if (this$0 instanceof InterfaceWirelessLinkBridge bridge) { + bridge.eap$updateWirelessLink(); + } + } +} 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..e2c947e --- /dev/null +++ b/src/main/java/com/extendedae_plus/mixin/ae2/menu/PatternProviderMenuUpgradesMixin.java @@ -0,0 +1,54 @@ +package com.extendedae_plus.mixin.ae2.menu; + +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 com.extendedae_plus.compat.UpgradeSlotCompat; +import com.extendedae_plus.bridge.CompatUpgradeProvider; +import appeng.api.upgrades.IUpgradeableObject; +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; +import com.extendedae_plus.util.ExtendedAELogger; + +@Mixin(value = PatternProviderMenu.class, priority = 2000, remap = false) +public abstract class PatternProviderMenuUpgradesMixin extends AEBaseMenu implements IUpgradableMenu { + @Final + @Shadow protected PatternProviderLogic logic; + + @Unique + 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 eap$initUpgrades(MenuType menuType, int id, Inventory playerInventory, PatternProviderLogicHost host, CallbackInfo ci) { + this.eap$toolbox = new ToolboxMenu(this); + if (UpgradeSlotCompat.shouldEnableUpgradeSlots()) { + // 未安装 appflux:使用我们提供的升级槽 + ExtendedAELogger.LOGGER.debug("[样板供应器][菜单] 注入升级槽: 使用自带 compat 槽"); + this.setupUpgrades(((CompatUpgradeProvider) this.logic).eap$getCompatUpgrades()); + } else { + // 安装 appflux:使用 appflux 注入到 PatternProviderLogic 的升级槽 + ExtendedAELogger.LOGGER.debug("[样板供应器][菜单] 注入升级槽: 使用 appflux 槽"); + this.setupUpgrades(((IUpgradeableObject) this.logic).getUpgrades()); + } + } + + @Override + public ToolboxMenu getToolbox() { + return this.eap$toolbox; + } + + public PatternProviderMenuUpgradesMixin(MenuType menuType, int id, Inventory playerInventory, Object host) { + super(menuType, id, playerInventory, host); + } +} diff --git a/src/main/resources/extendedae_plus.mixins.json b/src/main/resources/extendedae_plus.mixins.json index b562441..9fc4e10 100644 --- a/src/main/resources/extendedae_plus.mixins.json +++ b/src/main/resources/extendedae_plus.mixins.json @@ -25,10 +25,13 @@ "ae2.autopattern.CraftingTreeNodeMixin", "ae2.autopattern.CraftingTreeProcessMixin", "ae2.autopattern.PatternProviderLogicContainsRedirectMixin", + "ae2.compat.PatternProviderLogicCompatMixin", "ae2.helpers.InterfaceLogicChannelCardMixin", "ae2.helpers.InterfaceLogicTickerMixin", "ae2.helpers.PatternProviderLogicAdvancedMixin", "ae2.helpers.PatternProviderLogicDoublingMixin", + "ae2.helpers.patternprovider.PatternProviderLogicTickerMixin", + "ae2.menu.PatternProviderMenuUpgradesMixin", "ae2.parts.automation.IOBusPartChannelCardMixin", "ae2.parts.storagebus.StorageBusPartChannelCardMixin", "ae2.menu.ContainerPatternEncodingTermMenuMixin", From aa0c26246b36aa8250a6b3bf74178615f8fa3d2e Mon Sep 17 00:00:00 2001 From: GaLicn <133291877+GaLicn@users.noreply.github.com> Date: Wed, 24 Sep 2025 23:19:59 +0800 Subject: [PATCH 20/43] =?UTF-8?q?=E6=A0=B7=E6=9D=BF=E4=BE=9B=E5=BA=94?= =?UTF-8?q?=E5=99=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 6 +- .../compat/AppliedFluxCompat.java | 41 ++++++++++ .../compat/UpgradeSlotCompat.java | 40 +++++++++- .../PatternProviderScreenUpgradesMixin.java | 74 +++++++++++++++---- .../ae2/client/gui/ScreenStyleMixin.java | 34 +++++++++ .../PatternProviderLogicCompatMixin.java | 56 ++++++++++++-- .../extendedae_plus/util/IStyleAccessor.java | 14 ++++ .../resources/extendedae_plus.mixins.json | 2 + 8 files changed, 241 insertions(+), 26 deletions(-) create mode 100644 src/main/java/com/extendedae_plus/compat/AppliedFluxCompat.java create mode 100644 src/main/java/com/extendedae_plus/mixin/ae2/client/gui/ScreenStyleMixin.java create mode 100644 src/main/java/com/extendedae_plus/util/IStyleAccessor.java diff --git a/build.gradle b/build.gradle index cbb9f40..bba57a0 100644 --- a/build.gradle +++ b/build.gradle @@ -139,12 +139,12 @@ dependencies { implementation "curse.maven:curios-309927:6529130" compileOnly "curse.maven:ex-pattern-provider-892005:6863556" - implementation "curse.maven:applied-flux-965012:5614830" + compileOnly "curse.maven:applied-flux-965012:5614830" compileOnly "dev.emi:emi-neoforge:1.1.10+1.21" compileOnly "curse.maven:mega-cells-622112:6005043" compileOnly "curse.maven:jade-324717:5427817" compileOnly "me.shedaniel:RoughlyEnoughItems-neoforge:16.0.729" - // compileOnly "mekanism:Mekanism:1.21.1-10.7.0.55" + compileOnly "mekanism:Mekanism:1.21.1-10.7.0.55" compileOnly "curse.maven:applied-mekanistics-574300:5978711" compileOnly "dev.latvian.mods:kubejs-neoforge:2101.7.1-build.188" compileOnly "dev.latvian.mods:rhino:2101.2.5-build.54" @@ -155,7 +155,7 @@ dependencies { compileOnly "curse.maven:ae2-jei-integration-1074338:5748513" // runtime test - runtimeOnly "curse.maven:applied-flux-965012:5614830" + //runtimeOnly "curse.maven:applied-flux-965012:5614830" runtimeOnly "de.mari_023:ae2wtlib:19.2.1" runtimeOnly "curse.maven:jade-324717:5427817" runtimeOnly "curse.maven:mega-cells-622112:6005043" diff --git a/src/main/java/com/extendedae_plus/compat/AppliedFluxCompat.java b/src/main/java/com/extendedae_plus/compat/AppliedFluxCompat.java new file mode 100644 index 0000000..651f5be --- /dev/null +++ b/src/main/java/com/extendedae_plus/compat/AppliedFluxCompat.java @@ -0,0 +1,41 @@ +package com.extendedae_plus.compat; + +import appeng.client.gui.implementations.PatternProviderScreen; +import appeng.client.gui.widgets.UpgradesPanel; +import com.extendedae_plus.util.ExtendedAELogger; + +/** + * AppliedFlux 兼容性处理工具类 + * 用于检测和处理与AppliedFlux模组的UI冲突 + */ +public final class AppliedFluxCompat { + + private AppliedFluxCompat() {} + + /** + * 检查PatternProviderScreen是否已经有AppliedFlux添加的升级面板 + * 简化版本:主要通过AppliedFlux模组加载状态来判断 + */ + public static boolean hasAppliedFluxUpgradePanel(PatternProviderScreen screen) { + // 如果AppliedFlux未加载,肯定没有其升级面板 + if (!UpgradeSlotCompat.shouldUseLowPriorityMode()) { + return false; + } + + // 如果AppliedFlux加载了,假设它会添加升级面板 + // 这是一个保守的假设,避免冲突 + ExtendedAELogger.LOGGER.debug("[AppliedFlux兼容] 检测到AppliedFlux已加载,假设存在升级面板"); + return true; + } + + /** + * 检查是否应该跳过添加我们的升级面板 + */ + public static boolean shouldSkipOurUpgradePanel(PatternProviderScreen screen) { + if (!UpgradeSlotCompat.shouldUseLowPriorityMode()) { + return false; + } + + return hasAppliedFluxUpgradePanel(screen); + } +} diff --git a/src/main/java/com/extendedae_plus/compat/UpgradeSlotCompat.java b/src/main/java/com/extendedae_plus/compat/UpgradeSlotCompat.java index 0c9f191..5b549ce 100644 --- a/src/main/java/com/extendedae_plus/compat/UpgradeSlotCompat.java +++ b/src/main/java/com/extendedae_plus/compat/UpgradeSlotCompat.java @@ -5,6 +5,7 @@ import net.neoforged.fml.ModList; public final class UpgradeSlotCompat { private static Boolean APPFLUX_LOADED; + private static Boolean APPFLUX_PATTERN_PROVIDER_MIXIN_ACTIVE; private UpgradeSlotCompat() {} @@ -20,6 +21,29 @@ public final class UpgradeSlotCompat { return APPFLUX_LOADED; } + /** + * 检测AppliedFlux的PatternProviderScreen mixin是否活跃 + * 这比简单检查模组是否加载更准确 + */ + private static boolean isAppfluxPatternProviderMixinActive() { + if (APPFLUX_PATTERN_PROVIDER_MIXIN_ACTIVE == null) { + try { + if (!isAppfluxLoaded()) { + APPFLUX_PATTERN_PROVIDER_MIXIN_ACTIVE = false; + } else { + // 尝试检测AppliedFlux的mixin类是否存在 + Class.forName("com.glodblock.github.appflux.mixins.MixinPatternProviderScreen"); + APPFLUX_PATTERN_PROVIDER_MIXIN_ACTIVE = true; + } + } catch (ClassNotFoundException e) { + APPFLUX_PATTERN_PROVIDER_MIXIN_ACTIVE = false; + } catch (Throwable t) { + APPFLUX_PATTERN_PROVIDER_MIXIN_ACTIVE = false; + } + } + return APPFLUX_PATTERN_PROVIDER_MIXIN_ACTIVE; + } + // 是否由我们提供升级槽(当未安装 appflux 时) public static boolean shouldEnableUpgradeSlots() { return !isAppfluxLoaded(); @@ -30,9 +54,19 @@ public final class UpgradeSlotCompat { return true; } - // 客户端界面是否需要显示升级面板(装/不装 appflux 均显示; - // appflux 提供的升级槽会以 SlotSemantics.UPGRADE 出现在菜单中,我们只负责渲染面板) + // 客户端界面是否需要显示升级面板 + // 如果AppliedFlux的mixin活跃,我们降低优先级让它处理 public static boolean shouldAddUpgradePanelToScreen() { - return true; + return true; // 总是尝试添加,但在代码中检测冲突 + } + + // 是否应该使用低优先级模式(当AppliedFlux存在时) + public static boolean shouldUseLowPriorityMode() { + return isAppfluxPatternProviderMixinActive(); + } + + // 获取推荐的mixin优先级 + public static int getRecommendedMixinPriority() { + return isAppfluxPatternProviderMixinActive() ? 1500 : 2000; } } 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 d8b60fb..2150990 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 @@ -3,7 +3,11 @@ 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.layout.SlotGridLayout; +import appeng.client.gui.style.Blitter; import appeng.client.gui.style.ScreenStyle; +import appeng.client.gui.style.SlotPosition; +import appeng.client.gui.style.WidgetStyle; import appeng.client.gui.widgets.ToolboxPanel; import appeng.client.gui.widgets.UpgradesPanel; import appeng.core.localization.GuiText; @@ -12,6 +16,8 @@ import appeng.menu.implementations.PatternProviderMenu; import appeng.helpers.patternprovider.PatternProviderLogicHost; import appeng.menu.AEBaseMenu; import com.extendedae_plus.compat.UpgradeSlotCompat; +import com.extendedae_plus.compat.AppliedFluxCompat; +import com.extendedae_plus.util.IStyleAccessor; import net.minecraft.network.chat.Component; import net.minecraft.world.entity.player.Inventory; import org.spongepowered.asm.mixin.Mixin; @@ -23,30 +29,70 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import java.util.ArrayList; import java.util.List; -@Mixin(value = PatternProviderScreen.class, priority = 2000, remap = false) +@Mixin(value = PatternProviderScreen.class, priority = 1500, remap = false) public abstract class PatternProviderScreenUpgradesMixin extends AEBaseScreen { - @Inject(method = "", at = @At("TAIL")) + @Inject(method = "", at = @At("TAIL"), remap = false) private void eap$initUpgrades(PatternProviderMenu menu, Inventory playerInventory, Component title, ScreenStyle style, CallbackInfo ci) { + com.extendedae_plus.util.ExtendedAELogger.LOGGER.debug("[样板供应器][界面] 开始初始化升级面板"); + com.extendedae_plus.util.ExtendedAELogger.LOGGER.debug("[样板供应器][界面] shouldAddUpgradePanelToScreen: {}", UpgradeSlotCompat.shouldAddUpgradePanelToScreen()); + com.extendedae_plus.util.ExtendedAELogger.LOGGER.debug("[样板供应器][界面] shouldUseLowPriorityMode: {}", UpgradeSlotCompat.shouldUseLowPriorityMode()); + if (!UpgradeSlotCompat.shouldAddUpgradePanelToScreen()) { + com.extendedae_plus.util.ExtendedAELogger.LOGGER.debug("[样板供应器][界面] 跳过添加升级面板:shouldAddUpgradePanelToScreen返回false"); return; } - try { - this.widgets.add("upgrades", new UpgradesPanel( - menu.getSlots(SlotSemantics.UPGRADE), - this::eap$getCompatibleUpgrades)); - } catch (IllegalStateException already) { - // 已存在同名面板(可能由 AE2 或其他模组添加),忽略 - com.extendedae_plus.util.ExtendedAELogger.LOGGER.debug("[样板供应器][界面] 升级面板已存在,跳过添加"); - } - if (menu instanceof AEBaseMenu base && base instanceof com.extendedae_plus.bridge.IUpgradableMenu upg && upg.getToolbox() != null && upg.getToolbox().isPresent()) { + + // 使用改进的AppliedFlux兼容性检测 + @SuppressWarnings("unchecked") + PatternProviderScreen screen = (PatternProviderScreen) (Object) this; + + boolean shouldSkip = AppliedFluxCompat.shouldSkipOurUpgradePanel(screen); + com.extendedae_plus.util.ExtendedAELogger.LOGGER.debug("[样板供应器][界面] AppliedFlux兼容性检测结果: shouldSkip={}", shouldSkip); + + if (shouldSkip) { + com.extendedae_plus.util.ExtendedAELogger.LOGGER.debug("[样板供应器][界面] 检测到AppliedFlux升级面板,跳过添加我们的面板"); + } else { + com.extendedae_plus.util.ExtendedAELogger.LOGGER.debug("[样板供应器][界面] 开始添加升级面板"); + + // 检查是否已经存在upgrades widget try { - this.widgets.add("toolbox", new ToolboxPanel(style, upg.getToolbox().getName())); - } catch (IllegalStateException already) { - com.extendedae_plus.util.ExtendedAELogger.LOGGER.debug("[样板供应器][界面] 工具箱面板已存在,跳过添加"); + // 尝试添加升级面板 + this.widgets.add("upgrades", new UpgradesPanel(menu.getSlots(SlotSemantics.UPGRADE), this::eap$getCompatibleUpgrades)); + com.extendedae_plus.util.ExtendedAELogger.LOGGER.debug("[样板供应器][界面] 成功添加升级面板"); + } catch (IllegalStateException e) { + com.extendedae_plus.util.ExtendedAELogger.LOGGER.warn("[样板供应器][界面] 升级面板已存在,跳过添加: {}", e.getMessage()); + return; // 如果升级面板已存在,不继续添加其他内容 } + + // 设置TOOLBOX样式(完全按照AppliedFlux的方式) + var sp = new SlotPosition(); + sp.setBottom(84); + sp.setRight(1); + sp.setGrid(SlotGridLayout.BREAK_AFTER_3COLS); + var ws = new WidgetStyle(); + ws.setRight(2); + ws.setBottom(90); + ws.setWidth(59); + ws.setHeight(66); + style.getSlots().put("TOOLBOX", sp); + ((IStyleAccessor) style).getImages().put("toolbox", Blitter.texture("guis/extra_panels.png", 128, 128).src(69, 62, 59, 66)); + ((IStyleAccessor) style).getWidgets().put("toolbox", ws); + + // 添加工具箱面板 + if (menu instanceof AEBaseMenu base && base instanceof com.extendedae_plus.bridge.IUpgradableMenu upg && upg.getToolbox() != null && upg.getToolbox().isPresent()) { + try { + this.widgets.add("toolbox", new ToolboxPanel(style, upg.getToolbox().getName())); + com.extendedae_plus.util.ExtendedAELogger.LOGGER.debug("[样板供应器][界面] 成功添加工具箱面板"); + } catch (IllegalStateException e) { + com.extendedae_plus.util.ExtendedAELogger.LOGGER.warn("[样板供应器][界面] 工具箱面板已存在,跳过添加: {}", e.getMessage()); + } + } + + com.extendedae_plus.util.ExtendedAELogger.LOGGER.debug("[样板供应器][界面] 按照AppliedFlux方式完成升级面板和工具箱初始化"); } } + @Unique private List eap$getCompatibleUpgrades() { diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/client/gui/ScreenStyleMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/client/gui/ScreenStyleMixin.java new file mode 100644 index 0000000..c9bb4c4 --- /dev/null +++ b/src/main/java/com/extendedae_plus/mixin/ae2/client/gui/ScreenStyleMixin.java @@ -0,0 +1,34 @@ +package com.extendedae_plus.mixin.ae2.client.gui; + +import appeng.client.gui.style.Blitter; +import appeng.client.gui.style.ScreenStyle; +import appeng.client.gui.style.WidgetStyle; +import com.extendedae_plus.util.IStyleAccessor; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; + +import java.util.Map; + +@Mixin(ScreenStyle.class) +public abstract class ScreenStyleMixin implements IStyleAccessor { + + @Final + @Shadow(remap = false) + private Map images; + @Final + @Shadow(remap = false) + private Map widgets; + + @SuppressWarnings("AddedMixinMembersNamePattern") + @Override + public Map getImages() { + return this.images; + } + + @SuppressWarnings("AddedMixinMembersNamePattern") + @Override + public Map getWidgets() { + return this.widgets; + } +} 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 d9a530b..fa9f960 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 @@ -184,18 +184,49 @@ public abstract class PatternProviderLogicCompatMixin implements CompatUpgradePr boolean found = false; IUpgradeInventory upgrades = null; - if (UpgradeSlotCompat.shouldEnableUpgradeSlots()) { - upgrades = this.eap$compatUpgrades; - ExtendedAELogger.LOGGER.debug("[样板供应器] 使用自带升级槽(未安装 appflux): {}", upgrades != null); - } else { - // appflux 应该注入其自身的 IUpgradeableObject 实现 + + // 优先尝试从AppliedFlux获取升级槽(如果安装了的话) + if (!UpgradeSlotCompat.shouldEnableUpgradeSlots()) { + // 安装了appflux:优先使用appflux的升级槽 try { if ((Object) this instanceof IUpgradeableObject uo) { upgrades = uo.getUpgrades(); ExtendedAELogger.LOGGER.debug("[样板供应器] 使用 appflux 提供的升级槽: {}", upgrades != null); } } catch (Throwable t) { - ExtendedAELogger.LOGGER.error("[样板供应器] 获取第三方升级槽失败", t); + ExtendedAELogger.LOGGER.error("[样板供应器] 获取 appflux 升级槽失败,回退到兼容槽", t); + // 如果获取AppliedFlux升级槽失败,回退到我们的兼容槽 + upgrades = this.eap$compatUpgrades; + } + } else { + // 未安装appflux:使用我们的兼容升级槽 + upgrades = this.eap$compatUpgrades; + ExtendedAELogger.LOGGER.debug("[样板供应器] 使用自带升级槽(未安装 appflux): {}", upgrades != null); + } + + // 双重保险:如果主要方式失败,尝试备用方式 + if (upgrades == null || eap$isUpgradeInventoryEmpty(upgrades)) { + ExtendedAELogger.LOGGER.debug("[样板供应器] 主升级槽为空,尝试备用方式"); + + if (UpgradeSlotCompat.shouldEnableUpgradeSlots()) { + // 如果我们的槽为空,尝试检查是否有AppliedFlux的槽 + try { + if ((Object) this instanceof IUpgradeableObject uo) { + IUpgradeInventory backupUpgrades = uo.getUpgrades(); + if (backupUpgrades != null && !eap$isUpgradeInventoryEmpty(backupUpgrades)) { + upgrades = backupUpgrades; + ExtendedAELogger.LOGGER.debug("[样板供应器] 使用备用 appflux 升级槽"); + } + } + } catch (Throwable t) { + ExtendedAELogger.LOGGER.debug("[样板供应器] 备用升级槽检查失败: {}", t.getMessage()); + } + } else { + // 如果AppliedFlux的槽为空,尝试我们的兼容槽 + if (this.eap$compatUpgrades != null && !eap$isUpgradeInventoryEmpty(this.eap$compatUpgrades)) { + upgrades = this.eap$compatUpgrades; + ExtendedAELogger.LOGGER.debug("[样板供应器] 使用备用兼容升级槽"); + } } } @@ -273,6 +304,19 @@ public abstract class PatternProviderLogicCompatMixin implements CompatUpgradePr } // CompatUpgradeProvider 实现:仅在未安装 appflux 时由我们提供升级槽 + @Unique + private boolean eap$isUpgradeInventoryEmpty(IUpgradeInventory inventory) { + if (inventory == null) { + return true; + } + for (ItemStack stack : inventory) { + if (!stack.isEmpty()) { + return false; + } + } + return true; + } + @Override public IUpgradeInventory eap$getCompatUpgrades() { return this.eap$compatUpgrades != null ? this.eap$compatUpgrades : UpgradeInventories.empty(); diff --git a/src/main/java/com/extendedae_plus/util/IStyleAccessor.java b/src/main/java/com/extendedae_plus/util/IStyleAccessor.java new file mode 100644 index 0000000..1b47c25 --- /dev/null +++ b/src/main/java/com/extendedae_plus/util/IStyleAccessor.java @@ -0,0 +1,14 @@ +package com.extendedae_plus.util; + +import appeng.client.gui.style.Blitter; +import appeng.client.gui.style.WidgetStyle; + +import java.util.Map; + +public interface IStyleAccessor { + + Map getImages(); + + Map getWidgets(); + +} diff --git a/src/main/resources/extendedae_plus.mixins.json b/src/main/resources/extendedae_plus.mixins.json index 9fc4e10..875b677 100644 --- a/src/main/resources/extendedae_plus.mixins.json +++ b/src/main/resources/extendedae_plus.mixins.json @@ -63,6 +63,8 @@ "ae2.client.gui.PatternEncodingTermScreenMixin", "ae2.client.gui.PatternProviderCloseMixin", "ae2.client.gui.PatternProviderScreenMixin", + "ae2.client.gui.PatternProviderScreenUpgradesMixin", + "ae2.client.gui.ScreenStyleMixin", "ae2.client.gui.SlotGridLayoutMixin", "ae2.menu.CraftConfirmMenuGoBackMixin", "extendedae.accessor.GuiExPatternTerminalAccessor", From b0684cd354f607522a48ba3f84af0a341c8941f9 Mon Sep 17 00:00:00 2001 From: GaLicn <133291877+GaLicn@users.noreply.github.com> Date: Wed, 24 Sep 2025 23:34:14 +0800 Subject: [PATCH 21/43] =?UTF-8?q?appliedflux=E4=B8=8B=E4=BE=9B=E5=BA=94?= =?UTF-8?q?=E5=99=A8=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 2 +- .../PatternProviderLogicCompatMixin.java | 102 ++++++++++++++---- 2 files changed, 84 insertions(+), 20 deletions(-) diff --git a/build.gradle b/build.gradle index bba57a0..cd15d59 100644 --- a/build.gradle +++ b/build.gradle @@ -155,7 +155,7 @@ dependencies { compileOnly "curse.maven:ae2-jei-integration-1074338:5748513" // runtime test - //runtimeOnly "curse.maven:applied-flux-965012:5614830" + runtimeOnly "curse.maven:applied-flux-965012:5614830" runtimeOnly "de.mari_023:ae2wtlib:19.2.1" runtimeOnly "curse.maven:jade-324717:5427817" runtimeOnly "curse.maven:mega-cells-622112:6005043" 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 fa9f960..f7a8f93 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 @@ -33,7 +33,7 @@ import java.util.List; * - 安装 appflux 时,优先从 appflux 提供的升级槽读取频道卡; * - 建立到无线主站的网格连接。 */ -@Mixin(value = PatternProviderLogic.class, remap = false) +@Mixin(value = PatternProviderLogic.class, priority = 1500, remap = false) public abstract class PatternProviderLogicCompatMixin implements CompatUpgradeProvider, InterfaceWirelessLinkBridge { @Unique @@ -51,9 +51,6 @@ public abstract class PatternProviderLogicCompatMixin implements CompatUpgradePr @Unique private boolean eap$compatHasInitialized = false; - @Unique - private int eap$compatDelayedInitTicks = 0; - @Final @Shadow private PatternProviderLogicHost host; @@ -70,10 +67,21 @@ public abstract class PatternProviderLogicCompatMixin implements CompatUpgradePr at = @At("TAIL")) private void eap$compatInit(IManagedGridNode mainNode, PatternProviderLogicHost host, int size, CallbackInfo ci) { try { + ExtendedAELogger.LOGGER.debug("[样板供应器] 初始化兼容升级槽,shouldEnableUpgradeSlots: {}", UpgradeSlotCompat.shouldEnableUpgradeSlots()); + ExtendedAELogger.LOGGER.debug("[样板供应器] this instanceof IUpgradeableObject: {}", this instanceof IUpgradeableObject); + if (UpgradeSlotCompat.shouldEnableUpgradeSlots()) { + // 未安装AppliedFlux,我们需要提供升级槽 this.eap$compatUpgrades = UpgradeInventories.forMachine( host.getTerminalIcon().getItem(), 1, this::eap$compatOnUpgradesChanged); ExtendedAELogger.LOGGER.debug("[样板供应器] 初始化自带升级槽 (未安装 appflux)"); + } else { + // 安装了AppliedFlux,我们不提供升级槽,但保留空的兼容槽用于备用 + this.eap$compatUpgrades = UpgradeInventories.empty(); + ExtendedAELogger.LOGGER.debug("[样板供应器] 跳过初始化升级槽 (已安装 appflux)"); + + // 尝试监听AppliedFlux的升级变更 + eap$tryHookAppliedFluxUpgradeChanges(); } } catch (Throwable t) { ExtendedAELogger.LOGGER.error("[样板供应器] 初始化兼容升级槽失败", t); @@ -93,6 +101,26 @@ public abstract class PatternProviderLogicCompatMixin implements CompatUpgradePr } } + /** + * 尝试监听AppliedFlux的升级变更 + */ + @Unique + private void eap$tryHookAppliedFluxUpgradeChanges() { + try { + if (this instanceof IUpgradeableObject upgradeableObject) { + IUpgradeInventory afUpgrades = upgradeableObject.getUpgrades(); + if (afUpgrades != null) { + ExtendedAELogger.LOGGER.debug("[样板供应器] 尝试监听AppliedFlux升级变更"); + // 我们不能直接修改AppliedFlux的升级槽回调,但我们可以定期检查 + // 这里我们先记录一下,实际的检查会在tick中进行 + } + } + } catch (Throwable t) { + ExtendedAELogger.LOGGER.debug("[样板供应器] 监听AppliedFlux升级变更失败: {}", t.getMessage()); + } + } + + @Inject(method = "writeToNBT", at = @At("TAIL")) private void eap$compatWrite(CompoundTag tag, net.minecraft.core.HolderLookup.Provider registries, CallbackInfo ci) { try { @@ -109,10 +137,11 @@ public abstract class PatternProviderLogicCompatMixin implements CompatUpgradePr try { if (UpgradeSlotCompat.shouldEnableUpgradeSlots()) { this.eap$compatUpgrades.readFromNBT(tag, "compat_upgrades", registries); - eap$compatLastChannel = -1; - eap$compatHasInitialized = false; - eap$compatInitializeChannelLink(); } + // 无论哪种模式都重新初始化 + eap$compatLastChannel = -1; + eap$compatHasInitialized = false; + eap$compatInitializeChannelLink(); } catch (Throwable t) { ExtendedAELogger.LOGGER.error("[样板供应器] 读取兼容升级失败", t); } @@ -147,7 +176,8 @@ public abstract class PatternProviderLogicCompatMixin implements CompatUpgradePr try { eap$compatLastChannel = -1; eap$compatHasInitialized = false; - eap$compatDelayedInitTicks = 10; + // 直接初始化,不使用延迟 + eap$compatInitializeChannelLink(); mainNode.ifPresent((grid, node) -> { try { grid.getTickManager().wakeDevice(node); } catch (Throwable ignored) {} }); @@ -156,6 +186,7 @@ public abstract class PatternProviderLogicCompatMixin implements CompatUpgradePr } } + @Override @@ -189,13 +220,16 @@ public abstract class PatternProviderLogicCompatMixin implements CompatUpgradePr if (!UpgradeSlotCompat.shouldEnableUpgradeSlots()) { // 安装了appflux:优先使用appflux的升级槽 try { - if ((Object) this instanceof IUpgradeableObject uo) { - upgrades = uo.getUpgrades(); - ExtendedAELogger.LOGGER.debug("[样板供应器] 使用 appflux 提供的升级槽: {}", upgrades != null); + // 更安全的方式获取AppliedFlux升级槽 + upgrades = eap$getAppliedFluxUpgrades(); + if (upgrades != null) { + ExtendedAELogger.LOGGER.debug("[样板供应器] 成功获取 appflux 升级槽,大小: {}", upgrades.size()); + } else { + ExtendedAELogger.LOGGER.warn("[样板供应器] 无法获取 appflux 升级槽,回退到兼容槽"); + upgrades = this.eap$compatUpgrades; } } catch (Throwable t) { ExtendedAELogger.LOGGER.error("[样板供应器] 获取 appflux 升级槽失败,回退到兼容槽", t); - // 如果获取AppliedFlux升级槽失败,回退到我们的兼容槽 upgrades = this.eap$compatUpgrades; } } else { @@ -211,12 +245,10 @@ public abstract class PatternProviderLogicCompatMixin implements CompatUpgradePr if (UpgradeSlotCompat.shouldEnableUpgradeSlots()) { // 如果我们的槽为空,尝试检查是否有AppliedFlux的槽 try { - if ((Object) this instanceof IUpgradeableObject uo) { - IUpgradeInventory backupUpgrades = uo.getUpgrades(); - if (backupUpgrades != null && !eap$isUpgradeInventoryEmpty(backupUpgrades)) { - upgrades = backupUpgrades; - ExtendedAELogger.LOGGER.debug("[样板供应器] 使用备用 appflux 升级槽"); - } + IUpgradeInventory backupUpgrades = eap$getAppliedFluxUpgrades(); + if (backupUpgrades != null && !eap$isUpgradeInventoryEmpty(backupUpgrades)) { + upgrades = backupUpgrades; + ExtendedAELogger.LOGGER.debug("[样板供应器] 使用备用 appflux 升级槽"); } } catch (Throwable t) { ExtendedAELogger.LOGGER.debug("[样板供应器] 备用升级槽检查失败: {}", t.getMessage()); @@ -269,7 +301,7 @@ public abstract class PatternProviderLogicCompatMixin implements CompatUpgradePr eap$compatHasInitialized = true; } else { eap$compatHasInitialized = false; - eap$compatDelayedInitTicks = Math.max(eap$compatDelayedInitTicks, 5); + // 如果连接失败,唤醒设备以便稍后重试 mainNode.ifPresent((grid, node) -> { try { grid.getTickManager().wakeDevice(node); } catch (Throwable ignored) {} }); @@ -317,6 +349,38 @@ public abstract class PatternProviderLogicCompatMixin implements CompatUpgradePr return true; } + /** + * 安全地获取AppliedFlux提供的升级槽 + */ + @Unique + private IUpgradeInventory eap$getAppliedFluxUpgrades() { + try { + ExtendedAELogger.LOGGER.debug("[样板供应器] 尝试获取AppliedFlux升级槽"); + ExtendedAELogger.LOGGER.debug("[样板供应器] this instanceof IUpgradeableObject: {}", this instanceof IUpgradeableObject); + + // 检查当前对象是否实现了IUpgradeableObject接口 + if (this instanceof IUpgradeableObject upgradeableObject) { + IUpgradeInventory upgrades = upgradeableObject.getUpgrades(); + ExtendedAELogger.LOGGER.debug("[样板供应器] AppliedFlux升级槽: {}", upgrades); + ExtendedAELogger.LOGGER.debug("[样板供应器] 兼容升级槽: {}", this.eap$compatUpgrades); + ExtendedAELogger.LOGGER.debug("[样板供应器] 是否为同一对象: {}", upgrades == this.eap$compatUpgrades); + + // 确保这不是我们自己的兼容升级槽 + if (upgrades != null && upgrades != this.eap$compatUpgrades) { + ExtendedAELogger.LOGGER.debug("[样板供应器] 成功获取AppliedFlux升级槽,大小: {}", upgrades.size()); + return upgrades; + } else { + ExtendedAELogger.LOGGER.debug("[样板供应器] AppliedFlux升级槽无效或与兼容槽相同"); + } + } else { + ExtendedAELogger.LOGGER.debug("[样板供应器] 当前对象未实现IUpgradeableObject接口"); + } + } catch (Throwable t) { + ExtendedAELogger.LOGGER.error("[样板供应器] 获取AppliedFlux升级槽时出错", t); + } + return null; + } + @Override public IUpgradeInventory eap$getCompatUpgrades() { return this.eap$compatUpgrades != null ? this.eap$compatUpgrades : UpgradeInventories.empty(); From 4327ccc67a805f5ab0fe91f8c0733850cda69b2e Mon Sep 17 00:00:00 2001 From: GaLicn <133291877+GaLicn@users.noreply.github.com> Date: Thu, 25 Sep 2025 13:25:44 +0800 Subject: [PATCH 22/43] =?UTF-8?q?appliedflux=E4=B8=8B=E4=BE=9B=E5=BA=94?= =?UTF-8?q?=E5=99=A8=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../PatternProviderLogicCompatMixin.java | 72 ++++++++++++++----- 1 file changed, 55 insertions(+), 17 deletions(-) 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 f7a8f93..0f38590 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 @@ -33,7 +33,7 @@ import java.util.List; * - 安装 appflux 时,优先从 appflux 提供的升级槽读取频道卡; * - 建立到无线主站的网格连接。 */ -@Mixin(value = PatternProviderLogic.class, priority = 1500, remap = false) +@Mixin(value = PatternProviderLogic.class, priority = 900, remap = false) public abstract class PatternProviderLogicCompatMixin implements CompatUpgradeProvider, InterfaceWirelessLinkBridge { @Unique @@ -239,14 +239,14 @@ public abstract class PatternProviderLogicCompatMixin implements CompatUpgradePr } // 双重保险:如果主要方式失败,尝试备用方式 - if (upgrades == null || eap$isUpgradeInventoryEmpty(upgrades)) { - ExtendedAELogger.LOGGER.debug("[样板供应器] 主升级槽为空,尝试备用方式"); + if (upgrades == null || !eap$hasChannelCard(upgrades)) { + ExtendedAELogger.LOGGER.debug("[样板供应器] 主升级槽无频道卡,尝试备用方式"); if (UpgradeSlotCompat.shouldEnableUpgradeSlots()) { - // 如果我们的槽为空,尝试检查是否有AppliedFlux的槽 + // 如果我们的槽无频道卡,尝试检查是否有AppliedFlux的槽 try { IUpgradeInventory backupUpgrades = eap$getAppliedFluxUpgrades(); - if (backupUpgrades != null && !eap$isUpgradeInventoryEmpty(backupUpgrades)) { + if (backupUpgrades != null && eap$hasChannelCard(backupUpgrades)) { upgrades = backupUpgrades; ExtendedAELogger.LOGGER.debug("[样板供应器] 使用备用 appflux 升级槽"); } @@ -254,8 +254,8 @@ public abstract class PatternProviderLogicCompatMixin implements CompatUpgradePr ExtendedAELogger.LOGGER.debug("[样板供应器] 备用升级槽检查失败: {}", t.getMessage()); } } else { - // 如果AppliedFlux的槽为空,尝试我们的兼容槽 - if (this.eap$compatUpgrades != null && !eap$isUpgradeInventoryEmpty(this.eap$compatUpgrades)) { + // 如果AppliedFlux的槽无频道卡,尝试我们的兼容槽 + if (this.eap$compatUpgrades != null && eap$hasChannelCard(this.eap$compatUpgrades)) { upgrades = this.eap$compatUpgrades; ExtendedAELogger.LOGGER.debug("[样板供应器] 使用备用兼容升级槽"); } @@ -279,6 +279,7 @@ public abstract class PatternProviderLogicCompatMixin implements CompatUpgradePr eap$compatLink.setFrequency(0L); eap$compatLink.updateStatus(); } + eap$compatLastChannel = 0L; eap$compatHasInitialized = true; try { host.saveChanges(); } catch (Throwable ignored) {} return; @@ -291,6 +292,7 @@ public abstract class PatternProviderLogicCompatMixin implements CompatUpgradePr eap$compatLink.setFrequency(channel); eap$compatLink.updateStatus(); + eap$compatLastChannel = channel; // 记录当前频道 ExtendedAELogger.LOGGER.debug("[样板供应器] 设置频道={} 连接状态={}", channel, eap$compatLink.isConnected()); try { host.saveChanges(); } catch (Throwable ignored) {} mainNode.ifPresent((grid, node) -> { @@ -335,18 +337,54 @@ public abstract class PatternProviderLogicCompatMixin implements CompatUpgradePr eap$compatHasInitialized = initialized; } - // CompatUpgradeProvider 实现:仅在未安装 appflux 时由我们提供升级槽 - @Unique - private boolean eap$isUpgradeInventoryEmpty(IUpgradeInventory inventory) { - if (inventory == null) { - return true; - } - for (ItemStack stack : inventory) { - if (!stack.isEmpty()) { - return false; + @Override + public void eap$handleDelayedInit() { + // 如果还未初始化,或者需要重新检查AppliedFlux升级槽 + if (!eap$compatHasInitialized) { + eap$compatInitializeChannelLink(); + } else if (!UpgradeSlotCompat.shouldEnableUpgradeSlots()) { + // 安装了AppliedFlux时,定期检查升级槽变化 + try { + IUpgradeInventory afUpgrades = eap$getAppliedFluxUpgrades(); + if (afUpgrades != null && eap$hasChannelCard(afUpgrades)) { + // 检查频道是否发生变化 + for (ItemStack stack : afUpgrades) { + if (!stack.isEmpty() && stack.getItem() == ModItems.CHANNEL_CARD.get()) { + long newChannel = ChannelCardItem.getChannel(stack); + if (newChannel != eap$compatLastChannel) { + ExtendedAELogger.LOGGER.debug("[样板供应器] 检测到频道变化: {} -> {}", eap$compatLastChannel, newChannel); + eap$compatLastChannel = -1; // 强制重新初始化 + eap$compatHasInitialized = false; + eap$compatInitializeChannelLink(); + } + break; + } + } + } else if (eap$compatLastChannel != 0L) { + // 频道卡被移除 + ExtendedAELogger.LOGGER.debug("[样板供应器] 频道卡被移除"); + eap$compatLastChannel = -1; + eap$compatHasInitialized = false; + eap$compatInitializeChannelLink(); + } + } catch (Throwable t) { + ExtendedAELogger.LOGGER.debug("[样板供应器] 延迟初始化检查失败: {}", t.getMessage()); } } - return true; + } + + // CompatUpgradeProvider 实现:仅在未安装 appflux 时由我们提供升级槽 + @Unique + private boolean eap$hasChannelCard(IUpgradeInventory inventory) { + if (inventory == null) { + return false; + } + for (ItemStack stack : inventory) { + if (!stack.isEmpty() && stack.getItem() == ModItems.CHANNEL_CARD.get()) { + return true; + } + } + return false; } /** From 2a4dd481a0f457026d4b1063d6a28ab4ee64005e Mon Sep 17 00:00:00 2001 From: GaLicn <133291877+GaLicn@users.noreply.github.com> Date: Thu, 25 Sep 2025 15:01:47 +0800 Subject: [PATCH 23/43] =?UTF-8?q?appliedflux=E4=B8=8B=E4=BE=9B=E5=BA=94?= =?UTF-8?q?=E5=99=A8=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../PatternProviderLogicCompatMixin.java | 19 ++++++ .../wireless/WirelessSlaveLink.java | 59 +++++++++++++++++++ 2 files changed, 78 insertions(+) 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 0f38590..d062e2e 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 @@ -1,6 +1,7 @@ package com.extendedae_plus.mixin.ae2.compat; import appeng.api.networking.IManagedGridNode; +import appeng.api.networking.IGridConnection; import appeng.api.networking.security.IActionSource; import appeng.api.upgrades.IUpgradeInventory; import appeng.api.upgrades.IUpgradeableObject; @@ -282,6 +283,24 @@ public abstract class PatternProviderLogicCompatMixin implements CompatUpgradePr eap$compatLastChannel = 0L; eap$compatHasInitialized = true; try { host.saveChanges(); } catch (Throwable ignored) {} + // 唤醒节点,加速 AE2 感知到连接断开 + mainNode.ifPresent((grid, node) -> { + try { grid.getTickManager().wakeDevice(node); } catch (Throwable ignored) {} + // 兜底:如仍存在针对无线主端的直连(非 in-world),强制销毁 + try { + for (IGridConnection gc : node.getConnections()) { + if (gc != null && !gc.isInWorld()) { + var other = gc.getOtherSide(node); + if (other != null && other.getOwner() instanceof com.extendedae_plus.wireless.IWirelessEndpoint) { + ExtendedAELogger.LOGGER.debug("[样板供应器] 兜底销毁残留无线直连: {} -> {}", node, other); + gc.destroy(); + try { grid.getTickManager().wakeDevice(node); } catch (Throwable ignored2) {} + try { if (other.getGrid() != null) { other.getGrid().getTickManager().wakeDevice(other); } } catch (Throwable ignored2) {} + } + } + } + } catch (Throwable ignored2) {} + }); return; } diff --git a/src/main/java/com/extendedae_plus/wireless/WirelessSlaveLink.java b/src/main/java/com/extendedae_plus/wireless/WirelessSlaveLink.java index 134d014..3157025 100644 --- a/src/main/java/com/extendedae_plus/wireless/WirelessSlaveLink.java +++ b/src/main/java/com/extendedae_plus/wireless/WirelessSlaveLink.java @@ -1,6 +1,7 @@ package com.extendedae_plus.wireless; import appeng.api.networking.GridHelper; +import appeng.api.networking.IGridConnection; import appeng.api.networking.IGridNode; import appeng.me.service.helpers.ConnectionWrapper; import com.extendedae_plus.config.ModConfigs; @@ -99,6 +100,13 @@ public class WirelessSlaveLink { current.destroy(); connection = new ConnectionWrapper(null); } + // AE2 侧是否已经存在连接(例如此前创建但 wrapper 丢失) + IGridConnection existing = findExistingConnection(a, b); + if (existing != null) { + ExtendedAELogger.LOGGER.debug("[无线] 复用已存在的连接: {}", existing); + connection = new ConnectionWrapper(existing); + return; + } ExtendedAELogger.LOGGER.debug("[无线] 创建连接: a={}, b={}", a, b); connection = new ConnectionWrapper(GridHelper.createConnection(a, b)); ExtendedAELogger.LOGGER.debug("[无线] 连接创建完成: {}", connection.getConnection()); @@ -128,9 +136,60 @@ public class WirelessSlaveLink { private void destroyConnection() { var current = connection.getConnection(); if (current != null) { + ExtendedAELogger.LOGGER.debug("[无线] 销毁连接: {}", current); + var a = current.a(); + var b = current.b(); + // 先销毁连接,再唤醒两端节点,使其尽快感知到状态变化 current.destroy(); + try { + if (a != null && a.getGrid() != null) { + a.getGrid().getTickManager().wakeDevice(a); + } + } catch (Throwable ignored) {} + try { + if (b != null && b.getGrid() != null) { + b.getGrid().getTickManager().wakeDevice(b); + } + } catch (Throwable ignored) {} connection.setConnection(null); + } else { + // 兜底:如果 wrapper 已丢失,但 AE2 内仍有直连,则尝试查找并销毁 + try { + IGridNode a = host.getGridNode(); + if (a != null) { + for (IGridConnection gc : a.getConnections()) { + // 我们创建的是非 in-world 直连 + if (gc != null && !gc.isInWorld()) { + IGridNode other = gc.getOtherSide(a); + Object owner = other != null ? other.getOwner() : null; + if (owner instanceof IWirelessEndpoint) { + ExtendedAELogger.LOGGER.debug("[无线] 兜底销毁直连: {} -> {}", a, other); + gc.destroy(); + try { if (a.getGrid() != null) { a.getGrid().getTickManager().wakeDevice(a); } } catch (Throwable ignored) {} + try { if (other != null && other.getGrid() != null) { other.getGrid().getTickManager().wakeDevice(other); } } catch (Throwable ignored) {} + } + } + } + } + } catch (Throwable ignored) {} } connection = new ConnectionWrapper(null); } + + /** + * 在 AE2 中查找 a 与 b 之间是否已经有连接存在(用于复用,避免重复创建异常)。 + */ + private IGridConnection findExistingConnection(IGridNode a, IGridNode b) { + try { + for (IGridConnection gc : a.getConnections()) { + var ga = gc.a(); + var gb = gc.b(); + if ((ga == a || gb == a) && (ga == b || gb == b)) { + return gc; + } + } + } catch (Throwable ignored) {} + return null; + } } + From 1d4524fb16b56eebf229dedc16b4d3be6f23e7ea Mon Sep 17 00:00:00 2001 From: GaLicn <133291877+GaLicn@users.noreply.github.com> Date: Thu, 25 Sep 2025 16:23:25 +0800 Subject: [PATCH 24/43] =?UTF-8?q?appliedflux=E4=B8=8B=E4=BE=9B=E5=BA=94?= =?UTF-8?q?=E5=99=A8jade=E6=98=BE=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 2 +- .../bridge/InterfaceWirelessLinkBridge.java | 7 ++++ .../PatternProviderLogicCompatMixin.java | 42 +++++++++++++++++++ .../PatternProviderLogicTickerMixin.java | 9 +++- 4 files changed, 58 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index cd15d59..30753b9 100644 --- a/build.gradle +++ b/build.gradle @@ -132,7 +132,7 @@ configurations { dependencies { // --- Added dependencies for target mods --- implementation "curse.maven:glodium-957920:5821676" - implementation "org.appliedenergistics:appliedenergistics2:19.2.8" + implementation "org.appliedenergistics:appliedenergistics2:19.2.15" implementation "org.appliedenergistics:guideme:2.5.1" // jarJar configuration not set in this build; use implementation for API for now implementation "de.mari_023:ae2wtlib_api:19.2.0" diff --git a/src/main/java/com/extendedae_plus/bridge/InterfaceWirelessLinkBridge.java b/src/main/java/com/extendedae_plus/bridge/InterfaceWirelessLinkBridge.java index d200f77..a9be54b 100644 --- a/src/main/java/com/extendedae_plus/bridge/InterfaceWirelessLinkBridge.java +++ b/src/main/java/com/extendedae_plus/bridge/InterfaceWirelessLinkBridge.java @@ -47,4 +47,11 @@ public interface InterfaceWirelessLinkBridge { default void eap$handleDelayedInit() { // 默认实现为空 } + + /** + * 指示是否需要保持慢速tick(用于轮询频道卡或维持无线状态) + */ + default boolean eap$shouldKeepTicking() { + return false; + } } 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 d062e2e..f5c500e 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 @@ -392,6 +392,48 @@ public abstract class PatternProviderLogicCompatMixin implements CompatUpgradePr } } + /** + * 指示 PatternProviderLogic 的 Ticker 是否需要保持慢速 tick 以轮询频道卡或维持无线连接。 + */ + public boolean eap$shouldKeepTicking() { + try { + // 仅在服务端保持tick + if (host.getBlockEntity() == null || host.getBlockEntity().getLevel() == null || host.getBlockEntity().getLevel().isClientSide) { + return false; + } + // 未初始化:需要继续tick直到初始化完成 + if (!eap$compatHasInitialized) { + return true; + } + // 安装了 AppliedFlux:根据连接状态与频道卡存在性决定是否维持慢速tick + if (!UpgradeSlotCompat.shouldEnableUpgradeSlots()) { + // 若曾经设置过频道或者当前存在未连通的链接,则保持tick + if (eap$compatLastChannel != 0L) { + return true; + } + if (eap$compatLink != null && !eap$compatLink.isConnected()) { + return true; + } + try { + IUpgradeInventory afUpgrades = eap$getAppliedFluxUpgrades(); + if (afUpgrades != null && eap$hasChannelCard(afUpgrades)) { + // 槽中有频道卡,保持tick以尽快完成连接 + return true; + } + } catch (Throwable ignored) {} + // 否则可以休眠 + return false; + } + // 未安装 AppliedFlux:当存在频道卡但连接尚未建立时保持tick + if (this.eap$compatUpgrades != null && eap$hasChannelCard(this.eap$compatUpgrades)) { + if (eap$compatLink == null || !eap$compatLink.isConnected()) { + return true; + } + } + } catch (Throwable ignored) {} + return false; + } + // CompatUpgradeProvider 实现:仅在未安装 appflux 时由我们提供升级槽 @Unique private boolean eap$hasChannelCard(IUpgradeInventory inventory) { 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 ab20ac4..13b6e63 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 @@ -32,11 +32,18 @@ public abstract class PatternProviderLogicTickerMixin { } } - @Inject(method = "tickingRequest", at = @At("TAIL")) + @Inject(method = "tickingRequest", at = @At("TAIL"), cancellable = true) private void eap$tickTail(appeng.api.networking.IGridNode node, int ticksSinceLastCall, CallbackInfoReturnable cir) { + // 仅在服务端设置慢速 tick + if (node != null && node.getLevel() != null && node.getLevel().isClientSide) { + return; + } if (this$0 instanceof InterfaceWirelessLinkBridge bridge) { bridge.eap$updateWirelessLink(); + if (bridge.eap$shouldKeepTicking()) { + cir.setReturnValue(appeng.api.networking.ticking.TickRateModulation.SLOWER); + } } } } From d76023307064176a74caa20dc9f0234e48039677 Mon Sep 17 00:00:00 2001 From: GaLicn <133291877+GaLicn@users.noreply.github.com> Date: Thu, 25 Sep 2025 16:34:02 +0800 Subject: [PATCH 25/43] =?UTF-8?q?appliedflux=E4=B8=8B=E4=BE=9B=E5=BA=94?= =?UTF-8?q?=E5=99=A8jade=E6=98=BE=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ae2/client/gui/PatternProviderScreenUpgradesMixin.java | 6 ++++++ .../mixin/ae2/menu/PatternProviderMenuUpgradesMixin.java | 5 ++--- 2 files changed, 8 insertions(+), 3 deletions(-) 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 2150990..890223b 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 @@ -42,6 +42,12 @@ public abstract class PatternProviderScreenUpgradesMixin Date: Thu, 25 Sep 2025 16:54:24 +0800 Subject: [PATCH 26/43] =?UTF-8?q?=E8=B0=83=E6=95=B4=E5=B8=83=E5=B1=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../extendedae_plus/init/UpgradeCards.java | 26 +++++++++++++++++++ .../ae2/client/gui/InterfaceScreenMixin.java | 2 +- .../client/gui/GuiExPatternProviderMixin.java | 2 +- 3 files changed, 28 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/extendedae_plus/init/UpgradeCards.java b/src/main/java/com/extendedae_plus/init/UpgradeCards.java index 0816756..9815ca6 100644 --- a/src/main/java/com/extendedae_plus/init/UpgradeCards.java +++ b/src/main/java/com/extendedae_plus/init/UpgradeCards.java @@ -32,6 +32,32 @@ public class UpgradeCards { 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, storageGroup); + + // 频道卡:ExtendedAE 扩展组件支持(强依赖,直接引用) + // 扩展接口 + Upgrades.add(ModItems.CHANNEL_CARD.get(), com.glodblock.github.extendedae.common.EAESingletons.EX_INTERFACE, 1, interfaceGroup); + Upgrades.add(ModItems.CHANNEL_CARD.get(), com.glodblock.github.extendedae.common.EAESingletons.EX_INTERFACE_PART, 1, interfaceGroup); + + // 扩展样板供应器 + Upgrades.add(ModItems.CHANNEL_CARD.get(), com.glodblock.github.extendedae.common.EAESingletons.EX_PATTERN_PROVIDER, 1, patternProviderGroup); + Upgrades.add(ModItems.CHANNEL_CARD.get(), com.glodblock.github.extendedae.common.EAESingletons.EX_PATTERN_PROVIDER_PART, 1, patternProviderGroup); + + // 扩展 I/O 总线 + Upgrades.add(ModItems.CHANNEL_CARD.get(), com.glodblock.github.extendedae.common.EAESingletons.EX_IMPORT_BUS, 1, ioBusGroup); + Upgrades.add(ModItems.CHANNEL_CARD.get(), com.glodblock.github.extendedae.common.EAESingletons.EX_EXPORT_BUS, 1, ioBusGroup); + + // 其他扩展总线(标签、精确、阈值等) + Upgrades.add(ModItems.CHANNEL_CARD.get(), com.glodblock.github.extendedae.common.EAESingletons.TAG_STORAGE_BUS, 1, storageGroup); + Upgrades.add(ModItems.CHANNEL_CARD.get(), com.glodblock.github.extendedae.common.EAESingletons.TAG_EXPORT_BUS, 1, ioBusGroup); + Upgrades.add(ModItems.CHANNEL_CARD.get(), com.glodblock.github.extendedae.common.EAESingletons.MOD_STORAGE_BUS, 1, storageGroup); + Upgrades.add(ModItems.CHANNEL_CARD.get(), com.glodblock.github.extendedae.common.EAESingletons.MOD_EXPORT_BUS, 1, ioBusGroup); + Upgrades.add(ModItems.CHANNEL_CARD.get(), com.glodblock.github.extendedae.common.EAESingletons.PRECISE_STORAGE_BUS, 1, storageGroup); + Upgrades.add(ModItems.CHANNEL_CARD.get(), com.glodblock.github.extendedae.common.EAESingletons.PRECISE_EXPORT_BUS, 1, ioBusGroup); + Upgrades.add(ModItems.CHANNEL_CARD.get(), com.glodblock.github.extendedae.common.EAESingletons.THRESHOLD_EXPORT_BUS, 1, ioBusGroup); + + // 超大接口 + Upgrades.add(ModItems.CHANNEL_CARD.get(), com.glodblock.github.extendedae.common.EAESingletons.OVERSIZE_INTERFACE, 1, interfaceGroup); + Upgrades.add(ModItems.CHANNEL_CARD.get(), com.glodblock.github.extendedae.common.EAESingletons.OVERSIZE_INTERFACE_PART, 1, interfaceGroup); }); } } \ No newline at end of file diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/client/gui/InterfaceScreenMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/client/gui/InterfaceScreenMixin.java index 49d03b3..34aa261 100644 --- a/src/main/java/com/extendedae_plus/mixin/ae2/client/gui/InterfaceScreenMixin.java +++ b/src/main/java/com/extendedae_plus/mixin/ae2/client/gui/InterfaceScreenMixin.java @@ -223,7 +223,7 @@ public abstract class InterfaceScreenMixin { int topPos = ((AbstractContainerScreenAccessor) (Object) this).eap$getTopPos(); int imageWidth = ((AbstractContainerScreenAccessor) (Object) this).eap$getImageWidth(); int bx = leftPos + imageWidth + 1; - int by = topPos + 20; + int by = topPos + 70; int spacing = 22; if (eap$divideBy2Button != null) { eap$divideBy2Button.setX(bx); eap$divideBy2Button.setY(by); } if (eap$x2Button != null) { eap$x2Button.setX(bx); eap$x2Button.setY(by + spacing); } diff --git a/src/main/java/com/extendedae_plus/mixin/extendedae/client/gui/GuiExPatternProviderMixin.java b/src/main/java/com/extendedae_plus/mixin/extendedae/client/gui/GuiExPatternProviderMixin.java index 5c1f520..40e3708 100644 --- a/src/main/java/com/extendedae_plus/mixin/extendedae/client/gui/GuiExPatternProviderMixin.java +++ b/src/main/java/com/extendedae_plus/mixin/extendedae/client/gui/GuiExPatternProviderMixin.java @@ -340,7 +340,7 @@ public abstract class GuiExPatternProviderMixin extends PatternProviderScreen Date: Thu, 25 Sep 2025 17:17:55 +0800 Subject: [PATCH 27/43] =?UTF-8?q?ME=E6=8E=A5=E5=8F=A3=E5=A2=9E=E5=8A=A0?= =?UTF-8?q?=E5=8D=87=E7=BA=A7=E6=A7=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../helpers/InterfaceLogicUpgradesMixin.java | 85 +++++++++++++++++++ .../ae2/menu/InterfaceMenuUpgradesMixin.java | 47 ++++++++++ .../resources/extendedae_plus.mixins.json | 2 + 3 files changed, 134 insertions(+) create mode 100644 src/main/java/com/extendedae_plus/mixin/ae2/helpers/InterfaceLogicUpgradesMixin.java create mode 100644 src/main/java/com/extendedae_plus/mixin/ae2/menu/InterfaceMenuUpgradesMixin.java diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/helpers/InterfaceLogicUpgradesMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/helpers/InterfaceLogicUpgradesMixin.java new file mode 100644 index 0000000..ec55abd --- /dev/null +++ b/src/main/java/com/extendedae_plus/mixin/ae2/helpers/InterfaceLogicUpgradesMixin.java @@ -0,0 +1,85 @@ +package com.extendedae_plus.mixin.ae2.helpers; + +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.bridge.CompatUpgradeProvider; +import com.extendedae_plus.compat.UpgradeSlotCompat; +import net.neoforged.fml.ModList; +import com.extendedae_plus.util.ExtendedAELogger; +import net.minecraft.world.item.Item; +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; + +/** + * 为ME接口增加升级槽数量 + * - 没有AppliedFlux时:从1个增加到2个 + * - 有AppliedFlux时:从2个增加到3个 + */ +@Mixin(value = InterfaceLogic.class, priority = 1100, remap = false) +public abstract class InterfaceLogicUpgradesMixin implements CompatUpgradeProvider { + + @Shadow(remap = false) + private IUpgradeInventory upgrades; + + @Unique + private IUpgradeInventory eap$compatUpgrades; + + + /** + * 在AppliedFlux之后进一步增加升级槽数量 + * mixin优先级设置为1100,确保在AppliedFlux的mixin之后执行 + */ + @Inject(method = "(Lappeng/api/networking/IManagedGridNode;Lappeng/helpers/InterfaceLogicHost;Lnet/minecraft/world/item/Item;I)V", + at = @At("TAIL"), remap = false) + private void eap$expandUpgradesAfterAppliedFlux(IManagedGridNode gridNode, InterfaceLogicHost host, Item is, int slots, CallbackInfo ci) { + boolean hasAppliedFlux = ModList.get().isLoaded("appflux"); + int currentSlots = this.upgrades.size(); + int targetSlots; + + if (hasAppliedFlux) { + // AppliedFlux已经将升级槽从1增加到2,我们再增加1个变成3 + targetSlots = 3; + ExtendedAELogger.LOGGER.debug("[ME接口] 检测到AppliedFlux,当前升级槽: {}, 目标升级槽: {}", currentSlots, targetSlots); + } else { + // 没有AppliedFlux,从原始的1增加到2 + targetSlots = 2; + ExtendedAELogger.LOGGER.debug("[ME接口] 未检测到AppliedFlux,当前升级槽: {}, 目标升级槽: {}", currentSlots, targetSlots); + } + + // 只有当当前槽数小于目标槽数时才需要扩展 + if (currentSlots < targetSlots) { + this.upgrades = UpgradeInventories.forMachine(is, targetSlots, this::eap$onUpgradesChanged); + ExtendedAELogger.LOGGER.debug("[ME接口] 升级槽已扩展到: {}", targetSlots); + + // 设置兼容升级槽 + if (UpgradeSlotCompat.shouldEnableUpgradeSlots()) { + this.eap$compatUpgrades = this.upgrades; + } + } + } + + @Unique + private void eap$onUpgradesChanged() { + // 调用原始的onUpgradesChanged方法 + try { + // 通过反射调用原始方法,因为它是protected的 + var method = InterfaceLogic.class.getDeclaredMethod("onUpgradesChanged"); + method.setAccessible(true); + method.invoke(this); + } catch (Exception e) { + ExtendedAELogger.LOGGER.error("[ME接口] 调用onUpgradesChanged失败", e); + } + } + + @Override + public IUpgradeInventory eap$getCompatUpgrades() { + return this.eap$compatUpgrades != null ? this.eap$compatUpgrades : this.upgrades; + } +} diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/menu/InterfaceMenuUpgradesMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/menu/InterfaceMenuUpgradesMixin.java new file mode 100644 index 0000000..5292d77 --- /dev/null +++ b/src/main/java/com/extendedae_plus/mixin/ae2/menu/InterfaceMenuUpgradesMixin.java @@ -0,0 +1,47 @@ +package com.extendedae_plus.mixin.ae2.menu; + +import appeng.helpers.InterfaceLogicHost; +import appeng.menu.AEBaseMenu; +import appeng.menu.ToolboxMenu; +import appeng.menu.implementations.InterfaceMenu; +import com.extendedae_plus.bridge.IUpgradableMenu; +import com.extendedae_plus.compat.UpgradeSlotCompat; +import com.extendedae_plus.bridge.CompatUpgradeProvider; +import com.extendedae_plus.util.ExtendedAELogger; +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; + +/** + * 为ME接口菜单添加升级槽支持,确保与AppliedFlux的兼容性 + */ +@Mixin(value = InterfaceMenu.class, priority = 900, remap = false) +public abstract class InterfaceMenuUpgradesMixin extends AEBaseMenu implements IUpgradableMenu { + + @Unique + private ToolboxMenu eap$toolbox; + + @Inject(method = "(Lnet/minecraft/world/inventory/MenuType;ILnet/minecraft/world/entity/player/Inventory;Lappeng/helpers/InterfaceLogicHost;)V", + at = @At("TAIL")) + private void eap$initUpgrades(MenuType menuType, int id, Inventory playerInventory, InterfaceLogicHost host, CallbackInfo ci) { + this.eap$toolbox = new ToolboxMenu(this); + + // InterfaceMenu已经继承自UpgradeableMenu,会自动处理升级槽 + // 这里只需要记录日志,不需要重复设置升级槽 + ExtendedAELogger.LOGGER.debug("[ME接口][菜单] 升级槽已由UpgradeableMenu自动处理,当前升级槽数量: {}", + host.getInterfaceLogic().getUpgrades().size()); + } + + @Override + public ToolboxMenu getToolbox() { + return this.eap$toolbox; + } + + public InterfaceMenuUpgradesMixin(MenuType menuType, int id, Inventory playerInventory, Object host) { + super(menuType, id, playerInventory, host); + } +} diff --git a/src/main/resources/extendedae_plus.mixins.json b/src/main/resources/extendedae_plus.mixins.json index 875b677..102ab73 100644 --- a/src/main/resources/extendedae_plus.mixins.json +++ b/src/main/resources/extendedae_plus.mixins.json @@ -28,9 +28,11 @@ "ae2.compat.PatternProviderLogicCompatMixin", "ae2.helpers.InterfaceLogicChannelCardMixin", "ae2.helpers.InterfaceLogicTickerMixin", + "ae2.helpers.InterfaceLogicUpgradesMixin", "ae2.helpers.PatternProviderLogicAdvancedMixin", "ae2.helpers.PatternProviderLogicDoublingMixin", "ae2.helpers.patternprovider.PatternProviderLogicTickerMixin", + "ae2.menu.InterfaceMenuUpgradesMixin", "ae2.menu.PatternProviderMenuUpgradesMixin", "ae2.parts.automation.IOBusPartChannelCardMixin", "ae2.parts.storagebus.StorageBusPartChannelCardMixin", From 6d7e82e7938f349a8f7b2ed115b64d1690f757f6 Mon Sep 17 00:00:00 2001 From: GaLicn <133291877+GaLicn@users.noreply.github.com> Date: Thu, 25 Sep 2025 18:01:49 +0800 Subject: [PATCH 28/43] =?UTF-8?q?=E6=A0=B7=E6=9D=BF=E4=BE=9B=E5=BA=94?= =?UTF-8?q?=E5=99=A8=E5=A2=9E=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 --- .../PatternProviderLogicUpgradesMixin.java | 279 ++++++++++++++++++ .../PatternProviderMenuUpgradesMixin.java | 20 +- .../resources/extendedae_plus.mixins.json | 1 + 3 files changed, 294 insertions(+), 6 deletions(-) create mode 100644 src/main/java/com/extendedae_plus/mixin/ae2/helpers/PatternProviderLogicUpgradesMixin.java diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/helpers/PatternProviderLogicUpgradesMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/helpers/PatternProviderLogicUpgradesMixin.java new file mode 100644 index 0000000..8b2f41c --- /dev/null +++ b/src/main/java/com/extendedae_plus/mixin/ae2/helpers/PatternProviderLogicUpgradesMixin.java @@ -0,0 +1,279 @@ +package com.extendedae_plus.mixin.ae2.helpers; + +import appeng.api.networking.IManagedGridNode; +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.util.ExtendedAELogger; +import net.minecraft.core.HolderLookup; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.world.item.ItemStack; +import net.neoforged.fml.ModList; +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.lang.reflect.Method; +import java.lang.reflect.Field; +import java.util.List; + +/** + * 为样板供应器添加升级槽支持 + * - 没有 AppliedFlux 时:添加 1 个升级槽 + * - 有 AppliedFlux 时:在其基础上再增加 1 个(总共 2 个) + * + * 优先级 1100 确保在 AppliedFlux (默认优先级 1000) 之后执行 + */ +@Mixin(value = PatternProviderLogic.class, priority = 1100, remap = false) +public abstract class PatternProviderLogicUpgradesMixin { + + @Final + @Shadow + private PatternProviderLogicHost host; + + @Final + @Shadow + private IManagedGridNode mainNode; + + @Unique + private IUpgradeInventory eap$upgrades = UpgradeInventories.empty(); + + @Unique + private boolean eap$hasAppliedFlux = false; + + @Unique + private boolean eap$upgradesInitialized = false; + + @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) { + try { + // 检测是否安装了 AppliedFlux + this.eap$hasAppliedFlux = ModList.get().isLoaded("appflux"); + ExtendedAELogger.LOGGER.debug("[样板供应器][升级槽] AppliedFlux 状态: {}", eap$hasAppliedFlux); + + if (eap$hasAppliedFlux) { + // AppliedFlux 已安装,尝试获取并扩展其升级槽 + eap$extendAppliedFluxUpgrades(); + } // 未安装 AppliedFlux 的情况由 CompatMixin 负责创建兼容升级槽 + } catch (Throwable t) { + ExtendedAELogger.LOGGER.error("[样板供应器][升级槽] 初始化失败", t); + } + } + + @Unique + private void eap$extendAppliedFluxUpgrades() { + try { + // 通过反射直接读取 AppliedFlux Mixin 注入的字段:af_upgrades + IUpgradeInventory existingUpgrades = null; + try { + Field f = this.getClass().getDeclaredField("af_upgrades"); + f.setAccessible(true); + existingUpgrades = (IUpgradeInventory) f.get(this); + } catch (Throwable t) { + ExtendedAELogger.LOGGER.debug("[样板供应器][升级槽] 读取 AF 升级槽字段失败: {}", t.getMessage()); + } + + if (existingUpgrades != null && existingUpgrades != UpgradeInventories.empty()) { + // AppliedFlux 已经创建了升级槽 + int currentSlots = existingUpgrades.size(); + int targetSlots = 2; // AppliedFlux 1个 + 我们 1个 = 2个 + + ExtendedAELogger.LOGGER.debug("[样板供应器][升级槽] AppliedFlux 升级槽数量: {}, 目标: {}", + currentSlots, targetSlots); + + if (currentSlots < targetSlots) { + // 需要扩展升级槽 + // 先保存现有物品 + ItemStack[] savedItems = new ItemStack[currentSlots]; + for (int i = 0; i < currentSlots; i++) { + savedItems[i] = existingUpgrades.getStackInSlot(i).copy(); + } + + // 创建新的升级槽(更多槽位) + this.eap$upgrades = UpgradeInventories.forMachine( + host.getTerminalIcon().getItem(), + targetSlots, + this::eap$onUpgradesChanged + ); + + // 恢复原有物品 + for (int i = 0; i < savedItems.length; i++) { + this.eap$upgrades.setItemDirect(i, savedItems[i]); + } + // 将 AF 的字段指向我们新的升级槽,保持其服务与 NBT 钩子一致 + try { + Field f = this.getClass().getDeclaredField("af_upgrades"); + f.setAccessible(true); + f.set(this, this.eap$upgrades); + } catch (Throwable t) { + ExtendedAELogger.LOGGER.debug("[样板供应器][升级槽] 绑定 AF 升级槽字段失败: {}", t.getMessage()); + } + + this.eap$upgradesInitialized = true; + ExtendedAELogger.LOGGER.debug("[样板供应器][升级槽] 扩展到 {} 个升级槽", targetSlots); + } else { + // AppliedFlux 或其他模组已经提供了足够的槽位 + this.eap$upgrades = existingUpgrades; + this.eap$upgradesInitialized = true; + ExtendedAELogger.LOGGER.debug("[样板供应器][升级槽] 使用现有 {} 个升级槽", currentSlots); + } + } else { + // AppliedFlux 还没初始化升级槽,或者出了问题,我们创建默认的 + this.eap$upgrades = UpgradeInventories.forMachine( + host.getTerminalIcon().getItem(), + 2, + this::eap$onUpgradesChanged + ); + // 同步 AF 字段 + try { + Field f = this.getClass().getDeclaredField("af_upgrades"); + f.setAccessible(true); + f.set(this, this.eap$upgrades); + } catch (Throwable t) { + ExtendedAELogger.LOGGER.debug("[样板供应器][升级槽] 初始化时绑定 AF 升级槽字段失败: {}", t.getMessage()); + } + this.eap$upgradesInitialized = true; + ExtendedAELogger.LOGGER.debug("[样板供应器][升级槽] 创建 2 个升级槽(AppliedFlux 未初始化)"); + } + } catch (Throwable t) { + ExtendedAELogger.LOGGER.error("[样板供应器][升级槽] 扩展 AppliedFlux 升级槽失败", t); + // 失败时创建默认数量 + this.eap$upgrades = UpgradeInventories.forMachine( + host.getTerminalIcon().getItem(), + 2, + this::eap$onUpgradesChanged + ); + // 同步 AF 字段(最佳努力) + try { + Field f = this.getClass().getDeclaredField("af_upgrades"); + f.setAccessible(true); + f.set(this, this.eap$upgrades); + } catch (Throwable ignored) {} + this.eap$upgradesInitialized = true; + } + } + + @Unique + private void eap$onUpgradesChanged() { + try { + this.host.saveChanges(); + + // 如果 AppliedFlux 安装了,也调用其原始的 onUpgradesChanged 方法 + if (eap$hasAppliedFlux) { + try { + Method afMethod = this.getClass().getDeclaredMethod("af_onUpgradesChanged"); + afMethod.setAccessible(true); + afMethod.invoke(this); + } catch (NoSuchMethodException e) { + // AppliedFlux 的方法不存在,这是正常的 + } catch (Throwable t) { + ExtendedAELogger.LOGGER.debug("[样板供应器][升级槽] 调用 AppliedFlux onUpgradesChanged 失败: {}", t.getMessage()); + } + } + + ExtendedAELogger.LOGGER.debug("[样板供应器][升级槽] 升级槽内容已变更"); + } catch (Throwable t) { + ExtendedAELogger.LOGGER.error("[样板供应器][升级槽] onUpgradesChanged 处理失败", t); + } + } + + + + @Inject(method = "writeToNBT", at = @At("TAIL")) + private void eap$saveUpgrades(CompoundTag tag, HolderLookup.Provider registries, CallbackInfo ci) { + try { + // 已安装 AF 时交由 AF 的 mixin 处理,避免重复写入 + if (eap$hasAppliedFlux) { + return; + } + if (eap$upgradesInitialized && this.eap$upgrades != null && this.eap$upgrades != UpgradeInventories.empty()) { + // 根据是否有 AppliedFlux 使用不同的 NBT 键 + if (eap$hasAppliedFlux) { + // AppliedFlux 使用 "upgrades" 键,我们使用 "eap_upgrades" 避免冲突 + this.eap$upgrades.writeToNBT(tag, "eap_upgrades", registries); + } else { + // 没有 AppliedFlux,使用标准键 + this.eap$upgrades.writeToNBT(tag, "upgrades", registries); + } + ExtendedAELogger.LOGGER.debug("[样板供应器][升级槽] 保存升级槽到 NBT"); + } + } catch (Throwable t) { + ExtendedAELogger.LOGGER.error("[样板供应器][升级槽] 保存升级槽失败", t); + } + } + + @Inject(method = "readFromNBT", at = @At("TAIL")) + private void eap$loadUpgrades(CompoundTag tag, HolderLookup.Provider registries, CallbackInfo ci) { + try { + // 重新检测 AppliedFlux 状态(可能在世界加载时状态有变化) + this.eap$hasAppliedFlux = ModList.get().isLoaded("appflux"); + + if (!eap$upgradesInitialized) { + // 如果还没初始化,先初始化 + if (eap$hasAppliedFlux) { + eap$extendAppliedFluxUpgrades(); + } else { + this.eap$upgrades = UpgradeInventories.forMachine( + host.getTerminalIcon().getItem(), + 1, + this::eap$onUpgradesChanged + ); + this.eap$upgradesInitialized = true; + } + } + + // 已安装 AF 时,由 AF 自行从 "upgrades" 读取;我们只处理无 AF 情况 + if (!eap$hasAppliedFlux && this.eap$upgrades != null && this.eap$upgrades != UpgradeInventories.empty()) { + if (tag.contains("upgrades")) { + this.eap$upgrades.readFromNBT(tag, "upgrades", registries); + ExtendedAELogger.LOGGER.debug("[样板供应器][升级槽] 从 upgrades 加载升级槽"); + } + } + } catch (Throwable t) { + ExtendedAELogger.LOGGER.error("[样板供应器][升级槽] 加载升级槽失败", t); + } + } + + @Inject(method = "addDrops", at = @At("TAIL")) + private void eap$dropUpgrades(List drops, CallbackInfo ci) { + try { + // AF 已安装时交由其自身处理掉落 + if (eap$hasAppliedFlux) { + return; + } + if (eap$upgradesInitialized && this.eap$upgrades != null) { + for (var is : this.eap$upgrades) { + if (!is.isEmpty()) { + drops.add(is); + } + } + ExtendedAELogger.LOGGER.debug("[样板供应器][升级槽] 添加升级槽物品到掉落列表"); + } + } catch (Throwable t) { + ExtendedAELogger.LOGGER.error("[样板供应器][升级槽] 添加掉落失败", t); + } + } + + @Inject(method = "clearContent", at = @At("TAIL")) + private void eap$clearUpgrades(CallbackInfo ci) { + try { + // AF 已安装时交由其自身处理清理 + if (eap$hasAppliedFlux) { + return; + } + if (eap$upgradesInitialized && this.eap$upgrades != null) { + this.eap$upgrades.clear(); + ExtendedAELogger.LOGGER.debug("[样板供应器][升级槽] 清空升级槽"); + } + } catch (Throwable t) { + ExtendedAELogger.LOGGER.error("[样板供应器][升级槽] 清空升级槽失败", t); + } + } +} 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 17f2f60..385bd31 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.implementations.PatternProviderMenu; import com.extendedae_plus.bridge.IUpgradableMenu; import com.extendedae_plus.compat.UpgradeSlotCompat; import com.extendedae_plus.bridge.CompatUpgradeProvider; +import appeng.api.upgrades.IUpgradeInventory; import appeng.api.upgrades.IUpgradeableObject; import net.minecraft.world.entity.player.Inventory; import net.minecraft.world.inventory.MenuType; @@ -32,13 +33,20 @@ public abstract class PatternProviderMenuUpgradesMixin extends AEBaseMenu implem at = @At("TAIL")) private void eap$initUpgrades(MenuType menuType, int id, Inventory playerInventory, PatternProviderLogicHost host, CallbackInfo ci) { this.eap$toolbox = new ToolboxMenu(this); - if (UpgradeSlotCompat.shouldEnableUpgradeSlots()) { - // 未安装 appflux:使用我们提供的升级槽 - ExtendedAELogger.LOGGER.debug("[样板供应器][菜单] 注入升级槽: 使用自带 compat 槽"); + + // 现在 PatternProviderLogic 始终实现 IUpgradeableObject(通过我们的 mixin) + if (this.logic instanceof IUpgradeableObject upgradeableLogic) { + IUpgradeInventory upgrades = upgradeableLogic.getUpgrades(); + if (upgrades != null && upgrades != appeng.api.upgrades.UpgradeInventories.empty()) { + ExtendedAELogger.LOGGER.debug("[样板供应器][菜单] 设置升级槽 UI,槽位数: {}", upgrades.size()); + this.setupUpgrades(upgrades); + } else { + ExtendedAELogger.LOGGER.debug("[样板供应器][菜单] 升级槽为空或未初始化"); + } + } else if (UpgradeSlotCompat.shouldEnableUpgradeSlots()) { + // 备用方案:使用 compat 升级槽 + ExtendedAELogger.LOGGER.debug("[样板供应器][菜单] 备用方案:使用 compat 升级槽"); this.setupUpgrades(((CompatUpgradeProvider) this.logic).eap$getCompatUpgrades()); - } else { - // 安装 appflux:AE2/AppliedFlux 已在其原始构造流程中添加升级槽,这里避免重复注入导致界面重复渲染 - ExtendedAELogger.LOGGER.debug("[样板供应器][菜单] 跳过注入升级槽: 由 AE2/AppliedFlux 负责渲染"); } } diff --git a/src/main/resources/extendedae_plus.mixins.json b/src/main/resources/extendedae_plus.mixins.json index 102ab73..1aec0a9 100644 --- a/src/main/resources/extendedae_plus.mixins.json +++ b/src/main/resources/extendedae_plus.mixins.json @@ -31,6 +31,7 @@ "ae2.helpers.InterfaceLogicUpgradesMixin", "ae2.helpers.PatternProviderLogicAdvancedMixin", "ae2.helpers.PatternProviderLogicDoublingMixin", + "ae2.helpers.PatternProviderLogicUpgradesMixin", "ae2.helpers.patternprovider.PatternProviderLogicTickerMixin", "ae2.menu.InterfaceMenuUpgradesMixin", "ae2.menu.PatternProviderMenuUpgradesMixin", From 655c17c7f99f837f5f694bc466744d6add2d1a98 Mon Sep 17 00:00:00 2001 From: GaLicn <133291877+GaLicn@users.noreply.github.com> Date: Thu, 25 Sep 2025 18:13:02 +0800 Subject: [PATCH 29/43] =?UTF-8?q?=E6=A0=B7=E6=9D=BF=E4=BE=9B=E5=BA=94?= =?UTF-8?q?=E5=99=A8=E5=A2=9E=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 --- .../menu/AEBaseMenuUpgradesDedupMixin.java | 34 +++++++++++++++++++ .../PatternProviderMenuUpgradesMixin.java | 17 +++------- .../resources/extendedae_plus.mixins.json | 1 + 3 files changed, 40 insertions(+), 12 deletions(-) create mode 100644 src/main/java/com/extendedae_plus/mixin/ae2/menu/AEBaseMenuUpgradesDedupMixin.java diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/menu/AEBaseMenuUpgradesDedupMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/menu/AEBaseMenuUpgradesDedupMixin.java new file mode 100644 index 0000000..6a3fc9a --- /dev/null +++ b/src/main/java/com/extendedae_plus/mixin/ae2/menu/AEBaseMenuUpgradesDedupMixin.java @@ -0,0 +1,34 @@ +package com.extendedae_plus.mixin.ae2.menu; + +import appeng.menu.AEBaseMenu; +import appeng.menu.SlotSemantics; +import appeng.menu.implementations.PatternProviderMenu; +import appeng.api.upgrades.IUpgradeInventory; +import com.extendedae_plus.util.ExtendedAELogger; +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; + +/** + * 去重样板供应器升级槽的菜单注入: + * - 多个模组(AppliedFlux、expandedae等)都会在 PatternProviderMenu 的构造中调用 setupUpgrades, + * 导致同一套升级槽 UI 被重复添加(渲染为四个)。 + * - 拦截 AEBaseMenu.setupUpgrades,在目标菜单为 PatternProviderMenu 且已存在升级槽时取消后续注入。 + */ +@Mixin(value = AEBaseMenu.class, priority = 2000, remap = false) +public abstract class AEBaseMenuUpgradesDedupMixin { + + @Inject(method = "setupUpgrades(Lappeng/api/upgrades/IUpgradeInventory;)V", at = @At("HEAD"), cancellable = true) + private void eap$dedupPatternProviderUpgradeSlots(IUpgradeInventory upgrades, CallbackInfo ci) { + // 仅对样板供应器菜单去重,避免影响其他菜单 + if (((Object) this) instanceof PatternProviderMenu) { + var self = (AEBaseMenu) (Object) this; + var existing = self.getSlots(SlotSemantics.UPGRADE); + if (existing != null && !existing.isEmpty()) { + ExtendedAELogger.LOGGER.debug("[样板供应器][菜单] 检测到升级槽已存在({}),取消重复注入", existing.size()); + ci.cancel(); + } + } + } +} 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 385bd31..3d67e3b 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 @@ -34,19 +34,12 @@ public abstract class PatternProviderMenuUpgradesMixin extends AEBaseMenu implem private void eap$initUpgrades(MenuType menuType, int id, Inventory playerInventory, PatternProviderLogicHost host, CallbackInfo ci) { this.eap$toolbox = new ToolboxMenu(this); - // 现在 PatternProviderLogic 始终实现 IUpgradeableObject(通过我们的 mixin) - if (this.logic instanceof IUpgradeableObject upgradeableLogic) { - IUpgradeInventory upgrades = upgradeableLogic.getUpgrades(); - if (upgrades != null && upgrades != appeng.api.upgrades.UpgradeInventories.empty()) { - ExtendedAELogger.LOGGER.debug("[样板供应器][菜单] 设置升级槽 UI,槽位数: {}", upgrades.size()); - this.setupUpgrades(upgrades); - } else { - ExtendedAELogger.LOGGER.debug("[样板供应器][菜单] 升级槽为空或未初始化"); - } - } else if (UpgradeSlotCompat.shouldEnableUpgradeSlots()) { - // 备用方案:使用 compat 升级槽 - ExtendedAELogger.LOGGER.debug("[样板供应器][菜单] 备用方案:使用 compat 升级槽"); + // 当未安装 AppliedFlux 时,我们负责注入升级槽;安装了 AF 则由 AF 的菜单 Mixin 负责,避免重复渲染 + if (UpgradeSlotCompat.shouldEnableUpgradeSlots()) { + ExtendedAELogger.LOGGER.debug("[样板供应器][菜单] 注入升级槽: 使用自带 compat 槽"); this.setupUpgrades(((CompatUpgradeProvider) this.logic).eap$getCompatUpgrades()); + } else { + ExtendedAELogger.LOGGER.debug("[样板供应器][菜单] 跳过注入升级槽: 由 AE2/AppliedFlux 负责渲染"); } } diff --git a/src/main/resources/extendedae_plus.mixins.json b/src/main/resources/extendedae_plus.mixins.json index 1aec0a9..dfda552 100644 --- a/src/main/resources/extendedae_plus.mixins.json +++ b/src/main/resources/extendedae_plus.mixins.json @@ -34,6 +34,7 @@ "ae2.helpers.PatternProviderLogicUpgradesMixin", "ae2.helpers.patternprovider.PatternProviderLogicTickerMixin", "ae2.menu.InterfaceMenuUpgradesMixin", + "ae2.menu.AEBaseMenuUpgradesDedupMixin", "ae2.menu.PatternProviderMenuUpgradesMixin", "ae2.parts.automation.IOBusPartChannelCardMixin", "ae2.parts.storagebus.StorageBusPartChannelCardMixin", From 9b2c214d822d21708886e698ea34414da6c798ee Mon Sep 17 00:00:00 2001 From: GaLicn <133291877+GaLicn@users.noreply.github.com> Date: Thu, 25 Sep 2025 18:21:13 +0800 Subject: [PATCH 30/43] =?UTF-8?q?=E9=A2=91=E9=81=93=E5=8D=A1=E5=AE=8C?= =?UTF-8?q?=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 2 +- .../mixin/ae2/helpers/InterfaceLogicUpgradesMixin.java | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 30753b9..438df2a 100644 --- a/build.gradle +++ b/build.gradle @@ -155,7 +155,7 @@ dependencies { compileOnly "curse.maven:ae2-jei-integration-1074338:5748513" // runtime test - runtimeOnly "curse.maven:applied-flux-965012:5614830" + //runtimeOnly "curse.maven:applied-flux-965012:5614830" runtimeOnly "de.mari_023:ae2wtlib:19.2.1" runtimeOnly "curse.maven:jade-324717:5427817" runtimeOnly "curse.maven:mega-cells-622112:6005043" diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/helpers/InterfaceLogicUpgradesMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/helpers/InterfaceLogicUpgradesMixin.java index ec55abd..471d053 100644 --- a/src/main/java/com/extendedae_plus/mixin/ae2/helpers/InterfaceLogicUpgradesMixin.java +++ b/src/main/java/com/extendedae_plus/mixin/ae2/helpers/InterfaceLogicUpgradesMixin.java @@ -10,8 +10,10 @@ import com.extendedae_plus.compat.UpgradeSlotCompat; import net.neoforged.fml.ModList; import com.extendedae_plus.util.ExtendedAELogger; import net.minecraft.world.item.Item; +import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Mutable; import org.spongepowered.asm.mixin.Unique; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; @@ -26,6 +28,8 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; public abstract class InterfaceLogicUpgradesMixin implements CompatUpgradeProvider { @Shadow(remap = false) + @Final + @Mutable private IUpgradeInventory upgrades; @Unique From 890a6c24edd214a071179238acbba2bb04015b70 Mon Sep 17 00:00:00 2001 From: GaLicn <133291877+GaLicn@users.noreply.github.com> Date: Thu, 25 Sep 2025 18:42:38 +0800 Subject: [PATCH 31/43] =?UTF-8?q?=E5=88=A0=E6=97=A5=E5=BF=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 2 +- .../compat/AppliedFluxCompat.java | 4 +-- .../ae2/client/gui/AEBaseScreenMixin.java | 8 ----- .../ae2/client/gui/InterfaceScreenMixin.java | 2 -- .../PatternProviderScreenUpgradesMixin.java | 11 ------- .../PatternProviderLogicCompatMixin.java | 29 ------------------- .../InterfaceLogicChannelCardMixin.java | 7 ----- .../helpers/InterfaceLogicUpgradesMixin.java | 3 -- .../PatternProviderLogicUpgradesMixin.java | 15 ---------- .../menu/AEBaseMenuUpgradesDedupMixin.java | 1 - .../ae2/menu/InterfaceMenuUpgradesMixin.java | 3 -- .../PatternProviderMenuUpgradesMixin.java | 2 -- .../automation/IOBusPartChannelCardMixin.java | 4 --- .../StorageBusPartChannelCardMixin.java | 4 --- .../network/CraftingMonitorJumpC2SPacket.java | 12 -------- .../network/ScalePatternsC2SPacket.java | 3 -- .../util/PatternProviderDataUtil.java | 2 -- .../wireless/WirelessSlaveLink.java | 16 ---------- 18 files changed, 3 insertions(+), 125 deletions(-) diff --git a/build.gradle b/build.gradle index 438df2a..30753b9 100644 --- a/build.gradle +++ b/build.gradle @@ -155,7 +155,7 @@ dependencies { compileOnly "curse.maven:ae2-jei-integration-1074338:5748513" // runtime test - //runtimeOnly "curse.maven:applied-flux-965012:5614830" + runtimeOnly "curse.maven:applied-flux-965012:5614830" runtimeOnly "de.mari_023:ae2wtlib:19.2.1" runtimeOnly "curse.maven:jade-324717:5427817" runtimeOnly "curse.maven:mega-cells-622112:6005043" diff --git a/src/main/java/com/extendedae_plus/compat/AppliedFluxCompat.java b/src/main/java/com/extendedae_plus/compat/AppliedFluxCompat.java index 651f5be..a5d4aa9 100644 --- a/src/main/java/com/extendedae_plus/compat/AppliedFluxCompat.java +++ b/src/main/java/com/extendedae_plus/compat/AppliedFluxCompat.java @@ -22,14 +22,14 @@ public final class AppliedFluxCompat { return false; } - // 如果AppliedFlux加载了,假设它会添加升级面板 + // 如果 AppliedFlux 加载了,假设它会添加升级面板 // 这是一个保守的假设,避免冲突 - ExtendedAELogger.LOGGER.debug("[AppliedFlux兼容] 检测到AppliedFlux已加载,假设存在升级面板"); return true; } /** * 检查是否应该跳过添加我们的升级面板 + * 主要用于检测 AppliedFlux 是否已经添加了升级面板 */ public static boolean shouldSkipOurUpgradePanel(PatternProviderScreen screen) { if (!UpgradeSlotCompat.shouldUseLowPriorityMode()) { diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/client/gui/AEBaseScreenMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/client/gui/AEBaseScreenMixin.java index b423345..3c2214f 100644 --- a/src/main/java/com/extendedae_plus/mixin/ae2/client/gui/AEBaseScreenMixin.java +++ b/src/main/java/com/extendedae_plus/mixin/ae2/client/gui/AEBaseScreenMixin.java @@ -75,10 +75,6 @@ public abstract class AEBaseScreenMixin { if (key == null) { return; } - // Debug: 标记一次发送 - try { - LogUtils.getLogger().info("EAP: Send CraftingMonitorJumpC2SPacket: {}", key); - } catch (Throwable ignored2) {} PacketDistributor.sendToServer(new CraftingMonitorJumpC2SPacket(key)); cir.setReturnValue(true); } catch (Throwable ignored) { @@ -109,10 +105,6 @@ public abstract class AEBaseScreenMixin { if (key == null) { return; } - // Debug: 标记一次发送(打开供应器UI) - try { - LogUtils.getLogger().info("EAP: Send CraftingMonitorOpenProviderC2SPacket: {}", key); - } catch (Throwable ignored2) {} PacketDistributor.sendToServer(new CraftingMonitorOpenProviderC2SPacket(key)); cir.setReturnValue(true); } catch (Throwable ignored) { diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/client/gui/InterfaceScreenMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/client/gui/InterfaceScreenMixin.java index 34aa261..e7a4a46 100644 --- a/src/main/java/com/extendedae_plus/mixin/ae2/client/gui/InterfaceScreenMixin.java +++ b/src/main/java/com/extendedae_plus/mixin/ae2/client/gui/InterfaceScreenMixin.java @@ -44,7 +44,6 @@ public abstract class InterfaceScreenMixin { if (!eap$isSupportedInterfaceScreen()) { return; } - try { LogUtils.getLogger().info("[EAP][InterfaceMixin] init tail reached, preparing scale buttons."); } catch (Throwable ignored) {} if (eap$x2Button == null) { eap$x2Button = new ActionEPPButton((b) -> eap$sendAdjustForAllConfigs(false, 2), NewIcon.MULTIPLY2); eap$x2Button.setTooltip(null); @@ -92,7 +91,6 @@ public abstract class InterfaceScreenMixin { if (!accessor.eap$getChildren().contains(eap$x10Button)) accessor.eap$getChildren().add(eap$x10Button); eap$relayoutButtons(); - try { LogUtils.getLogger().info("[EAP][InterfaceMixin] buttons added and laid out."); } catch (Throwable ignored) {} } @Inject(method = "containerTick", at = @At("TAIL")) 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 890223b..977436a 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 @@ -34,18 +34,13 @@ public abstract class PatternProviderScreenUpgradesMixin screen = (PatternProviderScreen) (Object) this; boolean shouldSkip = AppliedFluxCompat.shouldSkipOurUpgradePanel(screen); - com.extendedae_plus.util.ExtendedAELogger.LOGGER.debug("[样板供应器][界面] AppliedFlux兼容性检测结果: shouldSkip={}", shouldSkip); if (shouldSkip) { - com.extendedae_plus.util.ExtendedAELogger.LOGGER.debug("[样板供应器][界面] 检测到AppliedFlux升级面板,跳过添加我们的面板"); } else { - com.extendedae_plus.util.ExtendedAELogger.LOGGER.debug("[样板供应器][界面] 开始添加升级面板"); // 检查是否已经存在upgrades widget try { // 尝试添加升级面板 this.widgets.add("upgrades", new UpgradesPanel(menu.getSlots(SlotSemantics.UPGRADE), this::eap$getCompatibleUpgrades)); - com.extendedae_plus.util.ExtendedAELogger.LOGGER.debug("[样板供应器][界面] 成功添加升级面板"); } catch (IllegalStateException e) { com.extendedae_plus.util.ExtendedAELogger.LOGGER.warn("[样板供应器][界面] 升级面板已存在,跳过添加: {}", e.getMessage()); return; // 如果升级面板已存在,不继续添加其他内容 @@ -89,13 +80,11 @@ public abstract class PatternProviderScreenUpgradesMixin 触发初始化"); eap$compatInitializeChannelLink(); } catch (Throwable t) { ExtendedAELogger.LOGGER.error("[样板供应器] 兼容升级变更处理失败", t); @@ -111,13 +106,11 @@ public abstract class PatternProviderLogicCompatMixin implements CompatUpgradePr if (this instanceof IUpgradeableObject upgradeableObject) { IUpgradeInventory afUpgrades = upgradeableObject.getUpgrades(); if (afUpgrades != null) { - ExtendedAELogger.LOGGER.debug("[样板供应器] 尝试监听AppliedFlux升级变更"); // 我们不能直接修改AppliedFlux的升级槽回调,但我们可以定期检查 // 这里我们先记录一下,实际的检查会在tick中进行 } } } catch (Throwable t) { - ExtendedAELogger.LOGGER.debug("[样板供应器] 监听AppliedFlux升级变更失败: {}", t.getMessage()); } } @@ -208,7 +201,6 @@ public abstract class PatternProviderLogicCompatMixin implements CompatUpgradePr return; } if (mainNode == null || mainNode.getNode() == null) { - ExtendedAELogger.LOGGER.debug("[样板供应器] 初始化跳过:mainNode 或 Node 不可用"); return; } @@ -224,7 +216,6 @@ public abstract class PatternProviderLogicCompatMixin implements CompatUpgradePr // 更安全的方式获取AppliedFlux升级槽 upgrades = eap$getAppliedFluxUpgrades(); if (upgrades != null) { - ExtendedAELogger.LOGGER.debug("[样板供应器] 成功获取 appflux 升级槽,大小: {}", upgrades.size()); } else { ExtendedAELogger.LOGGER.warn("[样板供应器] 无法获取 appflux 升级槽,回退到兼容槽"); upgrades = this.eap$compatUpgrades; @@ -236,12 +227,10 @@ public abstract class PatternProviderLogicCompatMixin implements CompatUpgradePr } else { // 未安装appflux:使用我们的兼容升级槽 upgrades = this.eap$compatUpgrades; - ExtendedAELogger.LOGGER.debug("[样板供应器] 使用自带升级槽(未安装 appflux): {}", upgrades != null); } // 双重保险:如果主要方式失败,尝试备用方式 if (upgrades == null || !eap$hasChannelCard(upgrades)) { - ExtendedAELogger.LOGGER.debug("[样板供应器] 主升级槽无频道卡,尝试备用方式"); if (UpgradeSlotCompat.shouldEnableUpgradeSlots()) { // 如果我们的槽无频道卡,尝试检查是否有AppliedFlux的槽 @@ -249,16 +238,13 @@ public abstract class PatternProviderLogicCompatMixin implements CompatUpgradePr IUpgradeInventory backupUpgrades = eap$getAppliedFluxUpgrades(); if (backupUpgrades != null && eap$hasChannelCard(backupUpgrades)) { upgrades = backupUpgrades; - ExtendedAELogger.LOGGER.debug("[样板供应器] 使用备用 appflux 升级槽"); } } catch (Throwable t) { - ExtendedAELogger.LOGGER.debug("[样板供应器] 备用升级槽检查失败: {}", t.getMessage()); } } else { // 如果AppliedFlux的槽无频道卡,尝试我们的兼容槽 if (this.eap$compatUpgrades != null && eap$hasChannelCard(this.eap$compatUpgrades)) { upgrades = this.eap$compatUpgrades; - ExtendedAELogger.LOGGER.debug("[样板供应器] 使用备用兼容升级槽"); } } } @@ -268,14 +254,12 @@ public abstract class PatternProviderLogicCompatMixin implements CompatUpgradePr if (!stack.isEmpty() && stack.getItem() == ModItems.CHANNEL_CARD.get()) { channel = ChannelCardItem.getChannel(stack); found = true; - ExtendedAELogger.LOGGER.debug("[样板供应器] 检测到频道卡,频道={} ", channel); break; } } } if (!found) { - ExtendedAELogger.LOGGER.debug("[样板供应器] 未发现频道卡 -> 断开无线"); if (eap$compatLink != null) { eap$compatLink.setFrequency(0L); eap$compatLink.updateStatus(); @@ -292,7 +276,6 @@ public abstract class PatternProviderLogicCompatMixin implements CompatUpgradePr if (gc != null && !gc.isInWorld()) { var other = gc.getOtherSide(node); if (other != null && other.getOwner() instanceof com.extendedae_plus.wireless.IWirelessEndpoint) { - ExtendedAELogger.LOGGER.debug("[样板供应器] 兜底销毁残留无线直连: {} -> {}", node, other); gc.destroy(); try { grid.getTickManager().wakeDevice(node); } catch (Throwable ignored2) {} try { if (other.getGrid() != null) { other.getGrid().getTickManager().wakeDevice(other); } } catch (Throwable ignored2) {} @@ -312,7 +295,6 @@ public abstract class PatternProviderLogicCompatMixin implements CompatUpgradePr eap$compatLink.setFrequency(channel); eap$compatLink.updateStatus(); eap$compatLastChannel = channel; // 记录当前频道 - ExtendedAELogger.LOGGER.debug("[样板供应器] 设置频道={} 连接状态={}", channel, eap$compatLink.isConnected()); try { host.saveChanges(); } catch (Throwable ignored) {} mainNode.ifPresent((grid, node) -> { try { grid.getTickManager().wakeDevice(node); } catch (Throwable ignored) {} @@ -371,7 +353,6 @@ public abstract class PatternProviderLogicCompatMixin implements CompatUpgradePr if (!stack.isEmpty() && stack.getItem() == ModItems.CHANNEL_CARD.get()) { long newChannel = ChannelCardItem.getChannel(stack); if (newChannel != eap$compatLastChannel) { - ExtendedAELogger.LOGGER.debug("[样板供应器] 检测到频道变化: {} -> {}", eap$compatLastChannel, newChannel); eap$compatLastChannel = -1; // 强制重新初始化 eap$compatHasInitialized = false; eap$compatInitializeChannelLink(); @@ -381,13 +362,11 @@ public abstract class PatternProviderLogicCompatMixin implements CompatUpgradePr } } else if (eap$compatLastChannel != 0L) { // 频道卡被移除 - ExtendedAELogger.LOGGER.debug("[样板供应器] 频道卡被移除"); eap$compatLastChannel = -1; eap$compatHasInitialized = false; eap$compatInitializeChannelLink(); } } catch (Throwable t) { - ExtendedAELogger.LOGGER.debug("[样板供应器] 延迟初始化检查失败: {}", t.getMessage()); } } } @@ -454,25 +433,17 @@ public abstract class PatternProviderLogicCompatMixin implements CompatUpgradePr @Unique private IUpgradeInventory eap$getAppliedFluxUpgrades() { try { - ExtendedAELogger.LOGGER.debug("[样板供应器] 尝试获取AppliedFlux升级槽"); - ExtendedAELogger.LOGGER.debug("[样板供应器] this instanceof IUpgradeableObject: {}", this instanceof IUpgradeableObject); // 检查当前对象是否实现了IUpgradeableObject接口 if (this instanceof IUpgradeableObject upgradeableObject) { IUpgradeInventory upgrades = upgradeableObject.getUpgrades(); - ExtendedAELogger.LOGGER.debug("[样板供应器] AppliedFlux升级槽: {}", upgrades); - ExtendedAELogger.LOGGER.debug("[样板供应器] 兼容升级槽: {}", this.eap$compatUpgrades); - ExtendedAELogger.LOGGER.debug("[样板供应器] 是否为同一对象: {}", upgrades == this.eap$compatUpgrades); // 确保这不是我们自己的兼容升级槽 if (upgrades != null && upgrades != this.eap$compatUpgrades) { - ExtendedAELogger.LOGGER.debug("[样板供应器] 成功获取AppliedFlux升级槽,大小: {}", upgrades.size()); return upgrades; } else { - ExtendedAELogger.LOGGER.debug("[样板供应器] AppliedFlux升级槽无效或与兼容槽相同"); } } else { - ExtendedAELogger.LOGGER.debug("[样板供应器] 当前对象未实现IUpgradeableObject接口"); } } catch (Throwable t) { ExtendedAELogger.LOGGER.error("[样板供应器] 获取AppliedFlux升级槽时出错", t); 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 41eb689..3eee600 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 @@ -51,7 +51,6 @@ public abstract class InterfaceLogicChannelCardMixin implements InterfaceWireles @Inject(method = "onUpgradesChanged", at = @At("TAIL"), remap = false) private void eap$onUpgradesChangedTail(CallbackInfo ci) { // 升级变更时重置标志并尝试初始化 - ExtendedAELogger.LOGGER.debug("[服务端] Interface onUpgradesChanged -> 重置初始化状态"); eap$lastChannel = -1; eap$hasInitialized = false; eap$initializeChannelLink(); @@ -60,7 +59,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,等待网格完成引导 @@ -105,7 +103,6 @@ public abstract class InterfaceLogicChannelCardMixin implements InterfaceWireles // 仅要求节点对象可用;不要依赖 isActive(无线连接本身会建立连接与激活节点) if (mainNode == null || mainNode.getNode() == null) { - ExtendedAELogger.LOGGER.debug("[服务端] Interface 初始化频道链接跳过:mainNode 或其 Node 不可用"); return; } @@ -125,7 +122,6 @@ public abstract class InterfaceLogicChannelCardMixin implements InterfaceWireles if (eap$link != null) { eap$link.setFrequency(0L); eap$link.updateStatus(); - ExtendedAELogger.LOGGER.debug("[服务端] Interface 无频道卡 -> 断开无线链接"); } eap$hasInitialized = true; // 保存一次状态 @@ -142,12 +138,10 @@ public abstract class InterfaceLogicChannelCardMixin implements InterfaceWireles if (eap$link == null) { var endpoint = new InterfaceNodeEndpointImpl(host, () -> this.mainNode.getNode()); eap$link = new WirelessSlaveLink(endpoint); - ExtendedAELogger.LOGGER.debug("[服务端] Interface 创建新的无线链接实例"); } eap$link.setFrequency(channel); eap$link.updateStatus(); - ExtendedAELogger.LOGGER.debug("[服务端] Interface 设置频道: {} 连接状态: {}", channel, eap$link.isConnected()); try { host.saveChanges(); } catch (Throwable ignored) {} // 唤醒设备,加速后续 tick 以完成连接 try { @@ -239,7 +233,6 @@ public abstract class InterfaceLogicChannelCardMixin implements InterfaceWireles } } else { // 网格已引导完成,执行初始化 - ExtendedAELogger.LOGGER.debug("[服务端] Interface 延迟初始化触发 -> 开始初始化频道链接"); eap$initializeChannelLink(); } } diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/helpers/InterfaceLogicUpgradesMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/helpers/InterfaceLogicUpgradesMixin.java index 471d053..ddfa398 100644 --- a/src/main/java/com/extendedae_plus/mixin/ae2/helpers/InterfaceLogicUpgradesMixin.java +++ b/src/main/java/com/extendedae_plus/mixin/ae2/helpers/InterfaceLogicUpgradesMixin.java @@ -50,17 +50,14 @@ public abstract class InterfaceLogicUpgradesMixin implements CompatUpgradeProvid if (hasAppliedFlux) { // AppliedFlux已经将升级槽从1增加到2,我们再增加1个变成3 targetSlots = 3; - ExtendedAELogger.LOGGER.debug("[ME接口] 检测到AppliedFlux,当前升级槽: {}, 目标升级槽: {}", currentSlots, targetSlots); } else { // 没有AppliedFlux,从原始的1增加到2 targetSlots = 2; - ExtendedAELogger.LOGGER.debug("[ME接口] 未检测到AppliedFlux,当前升级槽: {}, 目标升级槽: {}", currentSlots, targetSlots); } // 只有当当前槽数小于目标槽数时才需要扩展 if (currentSlots < targetSlots) { this.upgrades = UpgradeInventories.forMachine(is, targetSlots, this::eap$onUpgradesChanged); - ExtendedAELogger.LOGGER.debug("[ME接口] 升级槽已扩展到: {}", targetSlots); // 设置兼容升级槽 if (UpgradeSlotCompat.shouldEnableUpgradeSlots()) { diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/helpers/PatternProviderLogicUpgradesMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/helpers/PatternProviderLogicUpgradesMixin.java index 8b2f41c..cdff2af 100644 --- a/src/main/java/com/extendedae_plus/mixin/ae2/helpers/PatternProviderLogicUpgradesMixin.java +++ b/src/main/java/com/extendedae_plus/mixin/ae2/helpers/PatternProviderLogicUpgradesMixin.java @@ -56,7 +56,6 @@ public abstract class PatternProviderLogicUpgradesMixin { try { // 检测是否安装了 AppliedFlux this.eap$hasAppliedFlux = ModList.get().isLoaded("appflux"); - ExtendedAELogger.LOGGER.debug("[样板供应器][升级槽] AppliedFlux 状态: {}", eap$hasAppliedFlux); if (eap$hasAppliedFlux) { // AppliedFlux 已安装,尝试获取并扩展其升级槽 @@ -77,7 +76,6 @@ public abstract class PatternProviderLogicUpgradesMixin { f.setAccessible(true); existingUpgrades = (IUpgradeInventory) f.get(this); } catch (Throwable t) { - ExtendedAELogger.LOGGER.debug("[样板供应器][升级槽] 读取 AF 升级槽字段失败: {}", t.getMessage()); } if (existingUpgrades != null && existingUpgrades != UpgradeInventories.empty()) { @@ -85,8 +83,6 @@ public abstract class PatternProviderLogicUpgradesMixin { int currentSlots = existingUpgrades.size(); int targetSlots = 2; // AppliedFlux 1个 + 我们 1个 = 2个 - ExtendedAELogger.LOGGER.debug("[样板供应器][升级槽] AppliedFlux 升级槽数量: {}, 目标: {}", - currentSlots, targetSlots); if (currentSlots < targetSlots) { // 需要扩展升级槽 @@ -113,16 +109,13 @@ public abstract class PatternProviderLogicUpgradesMixin { f.setAccessible(true); f.set(this, this.eap$upgrades); } catch (Throwable t) { - ExtendedAELogger.LOGGER.debug("[样板供应器][升级槽] 绑定 AF 升级槽字段失败: {}", t.getMessage()); } this.eap$upgradesInitialized = true; - ExtendedAELogger.LOGGER.debug("[样板供应器][升级槽] 扩展到 {} 个升级槽", targetSlots); } else { // AppliedFlux 或其他模组已经提供了足够的槽位 this.eap$upgrades = existingUpgrades; this.eap$upgradesInitialized = true; - ExtendedAELogger.LOGGER.debug("[样板供应器][升级槽] 使用现有 {} 个升级槽", currentSlots); } } else { // AppliedFlux 还没初始化升级槽,或者出了问题,我们创建默认的 @@ -137,10 +130,8 @@ public abstract class PatternProviderLogicUpgradesMixin { f.setAccessible(true); f.set(this, this.eap$upgrades); } catch (Throwable t) { - ExtendedAELogger.LOGGER.debug("[样板供应器][升级槽] 初始化时绑定 AF 升级槽字段失败: {}", t.getMessage()); } this.eap$upgradesInitialized = true; - ExtendedAELogger.LOGGER.debug("[样板供应器][升级槽] 创建 2 个升级槽(AppliedFlux 未初始化)"); } } catch (Throwable t) { ExtendedAELogger.LOGGER.error("[样板供应器][升级槽] 扩展 AppliedFlux 升级槽失败", t); @@ -174,11 +165,9 @@ public abstract class PatternProviderLogicUpgradesMixin { } catch (NoSuchMethodException e) { // AppliedFlux 的方法不存在,这是正常的 } catch (Throwable t) { - ExtendedAELogger.LOGGER.debug("[样板供应器][升级槽] 调用 AppliedFlux onUpgradesChanged 失败: {}", t.getMessage()); } } - ExtendedAELogger.LOGGER.debug("[样板供应器][升级槽] 升级槽内容已变更"); } catch (Throwable t) { ExtendedAELogger.LOGGER.error("[样板供应器][升级槽] onUpgradesChanged 处理失败", t); } @@ -202,7 +191,6 @@ public abstract class PatternProviderLogicUpgradesMixin { // 没有 AppliedFlux,使用标准键 this.eap$upgrades.writeToNBT(tag, "upgrades", registries); } - ExtendedAELogger.LOGGER.debug("[样板供应器][升级槽] 保存升级槽到 NBT"); } } catch (Throwable t) { ExtendedAELogger.LOGGER.error("[样板供应器][升级槽] 保存升级槽失败", t); @@ -233,7 +221,6 @@ public abstract class PatternProviderLogicUpgradesMixin { if (!eap$hasAppliedFlux && this.eap$upgrades != null && this.eap$upgrades != UpgradeInventories.empty()) { if (tag.contains("upgrades")) { this.eap$upgrades.readFromNBT(tag, "upgrades", registries); - ExtendedAELogger.LOGGER.debug("[样板供应器][升级槽] 从 upgrades 加载升级槽"); } } } catch (Throwable t) { @@ -254,7 +241,6 @@ public abstract class PatternProviderLogicUpgradesMixin { drops.add(is); } } - ExtendedAELogger.LOGGER.debug("[样板供应器][升级槽] 添加升级槽物品到掉落列表"); } } catch (Throwable t) { ExtendedAELogger.LOGGER.error("[样板供应器][升级槽] 添加掉落失败", t); @@ -270,7 +256,6 @@ public abstract class PatternProviderLogicUpgradesMixin { } if (eap$upgradesInitialized && this.eap$upgrades != null) { this.eap$upgrades.clear(); - ExtendedAELogger.LOGGER.debug("[样板供应器][升级槽] 清空升级槽"); } } catch (Throwable t) { ExtendedAELogger.LOGGER.error("[样板供应器][升级槽] 清空升级槽失败", t); diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/menu/AEBaseMenuUpgradesDedupMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/menu/AEBaseMenuUpgradesDedupMixin.java index 6a3fc9a..93bc1c9 100644 --- a/src/main/java/com/extendedae_plus/mixin/ae2/menu/AEBaseMenuUpgradesDedupMixin.java +++ b/src/main/java/com/extendedae_plus/mixin/ae2/menu/AEBaseMenuUpgradesDedupMixin.java @@ -26,7 +26,6 @@ public abstract class AEBaseMenuUpgradesDedupMixin { var self = (AEBaseMenu) (Object) this; var existing = self.getSlots(SlotSemantics.UPGRADE); if (existing != null && !existing.isEmpty()) { - ExtendedAELogger.LOGGER.debug("[样板供应器][菜单] 检测到升级槽已存在({}),取消重复注入", existing.size()); ci.cancel(); } } diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/menu/InterfaceMenuUpgradesMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/menu/InterfaceMenuUpgradesMixin.java index 5292d77..a3667cc 100644 --- a/src/main/java/com/extendedae_plus/mixin/ae2/menu/InterfaceMenuUpgradesMixin.java +++ b/src/main/java/com/extendedae_plus/mixin/ae2/menu/InterfaceMenuUpgradesMixin.java @@ -31,9 +31,6 @@ public abstract class InterfaceMenuUpgradesMixin extends AEBaseMenu implements I this.eap$toolbox = new ToolboxMenu(this); // InterfaceMenu已经继承自UpgradeableMenu,会自动处理升级槽 - // 这里只需要记录日志,不需要重复设置升级槽 - ExtendedAELogger.LOGGER.debug("[ME接口][菜单] 升级槽已由UpgradeableMenu自动处理,当前升级槽数量: {}", - host.getInterfaceLogic().getUpgrades().size()); } @Override 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 3d67e3b..5022583 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 @@ -36,10 +36,8 @@ public abstract class PatternProviderMenuUpgradesMixin extends AEBaseMenu implem // 当未安装 AppliedFlux 时,我们负责注入升级槽;安装了 AF 则由 AF 的菜单 Mixin 负责,避免重复渲染 if (UpgradeSlotCompat.shouldEnableUpgradeSlots()) { - ExtendedAELogger.LOGGER.debug("[样板供应器][菜单] 注入升级槽: 使用自带 compat 槽"); this.setupUpgrades(((CompatUpgradeProvider) this.logic).eap$getCompatUpgrades()); } else { - ExtendedAELogger.LOGGER.debug("[样板供应器][菜单] 跳过注入升级槽: 由 AE2/AppliedFlux 负责渲染"); } } 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 8f15d70..97e1945 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 @@ -88,14 +88,12 @@ public abstract class IOBusPartChannelCardMixin implements InterfaceWirelessLink } eap$lastChannel = channel; - ExtendedAELogger.LOGGER.debug("[服务端] IOBus 初始化频道链接: found={}, channel={}", found, channel); if (!found) { // 无频道卡则断开 if (eap$link != null) { eap$link.setFrequency(0L); eap$link.updateStatus(); - ExtendedAELogger.LOGGER.debug("[服务端] IOBus 断开频道链接"); // 立即通知客户端状态变化(断开连接无需延迟) ((appeng.parts.AEBasePart)(Object)this).getHost().markForUpdate(); } @@ -108,12 +106,10 @@ public abstract class IOBusPartChannelCardMixin implements InterfaceWirelessLink () -> ((IActionHost)(Object)this).getActionableNode() ); eap$link = new WirelessSlaveLink(endpoint); - ExtendedAELogger.LOGGER.debug("[服务端] IOBus 创建新的无线链接"); } eap$link.setFrequency(channel); eap$link.updateStatus(); - ExtendedAELogger.LOGGER.debug("[服务端] IOBus 设置频道: {}, 连接状态: {}", channel, eap$link.isConnected()); // 通知客户端状态变化 ((appeng.parts.AEBasePart)(Object)this).getHost().markForUpdate(); 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 54a0bdb..69609a6 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 @@ -85,13 +85,11 @@ public abstract class StorageBusPartChannelCardMixin implements InterfaceWireles } eap$lastChannel = channel; - ExtendedAELogger.LOGGER.debug("[服务端] StorageBus 初始化频道链接: found={}, channel={}", found, channel); if (!found) { if (eap$link != null) { eap$link.setFrequency(0L); eap$link.updateStatus(); - ExtendedAELogger.LOGGER.debug("[服务端] StorageBus 断开频道链接"); // 通知客户端状态变化 ((appeng.parts.AEBasePart)(Object)this).getHost().markForUpdate(); } @@ -104,12 +102,10 @@ public abstract class StorageBusPartChannelCardMixin implements InterfaceWireles () -> ((IActionHost)(Object)this).getActionableNode() ); eap$link = new WirelessSlaveLink(endpoint); - ExtendedAELogger.LOGGER.debug("[服务端] StorageBus 创建新的无线链接"); } eap$link.setFrequency(channel); eap$link.updateStatus(); - ExtendedAELogger.LOGGER.debug("[服务端] StorageBus 设置频道: {}, 连接状态: {}", channel, eap$link.isConnected()); // 通知客户端状态变化 ((appeng.parts.AEBasePart)(Object)this).getHost().markForUpdate(); diff --git a/src/main/java/com/extendedae_plus/network/CraftingMonitorJumpC2SPacket.java b/src/main/java/com/extendedae_plus/network/CraftingMonitorJumpC2SPacket.java index 6b4f843..dbd38a2 100644 --- a/src/main/java/com/extendedae_plus/network/CraftingMonitorJumpC2SPacket.java +++ b/src/main/java/com/extendedae_plus/network/CraftingMonitorJumpC2SPacket.java @@ -55,11 +55,9 @@ public class CraftingMonitorJumpC2SPacket implements CustomPacketPayload { public static void handle(final CraftingMonitorJumpC2SPacket msg, final IPayloadContext ctx) { ctx.enqueueWork(() -> { if (!(ctx.player() instanceof ServerPlayer player)) return; - LogUtils.getLogger().info("EAP[S]: recv CraftingMonitorJumpC2SPacket key={} from {}", msg.what, player.getGameProfile().getName()); // 必须在 CraftingCPU 界面内 if (!(player.containerMenu instanceof appeng.menu.me.crafting.CraftingCPUMenu menu)) { - LogUtils.getLogger().info("EAP[S]: not in CraftingCPUMenu, abort"); return; } @@ -70,19 +68,16 @@ public class CraftingMonitorJumpC2SPacket implements CustomPacketPayload { grid = host.getActionableNode().getGrid(); } if (grid == null) { - LogUtils.getLogger().info("EAP[S]: grid is null, abort"); return; } var cs = grid.getCraftingService(); if (!(cs instanceof CraftingService craftingService)) { - LogUtils.getLogger().info("EAP[S]: craftingService is null/unsupported, abort"); return; } // 1) 根据 AEKey 找到可能的样板(pattern) Collection patterns = craftingService.getCraftingFor(msg.what); - LogUtils.getLogger().info("EAP[S]: patterns found={} for key={}", patterns.size(), msg.what); if (patterns.isEmpty()) { return; } @@ -93,9 +88,6 @@ public class CraftingMonitorJumpC2SPacket implements CustomPacketPayload { int providerCount = 0; for (var provider : providers) { providerCount++; - try { - LogUtils.getLogger().info("EAP[S]: provider class={}", provider.getClass().getName()); - } catch (Throwable ignored) {} if (provider instanceof PatternProviderLogic ppl) { // 使用 accessor 获取 host(受保护字段通过 accessor 访问) PatternProviderLogicHost host = ((PatternProviderLogicAccessor) ppl).eap$host(); @@ -108,14 +100,12 @@ public class CraftingMonitorJumpC2SPacket implements CustomPacketPayload { BlockPos targetPos = pbe.getBlockPos().relative(dir); var tbe = serverLevel.getBlockEntity(targetPos); if (tbe instanceof MenuProvider provider1) { - LogUtils.getLogger().info("EAP[S]: open screen via MenuProvider at {}", targetPos); player.openMenu(provider1, targetPos); return; } var tstate = serverLevel.getBlockState(targetPos); var provider2 = tstate.getMenuProvider(serverLevel, targetPos); if (provider2 != null) { - LogUtils.getLogger().info("EAP[S]: open screen via state.getMenuProvider at {}", targetPos); player.openMenu(provider2, targetPos); return; } @@ -124,9 +114,7 @@ public class CraftingMonitorJumpC2SPacket implements CustomPacketPayload { // 兜底:若无 MenuProvider,则跳过(不再模拟右键以确保兼容性) } } - LogUtils.getLogger().info("EAP[S]: providers count for one pattern: {}", providerCount); } - LogUtils.getLogger().info("EAP[S]: no target opened for key={}", msg.what); }); } } diff --git a/src/main/java/com/extendedae_plus/network/ScalePatternsC2SPacket.java b/src/main/java/com/extendedae_plus/network/ScalePatternsC2SPacket.java index 586c10a..4d5080c 100644 --- a/src/main/java/com/extendedae_plus/network/ScalePatternsC2SPacket.java +++ b/src/main/java/com/extendedae_plus/network/ScalePatternsC2SPacket.java @@ -80,9 +80,6 @@ public class ScalePatternsC2SPacket implements CustomPacketPayload { factor, result.getTotalPatterns(), result.getScaledPatterns(), result.getFailedPatterns()); player.displayClientMessage(net.minecraft.network.chat.Component.literal("[EAP] " + summary), true); - for (String err : result.getErrors()) { - ExtendedAELogger.LOGGER.debug("[EAP] scale error: {}", err); - } } catch (Throwable t) { ExtendedAELogger.LOGGER.error("[EAP] Handle ScalePatternsC2SPacket failed", t); } diff --git a/src/main/java/com/extendedae_plus/util/PatternProviderDataUtil.java b/src/main/java/com/extendedae_plus/util/PatternProviderDataUtil.java index 6360521..c7b671e 100644 --- a/src/main/java/com/extendedae_plus/util/PatternProviderDataUtil.java +++ b/src/main/java/com/extendedae_plus/util/PatternProviderDataUtil.java @@ -251,7 +251,6 @@ public class PatternProviderDataUtil { patternDataList.add(new PatternData(patternDetails, patternStack, i)); } } catch (Exception e) { - if (DEBUG) LogUtils.getLogger().debug("Pattern decode failed at slot {}: {}", i, e.toString()); } } @@ -572,7 +571,6 @@ public class PatternProviderDataUtil { return i; } } catch (Exception e) { - if (DEBUG) LogUtils.getLogger().debug("findSlotForPattern decode failed at {}: {}", i, e.toString()); } } return -1; diff --git a/src/main/java/com/extendedae_plus/wireless/WirelessSlaveLink.java b/src/main/java/com/extendedae_plus/wireless/WirelessSlaveLink.java index 3157025..872e461 100644 --- a/src/main/java/com/extendedae_plus/wireless/WirelessSlaveLink.java +++ b/src/main/java/com/extendedae_plus/wireless/WirelessSlaveLink.java @@ -53,19 +53,16 @@ public class WirelessSlaveLink { */ public void updateStatus() { if (host.isEndpointRemoved()) { - ExtendedAELogger.LOGGER.debug("[无线] 端点已移除或无效,销毁连接"); destroyConnection(); return; } final ServerLevel level = host.getServerLevel(); if (level == null || frequency == 0L) { - ExtendedAELogger.LOGGER.debug("[无线] 环境不满足:level={}, freq={}", level, frequency); destroyConnection(); return; } IWirelessEndpoint master = WirelessMasterRegistry.get(level, frequency); - ExtendedAELogger.LOGGER.debug("[无线] 查找主站: level={}, freq={} -> {}", level.dimension(), frequency, master); shutdown = false; distance = 0.0D; @@ -73,7 +70,6 @@ public class WirelessSlaveLink { if (master != null && !master.isEndpointRemoved() && (crossDim || master.getServerLevel() == level)) { if (!crossDim) { distance = Math.sqrt(master.getBlockPos().distSqr(host.getBlockPos())); - ExtendedAELogger.LOGGER.debug("[无线] 同维度距离={}, 最大距离={}", distance, ModConfigs.WIRELESS_MAX_RANGE.get()); } double maxRange = ModConfigs.WIRELESS_MAX_RANGE.get(); if (crossDim || distance <= maxRange) { @@ -82,8 +78,6 @@ public class WirelessSlaveLink { var current = connection.getConnection(); IGridNode a = host.getGridNode(); // 从端 IGridNode b = master.getGridNode(); // 主端 - if (a == null) { ExtendedAELogger.LOGGER.debug("[无线] 从端节点为 null,无法连接"); } - if (b == null) { ExtendedAELogger.LOGGER.debug("[无线] 主端节点为 null,无法连接"); } if (a == null || b == null) { shutdown = true; } else { @@ -92,36 +86,28 @@ public class WirelessSlaveLink { var ca = current.a(); var cb = current.b(); if ((ca == a || cb == a) && (ca == b || cb == b)) { - ExtendedAELogger.LOGGER.debug("[无线] 连接已存在且目标一致,保持"); return; // 连接已正确 } // 否则先断开,再重建 - ExtendedAELogger.LOGGER.debug("[无线] 连接目标变化,先销毁再重建"); current.destroy(); connection = new ConnectionWrapper(null); } // AE2 侧是否已经存在连接(例如此前创建但 wrapper 丢失) IGridConnection existing = findExistingConnection(a, b); if (existing != null) { - ExtendedAELogger.LOGGER.debug("[无线] 复用已存在的连接: {}", existing); connection = new ConnectionWrapper(existing); return; } - ExtendedAELogger.LOGGER.debug("[无线] 创建连接: a={}, b={}", a, b); connection = new ConnectionWrapper(GridHelper.createConnection(a, b)); - ExtendedAELogger.LOGGER.debug("[无线] 连接创建完成: {}", connection.getConnection()); return; } } catch (IllegalStateException ex) { // 连接非法(如重复连接等)——落入重建/关闭逻辑 - ExtendedAELogger.LOGGER.debug("[无线] 连接创建异常: {}", ex.toString()); } } else { - ExtendedAELogger.LOGGER.debug("[无线] 超出范围:{} > {},关闭连接", distance, maxRange); shutdown = true; // 超出范围 } } else { - ExtendedAELogger.LOGGER.debug("[无线] 无可用主站或跨维度不允许,关闭连接"); shutdown = true; // 无主或主端不可用 } @@ -136,7 +122,6 @@ public class WirelessSlaveLink { private void destroyConnection() { var current = connection.getConnection(); if (current != null) { - ExtendedAELogger.LOGGER.debug("[无线] 销毁连接: {}", current); var a = current.a(); var b = current.b(); // 先销毁连接,再唤醒两端节点,使其尽快感知到状态变化 @@ -163,7 +148,6 @@ public class WirelessSlaveLink { IGridNode other = gc.getOtherSide(a); Object owner = other != null ? other.getOwner() : null; if (owner instanceof IWirelessEndpoint) { - ExtendedAELogger.LOGGER.debug("[无线] 兜底销毁直连: {} -> {}", a, other); gc.destroy(); try { if (a.getGrid() != null) { a.getGrid().getTickManager().wakeDevice(a); } } catch (Throwable ignored) {} try { if (other != null && other.getGrid() != null) { other.getGrid().getTickManager().wakeDevice(other); } } catch (Throwable ignored) {} From 7c07dd148a39eee3ae95d3e1d73e3a8cc056ad82 Mon Sep 17 00:00:00 2001 From: GaLicn <133291877+GaLicn@users.noreply.github.com> Date: Thu, 25 Sep 2025 19:04:57 +0800 Subject: [PATCH 32/43] =?UTF-8?q?=E5=B4=A9=E6=BA=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/extendedae_plus/bridge/IUpgradableMenu.java | 2 +- .../ae2/client/gui/PatternProviderScreenUpgradesMixin.java | 4 ++-- .../mixin/ae2/menu/InterfaceMenuUpgradesMixin.java | 2 +- .../mixin/ae2/menu/PatternProviderMenuUpgradesMixin.java | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/extendedae_plus/bridge/IUpgradableMenu.java b/src/main/java/com/extendedae_plus/bridge/IUpgradableMenu.java index 9599172..63d56f6 100644 --- a/src/main/java/com/extendedae_plus/bridge/IUpgradableMenu.java +++ b/src/main/java/com/extendedae_plus/bridge/IUpgradableMenu.java @@ -8,5 +8,5 @@ import appeng.menu.ToolboxMenu; * 以便在界面上显示升级卡槽(用于放置频道卡)。 */ public interface IUpgradableMenu { - ToolboxMenu getToolbox(); + ToolboxMenu eap$getToolbox(); } 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 977436a..c46560f 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 @@ -77,9 +77,9 @@ public abstract class PatternProviderScreenUpgradesMixin Date: Thu, 25 Sep 2025 23:41:41 +0800 Subject: [PATCH 33/43] =?UTF-8?q?feat:=20=E6=97=A0=E9=99=90=E7=9B=98?= =?UTF-8?q?=E6=9D=90=E8=B4=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../textures/item/infinity_biginteger_cell.png | Bin 2266 -> 421 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/src/main/resources/assets/extendedae_plus/textures/item/infinity_biginteger_cell.png b/src/main/resources/assets/extendedae_plus/textures/item/infinity_biginteger_cell.png index 5c47ca4fdedf33c73a8afa63e5c1fe41f4978db7..75e2465e80eefebf2e0a16b5d6a9e829cab46469 100644 GIT binary patch delta 396 zcmV;70dxM^5v2o=BYy#GNklLb@)0i5U z3^zY-kt0?^Pz*rV`192}26izayoP`cK-UP;fvy>53c5=eL7L+{0>L(cG{FGK=3~Y4 zVO$2Q-bA>e$cBIo5Mg9t5Wnybt{B8HF82o;`e@-Hu=tZpcYm=O!i22rnfeh189ik% zKHIqwY@=Mf9asrS4&)+`%Rnxj6t|y&kHO+Uh6_Ln&Di7Nwt`#&0x!2;VL0`3A%oOS z8-|_w7rXM({eP=l4C{AnX3)3@iBb#~z*NEvF)=j(3u&wCF+6;Ionh~WPKHk$T3}m2 z5+IvF0F*jW0`VwA4&$5ue;Cf3J_A+&BVd|A8bKJO5tg)3QX)(P%q0%)mSCq}VAukd qf@y-O!%Kq<0YwuC<5fthBmiHxu5I}BX0iYP002ovP6b4+LSTaG0HaO- literal 2266 zcmcgueNYr-99|d)5hWog8wPb}Q}ctpyWQKp+uJx1Iq)zJ5GcSgb8q)O?)2_<-Cf`y zrqGNeq%2eOqiIT;v`;ll#}dgAF`Ud8ok%OgR2-eeufnvbcaP)320z9>x|zLw_uc1x zp6BJhA1G3_uoqzvHp z*sEnwSe;wMS1;f#0+u!vO%1S+zz0+g4fwo%g$-D-ATJBY+O!@+gAjFr6-(Cy(Go{K znjy*nwde?(HyB9NY|)t+r`cp=Qc%)B(s~1_CkULdu!Mm%Flgw9K{;7)u?4oQkQltO zVs2HHSiQcovQk%R)QNJLp0HRf8V5<@5P>UIew7R0e&ykQ1{+X#*(0eQ(T{43oKq}U ztr)c07lTg<)B2TAo?yoG0Z!5rI)fHd5Ge3roK!A*gW>|O2VURKx6lTH0t`))S~mw9gtdDVSh}jv^HB&E zhDKfw8te&{M1S3f?W+&4h`atW0Hsifm52b~7bN(^_ zcTPQa@4?(%U-r5DAA`|r`(Hph&!FvPtR#?d36x^`2Vfa-6FTyVy;D>pV;j2Vh zH+?vQM4ZdEr57=c@2p63C(KH2P184;M$1Q%N4f1AIv?D;eDat33*#Q$^klQPqsB=j@t2le0pTmq^P9W`ZM0yc}eTAs@x`N?y(kg?@v89 zs?wg>)16Q-v$t#MkGec5FY?cg*{__pe%d*=WrTTr^YiuD*MA|7HBRVRy?i(N_UcQQ z_bq#G_YXS{b{?WGb;Rvh<^QB@eKb-*MxMRmY3n+%aDH>tytghM7#crx=VUSGo0GeA z-|oIqlT|D1-#9zv2KvX|NcwUlI^<{OzB&$(To+@m*Q^!xk6S}Lu&?Iz)4zS3iu~R& zBC4<;;{KHpV})z&CG)nlcF#FwKaq&kani3>Q<7RY9-H1(8r!x0IZ#TiEw|;)Xe!M( zrPhrcQ;=2L+AuPa8Ij+9ZdqW$(MQGx*txCSQs#V@c}hi^jy{H@G*!o+%|XU(IsKLU zrFe1a{H-}pr`CbenOA#SV&07C$cw4%N$oy^F2Y}aan`mswlDU!(NjMxK76r4mig??bH%wcSIOZT0_w|s^mL;@A$L<|df4n!M zxj1S2wdmSb*=%3586gg}C2m-<=n_OJ*GG0{uI;1g^A`UDbnOYi From f77833a339546b8f21723c8c1242ae97f74930c7 Mon Sep 17 00:00:00 2001 From: C-H716 <1536152356@qq.com> Date: Fri, 26 Sep 2025 00:27:18 +0800 Subject: [PATCH 34/43] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E9=AB=98?= =?UTF-8?q?=E7=BA=A7ae=E6=94=AF=E6=8C=81=E6=99=BA=E8=83=BD=E7=B3=BB?= =?UTF-8?q?=E5=88=97mixin=E6=9E=84=E9=80=A0=E5=87=BD=E6=95=B0=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../advancedae/client/gui/AdvPatternProviderScreenMixin.java | 5 ++--- .../client/gui/SmallAdvPatternProviderScreenMixin.java | 5 ++--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/extendedae_plus/mixin/advancedae/client/gui/AdvPatternProviderScreenMixin.java b/src/main/java/com/extendedae_plus/mixin/advancedae/client/gui/AdvPatternProviderScreenMixin.java index 2136f1a..229c50c 100644 --- a/src/main/java/com/extendedae_plus/mixin/advancedae/client/gui/AdvPatternProviderScreenMixin.java +++ b/src/main/java/com/extendedae_plus/mixin/advancedae/client/gui/AdvPatternProviderScreenMixin.java @@ -14,7 +14,6 @@ import net.minecraft.network.chat.Component; import net.minecraft.world.entity.player.Inventory; import net.pedroksl.advanced_ae.client.gui.AdvPatternProviderScreen; import net.pedroksl.advanced_ae.gui.advpatternprovider.AdvPatternProviderMenu; -import org.checkerframework.checker.units.qual.C; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Unique; import org.spongepowered.asm.mixin.injection.At; @@ -43,8 +42,8 @@ public abstract class AdvPatternProviderScreenMixin extends AEBaseScreen Date: Fri, 26 Sep 2025 00:34:39 +0800 Subject: [PATCH 35/43] =?UTF-8?q?feat:=20=E6=9B=B4=E6=96=B0=E5=AE=9E?= =?UTF-8?q?=E4=BD=93=E5=8A=A0=E9=80=9F=E5=8D=A1=E6=9D=90=E8=B4=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../textures/item/entity_speed_card_x16.png | Bin 2485 -> 2400 bytes .../textures/item/entity_speed_card_x2.png | Bin 2271 -> 2360 bytes .../textures/item/entity_speed_card_x4.png | Bin 2501 -> 2380 bytes .../textures/item/entity_speed_card_x8.png | Bin 2476 -> 2394 bytes 4 files changed, 0 insertions(+), 0 deletions(-) diff --git a/src/main/resources/assets/extendedae_plus/textures/item/entity_speed_card_x16.png b/src/main/resources/assets/extendedae_plus/textures/item/entity_speed_card_x16.png index adfcc54a61cacbeaf5785fe622850a3a8549da53..fc3db8219d8075adc06819e594900d8a06171901 100644 GIT binary patch delta 845 zcmdlg{6J{K5hiXUvk(IVD`R6TBh$&}nUqlYf0zPPOw5za3@nXIbj^$mOmq#COpJ9c zEfdXk&61On%?yl_%+gXVCKoVksTdm=8yh938tPgYo2Ka+nVJ{@S%!(amPW=##%bmz zsVPZ@oA)uRF%f5E1B)1nTNbj|OrF6iHo2NDvR>`z%m@YsrUFkF#}JRsyHor zNpoOTCcZ?eCPS$Sk!-?D6h>%g+ZDUcI@en*aNp-*eyJeILwom_b3x|C;#! zj?Rr+lJ3@PwD4&PU9(tRBDAy>PXg+9H0hLK~T@zzPp zmR>toAsQ4@F2}fFyZV{qG5-QS&)&)rK1)xa^gY7`h7ZZLv72&L*Kb$%$@hp4?<=!r2l|ZT zCiff;wz*zA&uqRJ-c|qPfYdX6>mOnaSKbS1JvExUJ?d%kHq(qb`Hz2IQaYH>;E~sr zVBnCxqWN3^yS=^1>Ei6wt9gzJ{g!d~J9Xu%>2pozCh7S<)^ASI>ld(E^~!b5b=l*a z6a}1~Z>%^V<4?Dqe+_tI%J-2BG1S4UgV>Xgq;5;5Cc&6}L!rR=`?qMO&}FxhR=D|wez za{uU-n!9MjoTclv(@xL5!OwL%5{PD6Pm@2RduBq%z0Ibxqh~PsJzy_BBjc2;WMORY r(Yw!buYAPU0xkOu$Lb%j*E0m4=@V4QSyA_f0SG)@{an^LB{Ts5aZzDN delta 929 zcmaDLv{iV+5hiX!lMn+VD+3cNQ-jIpnUqlYf0zPPEDcP|Eewr}buBDXjC2i6OiXnx z%uUmDEsWBPjg1VF3{%ZgCl@elsiYVh7$%unnChllq?za@C7Pt_CYqX==$fXOB^g_$ zq$Qarrf%NHtj0u~kqs>3Tu4q^$YL{j2CLZQYPQJw-=1Za3=B+1JzX3_JUY)#Iqw}3 zD01B1|GxD7AIHp>Ez)@Drm?lvRjBR3qK*!h`UFm0wF|9^j*Sl%EiyRWmb18f!LmhS zjzTLZ{AJD5{@)O$#}ia2Gk0cr`n^A&wid|Dy?F7GZ+fxZ_j%v%4hY|SB2id#Uc$0< zQ)jFZ2n|`Z$<5{BwnIy7fi zuOuzXkI$=9k58G!&@gj(@!9-~FQ$}Lu7Bq9`{ngRO#HXHd$XzLq zefIr^^_+*!A09k8TejwF@|4}b5_yI6tNx|+vcGm#zR}XSx4fq}-8}KJfK9-=eQA$Z zcDwkM$?b4+-~3s@@Qu>CH_r|pdGe(GK(noJL$2I}CkOoIpKP{e84mFS{kJ$nk1W~Zq8@6XPT_SB0BjSiy|=^CfBowb0e%ZoxG66X7UWy z{Cf64<;4sPOdg&tjv*eMcc+~9mJSp+ZZBP=ZR>F>Eqfd5#GqNt0gl;Kfu5|ktOvWZ znd7+URlkxC$l$jXsrsPw<@(YM6`HLQ!n|8d7dTB`H9e-}`aWG(&3l_|m%RUJCcV;& ze`)c}+QYNo*QDP&bDg7!q2bOIpV!a%xBYmxb>aN_$3{PHUOvw7W@ntngIQC9>aKr1 z;d_?F$|Gj~pO?j^lP+FmczxSiMW|E2!$ycvXKI+!^XkQi88{vq->h-9l3x7rQP73o z584GMPgnW6^b*(RO6i81d(P(E3u{<5Q{}Gw<0P$PUng2$6ndh1{p*$RUnbvs_~Wn@TVxc}{Y==P;M*B$>6G412VHJ>;iZr*zF z$%*Vu(Q(##53|a*ziOPfbKl;@HJjFyu@%m3{QZ0F*EN%R?=hRrTJrmL)dKzrx6OR2 z_+<*`SFJsE=ufrtR|oT-DQ`C`c(1&kmBRMxib@-Uj`q>RbJs_Nb#)%9H<~t8$=Jjr zEknRN`_sL5di`o|AImZ5?5SzaRNJ}t`Q}KCqKD}R(iV0I*e#ps!m&R&QjyK+hmE1W za?ANgXSKCnEJPKR~ZRd+S@(sOxW-skW<4Gdp;Lc8|pSb!?Du#Urw;1HsygPcHZXcJ9Dc=C0eU^tBT z+XSYFv2dvmDW1qU$eRH=w8*YqfolIcs)+EmwGW3dx&Gq=ix<1G<4IDoe6$1IzlI`A zlxURBr5P44bdd(qs*`wW8Gn~2rkQ7&%F0SKx0|-facPf$y6H!bSxBig3oTtFWrc*I zl8`mxik2;t3SJ~(nkaFVk&iF;uLCeR@}=PS+;0R(m6p=($sDjh2h^(EhU|?zAYeIl zZij5zJ<@^f*F1YR{a#t&Y0sVA763x78rFwDaZV}Yr=n4zxR`gSSbvI&sW^(ZkQIqd`4))5hiXUvk(IVD`R6T6XVI}nUqlYf0zPP%u>t~O;XG(bq$iu40H{RP0V!@ z(~OLCQ%y|`Obv}pElms!CKoVksTiAEnkJhiC+eD|nVRZaB$*iMCK?+U>L!_6CK*`* zwWL`lZr;bN#zdTv4J=|PZdu4;GkFH9*yL)q$ofq#3%eN@m|{F#978-h?@rmD9ULff z++M#Vt8hi&=bS|WT^)?fYrnY1+?phEYbA@!nadsr9hv4HUHHGTxqE_Qck?CJijWWN zOENF9wTRtv63(8clPQ$%61C)lVWG$Ux4S)8WflsY|8&oC&hNeVi{DwsuZ>&JY*78) z@$qy1ZC}3C_ojW{z2@*ocTR>+;`cS#*HzwO`10n?MDd%ByWX5U?QUqHrOmK(($nJ{ zW$uMmbGbU06dHq8cgfF@VSjM*_wSah^&i<}M3SbzV!kLp>#*0R75dA=W_C+3oN?uO zrqsrArBwW$BD?qG$)$yw=) z4>ta5iZSc1zoD)k$jDJ&=3Y7Lf79{DX=)pR}q@!3HNqd?a6c@Lw~#EU{Ub>&C4#DT`*g zH52m^l_me^+TQ3`er`Scx7vU>#T&Az8uhc^3pUR@!nLkLadz}?iyPCf)-Pb+&mf;v Vttnhx^@ITkJYD@<);T3K0RXl6Tu}f3 delta 945 zcmX>jbX0i55hiX!lMn+VD+6OIW24FEnUqlYf0zPPEK(9p(=5zPbW<&oO?1-?Ow4sH z4U&v?&5SLRQcP1)3@itCK{QW=_V&9nj0jg zBv~4pT5R6Otj0u~kqs>3Tu4q^$YL{j2CLZQYPQIFhlfiI85o$Zdb&7>!I~*1pN(hTdh3i$Ys1^na$Ig%t4pcJSCnj~VBTO+*B-!Mm zix^YqCvlE-KbT`Ch6qnI_+ok4FTSUHTk}-0sd+ZDjdy-O^L}3OJKOmQx6PK$;CS%) zr;O$AlZ>ZRBi>h{YwJewva;+lBv< z+5SLDPUFgF60)-ntZvn3c*7(uAj8PYshhfZSx(j(l~t*Mvb~S`BsG>Dd~%29E&KP& z^LyHV>D0ZPWfN{?Jbz6du}XI|d&>jGDo zzgZ3^+Q_>boRQdE{afHsrw`lyFASo$T&~YL5LU~1v**L2z5QI5ADmcz{mk2! z$NJPgJCiQTSum~o$9&SHe!`+atLN%7Zr^p4@;E*9$m!KL%#F^OJic=6+M*qIWhD0M zKjA!~ZIyJ!$z$ov+}BE)UMeaxTUH26pAs`8;Dh4KwnmZZl2yMSe6M9V$Fo;|$Gba+ z&PH$dm^%IP!c@K=cYi;%lC!Uz^z+=l6E=(ttAn1poLJ^8e62F};-|V_-@a;_*H`&H zH8*^6>)53S)@2@{HPb}0RKzcf8ckD4_7HXTE?{|c+I=OwN@kR%9DU{b?(Q) zI~ED17(2ax_?dP2Mg@Mp((+HWZ=OD@*zmcmgukqSn|Gcghr_RjH|rleemKV=x~%c_ z9f{T7{SLoUt5D#zN&mX8y(F?wuENc|`?G@W8%5Q%&ksJia;5%2bCcu&DgF~h55m`L wOX<2AY0P9lr}IQiFZV$6wQJW(eRXEC+cP?RnC+OQ*vS9_p00i_>zopr00@eZOaK4? diff --git a/src/main/resources/assets/extendedae_plus/textures/item/entity_speed_card_x8.png b/src/main/resources/assets/extendedae_plus/textures/item/entity_speed_card_x8.png index dbfdae5b48127a7c29b6c395ce4b413f8542b4f9..c73e9ac1ea05c798bbbc0558fae103092e66e4ee 100644 GIT binary patch delta 839 zcmZ1@d`oD<5hiXUvk(IVD`O)o6N|~`nUqlYf0zPPQq#=M3{#RUbd3yB%ybjYOe}RR zEeuU{QxnaSjZ6$JjZ;iaCl@elsTilFT9{fG8tEn(8=L5+C7UGaTBaFW>LwW`nxz`1 zS|+C$7;N6htj0u~kqsI@Kj%7kI+iHTao#z8jGEK$@13r`fB*a5-+R|5%~E6dxH0AJ zWBF}64A$SMtv_nw`zYD7_;H|6!_LRreNF2U7#b#au{pfBY17U9`P6IW=HGhydeuc) z-_@;B*&Ehf|07~>_io?u$BK5-Omx-w7*0?7beiwKUxh@&hnTiAm^O zhsH~3s*zWwe>&~sr@}B}j)#He#0d?nULCDn+nyZHzHau`Yf=*RD^`ie>Yu*c#mJFZ z@Z&FgUX{fy>xPB)TNnho-A<$w^FGM)S3kawPme{9@dDe1@RcVrZ?}tgyLGrK8>zJ% ze0Wa$);B(jUlITAHl2UJyu|5x-REb|btO07T9y7_lktVGoA&IS*IUc>hJm^EoMK;y zNl+>4*JZ2X-f=ej`aZt;rl(#hJ;D6-^sD<>8_ehVnVI$;j8^)DRTeUuW?cRN+M{SO7%X1^3Jpcup)eGd>#i{NKU?@7H^NDZYxR{z7y7(Ug{d{kFf97`Pc27(8A5T-G@yGywn@uWzOR delta 920 zcmca5v_^Qt5hiX!lMn+VD+3cN1M|t}nUqlYf0zPPl2VLJjZzIvbj^~IQ*|vYADuTO_7g z7@8WUCT`xxtj0u~kqs>3Tu4q^$YL{j2CLZQYPQJwCa?M(3=B+rJY5_^JUY)#IqNM_ zC~@3=vGMCQi#IP@B=O4dG@q2GM3bW+t8C3dA*(eB?2b&#o=uCKCwZ1#Y!2{B5!m9Q zwf7I>#IjHP8XWv=D|fuxwfpn!xwXYsYu3GcrL}JQywl9a=WFWk-#0%0d7{qgM;40A zA6^FqEzRF!x3u0tf{j70pS@w#t1J`W$6qGfoz$G3&&lwedH#v}J;9aQU2?qv5@r{b z)LI`3R5IFMz8jud;;|?459fi*4Gfx^3l=`;hB+E%mFqyJyZ0@0r!DleVUIU7MC!pTDbd?-r)%_uEgu zPgZ<(|E1&o3ft@JD=s=WHBCtPa5(wctm+e=*CZJ;-Do-SLZPy?=)Tn~jt?I`8%WpL zpVL>CP&yJ*%N}GgsbIru_7?%#x64_)?yg>;bRZ>`-7K--=WmhE>Cqbx1tx8(*};5w zZ<7+wl!OV-Sj9i5M|Ch4n)cThxUQ=5_6^ypF2mL?`CxAvtXjHP#bWsD{!6+X*~ z?l{FI=&Gue-6ER1+sAA6;+6&jo+%qn9oYT+<$J~#azA#w|NJC>j?52V9rNjK?vEP2 zyb1K4A?N4w=$p$j4u%<(R_3*0y?mMeW!ZgG`v2+Zeq8akEK9idn^D;B`^8=->z}tT z3S4qy=GK)ny1EwLFp5*u47h8}>hQby(Uhj#*E5~0-dR1Di7P5dIorH&;leA9R$Bb` zj)mzikEyGXQvn97e7oeL3p|V1S;eDgN6slotD$ zD}UCmTN}*$0v^6$*FO?}$5M*ljLm%mCv$A|d5PKQcO@8e%V~U%+xYW}r;WNX|D45( zXI2Wx7YPM_`;<_X_RRjl!F_xQ?d&;)3FUsz5AI{S`AK2px!u!VI>=qAxqbOa^5+Ng YhhFsV*I&8rAOjG1y85}Sb4q9e071Hr&Hw-a From cc9dd480b43a4b203f6580957aaecb25b9cefd96 Mon Sep 17 00:00:00 2001 From: C-H716 <1536152356@qq.com> Date: Fri, 26 Sep 2025 15:52:51 +0800 Subject: [PATCH 36/43] =?UTF-8?q?feat:=20=E7=A7=BB=E6=A4=8D=E6=97=A0?= =?UTF-8?q?=E9=99=90=E7=9B=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/extendedae_plus/ExtendedAEPlus.java | 34 +- .../InfinityBigIntegerCellHandler.java | 22 + .../InfinityBigIntegerCellInventory.java | 414 ++++++++++++++++++ .../ae/items/InfinityBigIntegerCellItem.java | 88 ++++ .../extendedae_plus/init/ModCreativeTabs.java | 2 + .../com/extendedae_plus/init/ModItems.java | 6 + .../util/storage/InfinityConstants.java | 30 ++ .../util/storage/InfinityDataStorage.java | 48 ++ .../util/storage/InfinityStorageManager.java | 151 +++++++ .../assets/extendedae_plus/lang/en_us.json | 3 + .../assets/extendedae_plus/lang/zh_cn.json | 3 + 11 files changed, 799 insertions(+), 2 deletions(-) create mode 100644 src/main/java/com/extendedae_plus/ae/api/storage/InfinityBigIntegerCellHandler.java create mode 100644 src/main/java/com/extendedae_plus/ae/api/storage/InfinityBigIntegerCellInventory.java create mode 100644 src/main/java/com/extendedae_plus/ae/items/InfinityBigIntegerCellItem.java create mode 100644 src/main/java/com/extendedae_plus/util/storage/InfinityConstants.java create mode 100644 src/main/java/com/extendedae_plus/util/storage/InfinityDataStorage.java create mode 100644 src/main/java/com/extendedae_plus/util/storage/InfinityStorageManager.java diff --git a/src/main/java/com/extendedae_plus/ExtendedAEPlus.java b/src/main/java/com/extendedae_plus/ExtendedAEPlus.java index 5a01c42..43285e6 100644 --- a/src/main/java/com/extendedae_plus/ExtendedAEPlus.java +++ b/src/main/java/com/extendedae_plus/ExtendedAEPlus.java @@ -2,14 +2,18 @@ package com.extendedae_plus; import appeng.api.parts.IPart; import appeng.api.parts.PartModels; +import appeng.api.storage.StorageCells; import appeng.block.AEBaseEntityBlock; import appeng.blockentity.crafting.CraftingBlockEntity; import appeng.items.parts.PartModelsHelper; +import com.extendedae_plus.ae.api.storage.InfinityBigIntegerCellHandler; import com.extendedae_plus.config.ModConfigs; import com.extendedae_plus.init.*; +import com.extendedae_plus.util.storage.InfinityStorageManager; import com.mojang.logging.LogUtils; import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.MinecraftServer; import net.minecraft.world.level.block.Blocks; import net.neoforged.bus.api.IEventBus; import net.neoforged.bus.api.SubscribeEvent; @@ -18,7 +22,10 @@ import net.neoforged.fml.common.Mod; import net.neoforged.fml.config.ModConfig; import net.neoforged.fml.event.lifecycle.FMLCommonSetupEvent; import net.neoforged.neoforge.common.NeoForge; +import net.neoforged.neoforge.event.server.ServerStartedEvent; import net.neoforged.neoforge.event.server.ServerStartingEvent; +import net.neoforged.neoforge.event.server.ServerStoppedEvent; +import org.jetbrains.annotations.Nullable; import org.slf4j.Logger; // The value here should match an entry in the META-INF/neoforge.mods.toml file @@ -52,7 +59,8 @@ public class ExtendedAEPlus { // Note that this is necessary if and only if we want *this* class (ExtendedAEPlus) to respond directly to events. // Do not add this line if there are no @SubscribeEvent-annotated functions in this class, like onServerStarting() below. NeoForge.EVENT_BUS.register(this); - + NeoForge.EVENT_BUS.addListener(ExtendedAEPlus::onServerStarted); + NeoForge.EVENT_BUS.addListener(ExtendedAEPlus::onServerStopped); // 注册配置:接入自定义的 ModConfigs modContainer.registerConfig(ModConfig.Type.COMMON, ModConfigs.COMMON_SPEC, "extendedae_plus-common.toml"); modContainer.registerConfig(ModConfig.Type.CLIENT, ModConfigs.CLIENT_SPEC, "extendedae_plus-client.toml"); @@ -69,6 +77,7 @@ public class ExtendedAEPlus { LOGGER.info("HELLO FROM COMMON SETUP"); // 示例日志,避免引用不存在的模板 Config 字段 LOGGER.info("DIRT BLOCK >> {}", BuiltInRegistries.BLOCK.getKey(Blocks.DIRT)); + StorageCells.addCellHandler(InfinityBigIntegerCellHandler.INSTANCE); // 绑定 AE2 的 CraftingBlockEntity 到本模组的自定义加速器方块,避免 AEBaseEntityBlock.blockEntityType 为空 event.enqueueWork(() -> { @@ -116,7 +125,28 @@ public class ExtendedAEPlus { }); } - // 移除示例 addCreative 事件,避免将示例物品加入原版标签 + @Nullable + private static InfinityStorageManager storageManager; + + @Nullable + private static MinecraftServer storageManagerServer; + + private static void onServerStarted(ServerStartedEvent event) { + storageManagerServer = event.getServer(); + storageManager = InfinityStorageManager.getInstance(event.getServer()); + } + + private static void onServerStopped(ServerStoppedEvent event) { + if (storageManagerServer == event.getServer()) { + storageManagerServer = null; + storageManager = null; + } + } + + @Nullable + public static InfinityStorageManager currentStorageManager() { + return storageManager; + } // You can use SubscribeEvent and let the Event Bus discover methods to call @SubscribeEvent 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 new file mode 100644 index 0000000..3b7aa46 --- /dev/null +++ b/src/main/java/com/extendedae_plus/ae/api/storage/InfinityBigIntegerCellHandler.java @@ -0,0 +1,22 @@ +package com.extendedae_plus.ae.api.storage; + +import appeng.api.storage.cells.ICellHandler; +import appeng.api.storage.cells.ISaveProvider; +import com.extendedae_plus.ExtendedAEPlus; +import com.extendedae_plus.ae.items.InfinityBigIntegerCellItem; +import net.minecraft.world.item.ItemStack; + +public class InfinityBigIntegerCellHandler implements ICellHandler { + + public static final InfinityBigIntegerCellHandler INSTANCE = new InfinityBigIntegerCellHandler(); + + @Override + public boolean isCell(ItemStack is) { + return is.getItem() instanceof InfinityBigIntegerCellItem; + } + + @Override + public InfinityBigIntegerCellInventory getCellInventory(ItemStack is, ISaveProvider container) { + return InfinityBigIntegerCellInventory.createInventory(is, container, ExtendedAEPlus.currentStorageManager()); + } +} \ No newline at end of file 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 new file mode 100644 index 0000000..675c41e --- /dev/null +++ b/src/main/java/com/extendedae_plus/ae/api/storage/InfinityBigIntegerCellInventory.java @@ -0,0 +1,414 @@ +package com.extendedae_plus.ae.api.storage; + +import appeng.api.config.Actionable; +import appeng.api.networking.security.IActionSource; +import appeng.api.stacks.AEItemKey; +import appeng.api.stacks.AEKey; +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.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; +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; +import net.minecraft.core.component.DataComponents; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.ListTag; +import net.minecraft.network.chat.Component; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.component.CustomData; +import org.jetbrains.annotations.Nullable; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.math.RoundingMode; +import java.util.Objects; +import java.util.UUID; + +/** + * 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 { + private final InfinityBigIntegerCellItem cell; + // 磁盘本身 + private final ItemStack self; + @Nullable + private final InfinityStorageManager storageManager; + // AE2 提供的保存提供者,用于在容器中批量保存时触发回调 + private final ISaveProvider container; + // 存储物品键和数量的映射 + private Object2ObjectMap AEKey2AmountsMap; + // 存储的物品种类数量 + private int totalAEKeyType; + // 存储的物品总数 + private BigInteger totalAEKey2Amounts = BigInteger.ZERO; + // 标记是否已持久化到 SavedData + private boolean isPersisted = true; + + + public InfinityBigIntegerCellInventory(InfinityBigIntegerCellItem cell, + ItemStack stack, + ISaveProvider saveProvider, + @Nullable InfinityStorageManager storageManager) { + // 保存存储单元类型(InfinityBigIntegerCellItem 实例),用于访问磁盘属性 + this.cell = cell; + // 保存物品堆栈,表示磁盘本身,包含运行时的 NBT 数据 + this.self = stack; + // 保存提供者,用于触发数据保存 + this.container = saveProvider; + // 初始化 storedAmounts 为 null,延迟加载物品数据 + this.AEKey2AmountsMap = null; + this.storageManager = storageManager; + // 初始化磁盘数据 + initData(); + } + + // 将 BigInteger 格式化为带单位的字符串,保留两位小数 + public static String formatBigInteger(BigInteger number) { + // 使用方法局部的 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"}; + int idx = 0; + while (bd.compareTo(thousand) >= 0 && idx < units.length - 1) { + bd = bd.divide(thousand, 2, RoundingMode.HALF_UP); + idx++; + } + if (idx == 0) { + return bd.setScale(0, RoundingMode.DOWN).toPlainString(); + } + return df.format(bd.doubleValue()) + units[idx]; + } + + // 静态方法,创建存储单元库存 + public static InfinityBigIntegerCellInventory createInventory(ItemStack stack, + ISaveProvider saveProvider, + @Nullable InfinityStorageManager storageManager) { + // 检查物品堆栈是否为空 + 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, storageManager); + } + + // 获取磁盘的 InfinityDataStorage 数据 + private InfinityDataStorage getCellStorage() { + // 如果磁盘有 UUID,返回对应的 InfinityDataStorage + if (getUUID() != null && this.storageManager != null) { + return storageManager.getOrCreateCell(getUUID()); + } else { + // 否则返回空的 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.getTotalAEKey2Amounts().equals(BigInteger.ZERO)) { + return CellState.EMPTY; + } + // 否则返回满状态 + return CellState.NOT_EMPTY; + } + + // 获取存储单元的待机能耗 + @Override + public double getIdleDrain() { + return 512; + } + + // 持久化存储单元数据到全局存储 + @Override + public void persist() { + if (this.isPersisted || this.storageManager == null) + return; + + if (this.totalAEKey2Amounts.equals(BigInteger.ZERO)) { + if (hasUUID()) { + this.storageManager.removeCell(getUUID()); + // 从 DataComponents.CUSTOM_DATA 里移除对应字段 + CustomData data = self.getOrDefault(DataComponents.CUSTOM_DATA, CustomData.EMPTY); + CompoundTag tag = data.copyTag(); + tag.remove(InfinityConstants.INFINITY_CELL_UUID); + tag.remove(InfinityConstants.INFINITY_ITEM_TOTAL); + tag.remove(InfinityConstants.INFINITY_ITEM_TYPES); + // backward compat + tag.remove(InfinityConstants.INFINITY_CELL_ITEM_COUNT); + + self.set(DataComponents.CUSTOM_DATA, CustomData.of(tag)); + + 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(this.storageManager.getRegistries())); + CompoundTag amountTag = new CompoundTag(); + amountTag.putByteArray("value", amount.toByteArray()); + amounts.add(amountTag); + + itemCount = itemCount.add(amount); + } + } + + if (keys.isEmpty()) { + this.storageManager.updateCell(getUUID(), new InfinityDataStorage()); + } else { + this.storageManager.modifyDisk(getUUID(), keys, amounts, itemCount); + } + + // 更新存储的物品种类数量 + this.totalAEKeyType = this.AEKey2AmountsMap.size(); + // 更新存储的物品总数 + this.totalAEKey2Amounts = itemCount; + // 将物品总数与种类数量存入物品堆栈的 NBT(用于快捷查看/tooltip),同时保留旧字段以兼容历史版本 + + // 写回 DataComponents.CUSTOM_DATA(替代 getOrCreateTag) + CompoundTag tag = self.getOrDefault(DataComponents.CUSTOM_DATA, CustomData.EMPTY).copyTag(); + tag.putByteArray(InfinityConstants.INFINITY_ITEM_TOTAL, itemCount.toByteArray()); + tag.putInt(InfinityConstants.INFINITY_ITEM_TYPES, this.totalAEKeyType); + tag.putByteArray(InfinityConstants.INFINITY_CELL_ITEM_COUNT, itemCount.toByteArray()); + self.set(DataComponents.CUSTOM_DATA, CustomData.of(tag)); + // 标记数据已持久化 + this.isPersisted = true; + } + + // 获取存储单元的描述(此处返回null,可自定义) + @Override + public Component getDescription() { + return null; + } + + // 获取存储的物品总数 + public BigInteger getTotalAEKey2Amounts() { + return this.totalAEKey2Amounts; + } + + // 获取存储的物品种类数量 + public int getTotalAEKeyType() { + return this.totalAEKeyType; + } + + // 判断物品堆栈是否有UUID + public boolean hasUUID() { + CustomData data = self.getOrDefault(DataComponents.CUSTOM_DATA, CustomData.EMPTY); + return !data.isEmpty() && data.copyTag().contains(InfinityConstants.INFINITY_CELL_UUID); + } + + // 获取物品堆栈的UUID + public UUID getUUID() { + CustomData data = self.getOrDefault(DataComponents.CUSTOM_DATA, CustomData.EMPTY); + if (!data.isEmpty() && data.copyTag().contains(InfinityConstants.INFINITY_CELL_UUID)) { + return data.copyTag().getUUID(InfinityConstants.INFINITY_CELL_UUID); + } + return null; + } + + // 获取或初始化存储映射 + private Object2ObjectMap getCellStoredMap() { + if (AEKey2AmountsMap == null) { + AEKey2AmountsMap = new Object2ObjectOpenHashMap<>(); + this.loadCellStoredMap(); + } + return AEKey2AmountsMap; + } + + // 获取所有可用的物品堆栈及其数量 + @Override + public void getAvailableStacks(KeyCounter out) { + BigInteger maxLong = BigInteger.valueOf(Long.MAX_VALUE); + if (this.getCellStoredMap() == null) return; + for (var entry : this.getCellStoredMap().object2ObjectEntrySet()) { + AEKey key = entry.getKey(); + BigInteger value = entry.getValue(); + + // 获取 KeyCounter 中已有的值 + long existing = out.get(key); + + // 计算总和并限制到 Long.MAX_VALUE + BigInteger sum = BigInteger.valueOf(existing).add(value); + long toSet = sum.compareTo(maxLong) > 0 ? Long.MAX_VALUE : sum.longValue(); + // 更新 KeyCounter + if (existing == Long.MAX_VALUE) { + continue; + } + long delta = toSet - existing; + if (delta != 0) { + out.add(key, delta); + } + } + } + + + // 从存储中加载物品映射 + private void loadCellStoredMap() { + if (this.storageManager == null) { + return; + } + + boolean dataCorruption = false; + if (self.getOrDefault(DataComponents.CUSTOM_DATA, CustomData.EMPTY).isEmpty()) 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()); + } + + var registries = this.storageManager.getRegistries(); + + // 遍历数量和键,加载到 AEKey2AmountsMap + for (int i = 0; i < amounts.size(); i++) { + AEKey key = AEKey.fromTagGeneric(registries,keys.getCompound(i)); + BigInteger amount = new BigInteger(amounts.getCompound(i).getByteArray("value")); + // 检查数据是否损坏 + if (amount.compareTo(BigInteger.ZERO) <= 0 || key == null) { + dataCorruption = true; + } else { + AEKey2AmountsMap.put(key, amount); + } + } + if (dataCorruption) { + this.saveChanges(); + } + } + + // 标记数据需要保存,并通知容器或直接持久化 + 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) { + return 0; + } + // 不允许存储无限单元自身 + if (what instanceof AEItemKey itemKey && itemKey.getItem() instanceof InfinityBigIntegerCellItem) { + return 0; + } + + // 如果没有 UUID,且服务器端存储管理器已就绪,则生成 UUID 并初始化存储 + if (storageManager != null && !this.hasUUID()) { + // 取出自定义 NBT(如果没有就返回空) + CustomData data = self.getOrDefault(DataComponents.CUSTOM_DATA, CustomData.EMPTY); + CompoundTag tag = data.copyTag(); + + // 生成新的 UUID 并写入 + UUID newUUID = UUID.randomUUID(); + tag.putUUID(InfinityConstants.INFINITY_CELL_UUID, newUUID); + + // 回写到 ItemStack + self.set(DataComponents.CUSTOM_DATA, CustomData.of(tag)); + + // 初始化存储 + this.storageManager.getOrCreateCell(newUUID); + + // 加载已存储的映射 + loadCellStoredMap(); + } + // 获取当前物品数量 + BigInteger currentAmount = this.getCellStoredMap().getOrDefault(what, BigInteger.ZERO); + + if (mode == Actionable.MODULATE) { + // 实际插入,更新数量并保存 + BigInteger newAmount = currentAmount.add(BigInteger.valueOf(amount)); + getCellStoredMap().put(what, newAmount); + this.saveChanges(); + } + return amount; + } + + // 从存储单元提取物品 + @Override + public long extract(AEKey what, long amount, Actionable mode, IActionSource source) { + BigInteger currentAmount = this.getCellStoredMap().getOrDefault(what, BigInteger.ZERO); + // 如果有物品可提取 + if (currentAmount.compareTo(BigInteger.ZERO) > 0) { + + BigInteger requested = BigInteger.valueOf(amount); + + // 如果提取数量大于等于当前数量 + if (requested.compareTo(currentAmount) >= 0) { + if (mode == Actionable.MODULATE) { + getCellStoredMap().remove(what); + this.saveChanges(); + } + return currentAmount.compareTo(BigInteger.valueOf(Long.MAX_VALUE)) > 0 ? Long.MAX_VALUE : currentAmount.longValue(); + } else { + // 提取部分数量 + if (mode == Actionable.MODULATE) { + getCellStoredMap().put(what, currentAmount.subtract(requested)); + this.saveChanges(); + } + return requested.longValue(); + } + } + return 0; + } + + // 获取存储单元内所有物品的总数量(格式化字符串) + public String getTotalStorage() { + // 使用缓存的 totalStored,避免每次全表扫描 + 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 new file mode 100644 index 0000000..2ae9fb0 --- /dev/null +++ b/src/main/java/com/extendedae_plus/ae/items/InfinityBigIntegerCellItem.java @@ -0,0 +1,88 @@ +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.core.component.DataComponents; +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.item.component.CustomData; + +import java.math.BigInteger; +import java.util.List; + +public class InfinityBigIntegerCellItem extends Item implements ICellWorkbenchItem { + + public InfinityBigIntegerCellItem() { + super(new Properties().stacksTo(1).fireResistant()); + } + + @Override + public void appendHoverText(ItemStack stack, TooltipContext context, List tooltip, TooltipFlag tooltipFlag) { + 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,避免触发持久化或加载逻辑 + CustomData customData = stack.getOrDefault(DataComponents.CUSTOM_DATA, CustomData.EMPTY); + + if (!customData.isEmpty()) { + CompoundTag tag = customData.copyTag(); + + if (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)) + ); + } + + if (tag.contains(InfinityConstants.INFINITY_ITEM_TYPES)) { + try { + 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)) + ); + } catch (Exception ignored) {} + } + + if (tag.contains(InfinityConstants.INFINITY_ITEM_TOTAL)) { + try { + byte[] bytes = tag.getByteArray(InfinityConstants.INFINITY_ITEM_TOTAL); + BigInteger total = new 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) {} + } else if (tag.contains(InfinityConstants.INFINITY_CELL_ITEM_COUNT)) { + try { + byte[] bytes = tag.getByteArray(InfinityConstants.INFINITY_CELL_ITEM_COUNT); + BigInteger total = new 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) {} + } + } + } + + @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/init/ModCreativeTabs.java b/src/main/java/com/extendedae_plus/init/ModCreativeTabs.java index 10d9144..d5fc5da 100644 --- a/src/main/java/com/extendedae_plus/init/ModCreativeTabs.java +++ b/src/main/java/com/extendedae_plus/init/ModCreativeTabs.java @@ -29,6 +29,8 @@ public final class ModCreativeTabs { output.accept(ModItems.createEntitySpeedCardStack((byte) 4)); output.accept(ModItems.createEntitySpeedCardStack((byte) 8)); output.accept(ModItems.createEntitySpeedCardStack((byte) 16)); + + output.accept(ModItems.INFINITY_BIGINTEGER_CELL_ITEM.get()); }) .build()); diff --git a/src/main/java/com/extendedae_plus/init/ModItems.java b/src/main/java/com/extendedae_plus/init/ModItems.java index e985c1e..c059433 100644 --- a/src/main/java/com/extendedae_plus/init/ModItems.java +++ b/src/main/java/com/extendedae_plus/init/ModItems.java @@ -3,6 +3,7 @@ package com.extendedae_plus.init; import com.extendedae_plus.ExtendedAEPlus; import com.extendedae_plus.ae.definitions.upgrades.EntitySpeedCardItem; import com.extendedae_plus.ae.items.EntitySpeedTickerPartItem; +import com.extendedae_plus.ae.items.InfinityBigIntegerCellItem; import net.minecraft.world.item.BlockItem; import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; @@ -62,6 +63,11 @@ public final class ModItems { () -> new EntitySpeedCardItem(new Item.Properties()) ); + public static final DeferredItem INFINITY_BIGINTEGER_CELL_ITEM = ITEMS.register( + "infinity_biginteger_cell", InfinityBigIntegerCellItem::new + ); + + /** * 工厂:创建带 multiplier 的实体加速卡 ItemStack(2/4/8/16) */ 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 new file mode 100644 index 0000000..6462128 --- /dev/null +++ b/src/main/java/com/extendedae_plus/util/storage/InfinityDataStorage.java @@ -0,0 +1,48 @@ +package com.extendedae_plus.util.storage; + +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.ListTag; + +import java.math.BigInteger; + +/** + * 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 ListTag keys; + public ListTag amounts; + // 存储磁盘中物品的总数,使用 BigInteger 支持大容量 + public BigInteger itemCount; + + public InfinityDataStorage() { + this(new ListTag(), new ListTag(), BigInteger.ZERO); + } + + private InfinityDataStorage(ListTag keys, ListTag amounts, BigInteger itemCount) { + this.keys = keys; + this.amounts = amounts; + this.itemCount = itemCount; + } + + // 将 DataStorage 数据序列化为 NBT 格式 + public CompoundTag serializeNBT() { + CompoundTag nbt = new CompoundTag(); + 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 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 new file mode 100644 index 0000000..fabcfff --- /dev/null +++ b/src/main/java/com/extendedae_plus/util/storage/InfinityStorageManager.java @@ -0,0 +1,151 @@ +package com.extendedae_plus.util.storage; + +import net.minecraft.core.HolderLookup; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.ListTag; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.level.saveddata.SavedData; + +import javax.annotation.Nullable; +import java.lang.ref.WeakReference; +import java.math.BigInteger; +import java.util.*; + +/** + * 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 Factory FACTORY = new Factory<>(InfinityStorageManager::new, InfinityStorageManager::readNbt); + // 存储所有磁盘的Map,键为UUID,值为DataStorage对象 + private final Map cells; + @Nullable + private WeakReference registries; + + + // 构造方法,初始化磁盘Map + public InfinityStorageManager() { + cells = new HashMap<>(); + // 标记数据为“脏”,确保新创建的实例在下次保存时写入磁盘 + this.setDirty(); + } + + // 私有构造方法,用于从已有Map创建StorageManager + private InfinityStorageManager(Map cells) { + // 确保使用已加载的数据 + this.cells = cells; + // 标记数据为“脏”,确保新创建的实例在下次保存时写入磁盘 + this.setDirty(); + } + + + // 静态方法,从 NBT 数据反序列化创建 StorageManager 实例 + public static InfinityStorageManager readNbt(CompoundTag nbt, HolderLookup.Provider registries) { + // 读取格式版本,缺省视为 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); + } + + + @Override + public CompoundTag save(CompoundTag nbt, HolderLookup.Provider provider) { + // 将内存中的所有 cell 序列化为一个 ListTag + ListTag cellList = new ListTag(); + for (Map.Entry entry : cells.entrySet()) { + CompoundTag cell = new CompoundTag(); + cell.putUUID(InfinityConstants.INFINITY_CELL_UUID, entry.getKey()); + cell.put(InfinityConstants.INFINITY_CELL_DATA, entry.getValue().serializeNBT()); + cellList.add(cell); + } + nbt.put(InfinityConstants.INFINITY_CELL_LIST, cellList); + // 写入当前格式版本号,便于未来迁移与兼容判断 + nbt.putInt(InfinityConstants.FORMAT_VERSION_FIELD, InfinityConstants.FORMAT_VERSION); + return nbt; + } + + // 返回当前已加载的所有 UUID 的不可变视图,用于命令或调试用途 + public Set getAllLoadedUUIDs() { + return Collections.unmodifiableSet(cells.keySet()); + } + + // 更新或添加某个 UUID 对应的数据并标记为脏(需要保存) + public void updateCell(UUID uuid, InfinityDataStorage infinityDataStorage) { + cells.put(uuid, infinityDataStorage); + // 标记数据为“脏”,确保修改后的数据会在下次保存时写入磁盘 + setDirty(); + } + + // 删除某个 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 的磁盘数据,包括堆栈键、数量和总项目数 + 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; + } + // 更新 DataStorage 的 itemCount 字段 + cellToModify.itemCount = itemCount; + // 将修改后的 DataStorage 对象更新到 cells 映射 + updateCell(uuid, cellToModify); + } + + + public static InfinityStorageManager getInstance(MinecraftServer server) { + ServerLevel world = server.getLevel(ServerLevel.OVERWORLD); + var manager = world.getDataStorage().computeIfAbsent(FACTORY, InfinityConstants.SAVE_FILE_NAME); + manager.registries = new WeakReference<>(server.registryAccess()); + return manager; + } + + public HolderLookup.Provider getRegistries() { + var r = this.registries; + if (r == null) { + throw new IllegalStateException("StorageManager was not initialized properly."); + } + + var registries = r.get(); + if (registries == null) { + throw new IllegalStateException("Using a StorageManager whose server was already closed"); + } + + return registries; + } +} 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 babef8e..8775525 100644 --- a/src/main/resources/assets/extendedae_plus/lang/en_us.json +++ b/src/main/resources/assets/extendedae_plus/lang/en_us.json @@ -22,6 +22,9 @@ "item.extendedae_plus.entity_speed_card.x4": "Entity Speed Card (x4)", "item.extendedae_plus.entity_speed_card.x8": "Entity Speed Card (x8)", "item.extendedae_plus.entity_speed_card.x16": "Entity Speed Card (x16)", + "item.extendedae_plus.infinity_biginteger_cell": "§4De§cvou§6rer §eof §aCo§bsmic §dSilence", + "tooltip.extendedae_plus.infinity_biginteger_cell.summon1": "§6Through ninefold sacrifice, the Void echoes§r—§8Iava, Lord of the Void§r, bestows upon thee this artifact", + "tooltip.extendedae_plus.infinity_biginteger_cell.summon2": "§b—§4A §dUni§cverse §eWith§ain §6A §bSin§5gle §9Point", "tooltip.extendedae_plus.entity_speed_card.multiplier": "Multiplier: %s", "tooltip.extendedae_plus.entity_speed_card.max": "Maximum Effect: %s Times", 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 8592356..fe2d4a8 100644 --- a/src/main/resources/assets/extendedae_plus/lang/zh_cn.json +++ b/src/main/resources/assets/extendedae_plus/lang/zh_cn.json @@ -22,6 +22,9 @@ "item.extendedae_plus.entity_speed_card.x4": "实体加速卡 (x4)", "item.extendedae_plus.entity_speed_card.x8": "实体加速卡 (x8)", "item.extendedae_plus.entity_speed_card.x16": "实体加速卡 (x16)", + "item.extendedae_plus.infinity_biginteger_cell": "§4吞§c噬§6万§e籁§a的§b寂§d静", + "tooltip.extendedae_plus.infinity_biginteger_cell.summon1": "§6九重献祭, 终得虚空回响§r——觐见§8虚空之主Iava§r, 赐汝此物", + "tooltip.extendedae_plus.infinity_biginteger_cell.summon2": "§b——§4方§d寸§c之§e间§a, §6自§b有§5千§9寰", "tooltip.extendedae_plus.entity_speed_card.multiplier": "乘数: %s", "tooltip.extendedae_plus.entity_speed_card.max": "最大生效: %s 倍", From 9ac7d118138deff5ff38a74592839a63e3a7a695 Mon Sep 17 00:00:00 2001 From: C-H716 <1536152356@qq.com> Date: Fri, 26 Sep 2025 17:57:42 +0800 Subject: [PATCH 37/43] =?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 | 83 ++++++++++++------- 1 file changed, 53 insertions(+), 30 deletions(-) diff --git a/src/main/java/com/extendedae_plus/util/ConfigParsingUtils.java b/src/main/java/com/extendedae_plus/util/ConfigParsingUtils.java index c1f2395..0cf6ddc 100644 --- a/src/main/java/com/extendedae_plus/util/ConfigParsingUtils.java +++ b/src/main/java/com/extendedae_plus/util/ConfigParsingUtils.java @@ -114,48 +114,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(List source) { - if (cachedBlacklist == null) { - synchronized (CACHE_LOCK) { - if (cachedBlacklist == null) { - cachedBlacklist = compilePatterns(source); - } - } - } else { - synchronized (CACHE_LOCK) { - List newCachedBlackList = compilePatterns(source); - if (!cachedBlacklist.equals(newCachedBlackList)) { - cachedBlacklist = newCachedBlackList; - } - } + public static List getCachedBlacklist(java.util.List 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 Collections.unmodifiableList(cachedBlacklist); } /** * 获取已解析并缓存的倍率列表(线程安全、懒加载)。 */ - public static List getCachedMultiplierEntries(List source) { - if (cachedMultiplierEntries == null) { - synchronized (CACHE_LOCK) { - if (cachedMultiplierEntries == null) { - cachedMultiplierEntries = parseMultiplierList(source); - } - } - }else { - synchronized (CACHE_LOCK) { - List newCachedMultiplierEntries = parseMultiplierList(source); - if (!cachedMultiplierEntries.equals(newCachedMultiplierEntries)) { - cachedMultiplierEntries = newCachedMultiplierEntries; - } - } + public static List getCachedMultiplierEntries(java.util.List source) { + List normalized = normalizeSource(source); + + if (cachedMultiplierEntries != null && listEquals(cachedMultiplierSourceSnapshot, normalized)) { + return Collections.unmodifiableList(cachedMultiplierEntries); } - return Collections.unmodifiableList(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 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 fc25e2bd1c827d27bb91a99ccbce1a2b17b999eb Mon Sep 17 00:00:00 2001 From: C-H716 <1536152356@qq.com> Date: Fri, 26 Sep 2025 22:09:20 +0800 Subject: [PATCH 38/43] =?UTF-8?q?feat:=20=E5=AE=9E=E4=BD=93=E5=8A=A0?= =?UTF-8?q?=E9=80=9F=E5=99=A8=E7=95=8C=E9=9D=A2=E6=98=BE=E7=A4=BA=E8=83=BD?= =?UTF-8?q?=E9=87=8F=E6=98=AF=E5=90=A6=E5=85=85=E8=B6=B3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ae/menu/EntitySpeedTickerMenu.java | 44 ++++++++++++----- .../ae/parts/EntitySpeedTickerPart.java | 47 ++++++++++++++----- .../ae/screen/EntitySpeedTickerScreen.java | 7 +++ .../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, 78 insertions(+), 25 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 e72e8ce..40e1e28 100644 --- a/src/main/java/com/extendedae_plus/ae/menu/EntitySpeedTickerMenu.java +++ b/src/main/java/com/extendedae_plus/ae/menu/EntitySpeedTickerMenu.java @@ -36,6 +36,28 @@ public class EntitySpeedTickerMenu extends UpgradeableMenu partItem) { @@ -81,18 +75,42 @@ public class EntitySpeedTickerPart extends UpgradeablePart implements IGridTicka .setIdlePowerUsage(1) .addService(IGridTickable.class, this); } + // 当前打开的菜单实例(如果有) + public EntitySpeedTickerMenu menu; + // 控制是否启用加速(默认启用) + private boolean accelerateEnabled = true; + // 标记网络中能量是否充足(用于 GUI 提示,默认充足) + private boolean networkEnergySufficient = true; public boolean getAccelerateEnabled() { return this.accelerateEnabled; } + public boolean isNetworkEnergySufficient() { + return this.networkEnergySufficient; + } + public void setAccelerateEnabled(boolean accelerateEnabled) { this.accelerateEnabled = accelerateEnabled; } + /** + * 更新网络能量充足标记并在菜单存在且状态变化时触发同步 + * @param sufficient 是否能量充足 + */ + private void updateNetworkEnergySufficient(boolean sufficient) { + // 保持部件内部状态一致(部件为权威来源) + this.networkEnergySufficient = sufficient; + if (this.menu != null) { + try { + // 使用菜单的封装方法更新并广播,以保持封装性 + this.menu.setNetworkEnergySufficient(sufficient); + } catch (Exception ignored) {} + } + } + /** * 获取当前状态下的静态模型(用于渲染) - * * @return 当前状态的模型 */ public IPartModel getStaticModels() { @@ -105,8 +123,6 @@ public class EntitySpeedTickerPart extends UpgradeablePart implements IGridTicka } } - - /** * 当玩家激活部件(右键)时调用,打开自定义菜单 * @@ -239,14 +255,21 @@ public class EntitySpeedTickerPart extends UpgradeablePart implements IGridTicka requiredPower *= multiplier; - // 先模拟提取以检查网络中是否有足够能量,再真正抽取 +// 先模拟提取以检查网络中是否有足够能量,再真正抽取 double simulated = getMainNode().getGrid().getEnergyService() .extractAEPower(requiredPower, Actionable.SIMULATE, PowerMultiplier.CONFIG); - if (simulated < requiredPower) return; + if (simulated < requiredPower) { + updateNetworkEnergySufficient(false); // 能量不足 + return; + } double extractedPower = getMainNode().getGrid().getEnergyService() .extractAEPower(requiredPower, Actionable.MODULATE, PowerMultiplier.CONFIG); - if (extractedPower < requiredPower) return; + if (extractedPower < requiredPower) { + updateNetworkEnergySufficient(false); // 能量不足 + return; + } + updateNetworkEnergySufficient(true); // 能量充足 // 计算加速倍数:基于 2 的次方,并把 8 张映射到最大 1024x(2^10) // 已由 product 计算得到 speed;上面已在没有卡时提前返回 diff --git a/src/main/java/com/extendedae_plus/ae/screen/EntitySpeedTickerScreen.java b/src/main/java/com/extendedae_plus/ae/screen/EntitySpeedTickerScreen.java index 32421e2..30605bb 100644 --- a/src/main/java/com/extendedae_plus/ae/screen/EntitySpeedTickerScreen.java +++ b/src/main/java/com/extendedae_plus/ae/screen/EntitySpeedTickerScreen.java @@ -128,6 +128,13 @@ public class EntitySpeedTickerScreen extends Up double finalPower = PowerUtils.computeFinalPowerForProduct(effectiveSpeed, energyCardCount); double remainingRatio = PowerUtils.getRemainingRatio(energyCardCount); + // 如果网络能量不足,优先显示警告信息并在能量值处显示 0 + if (!getMenu().networkEnergySufficient) { + setTextContent("enable", Component.translatable("screen.extendedae_plus.entity_speed_ticker.warning_network_energy_insufficient")); + }else { + setTextContent("enable", null); + } + 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 aaeab6c..352842a 100644 --- a/src/main/resources/assets/ae2/screens/entity_speed_ticker.json +++ b/src/main/resources/assets/ae2/screens/entity_speed_ticker.json @@ -51,8 +51,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 8775525..c8d3754 100644 --- a/src/main/resources/assets/extendedae_plus/lang/en_us.json +++ b/src/main/resources/assets/extendedae_plus/lang/en_us.json @@ -53,6 +53,7 @@ "screen.extendedae_plus.entity_speed_ticker.power_ratio": "Power Ratio: %s", "screen.extendedae_plus.entity_speed_ticker.speed": "Current Acceleration Multiplier: %d", "screen.extendedae_plus.entity_speed_ticker.multiplier": "Additional 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 Speed Card to Enable Acceleration", "item.extendedae_plus.entity_speed_ticker.tip.max": "Up to 1024x Acceleration", 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 fe2d4a8..3df2054 100644 --- a/src/main/resources/assets/extendedae_plus/lang/zh_cn.json +++ b/src/main/resources/assets/extendedae_plus/lang/zh_cn.json @@ -53,6 +53,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 加速", From f60629aadfb75ca2f748f6fdd04e4dcd210339b7 Mon Sep 17 00:00:00 2001 From: C-H716 <1536152356@qq.com> Date: Sat, 27 Sep 2025 20:18:13 +0800 Subject: [PATCH 39/43] =?UTF-8?q?feat:=20=E8=AF=B7=E6=B1=82=E9=87=8Fpop?= =?UTF-8?q?=E4=BD=8D=E7=BD=AE=E8=B0=83=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mixin/ae2/autopattern/CraftingTreeNodeMixin.java | 7 ------- .../mixin/ae2/autopattern/CraftingTreeProcessMixin.java | 1 + 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/autopattern/CraftingTreeNodeMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/autopattern/CraftingTreeNodeMixin.java index b2651d5..23d0102 100644 --- a/src/main/java/com/extendedae_plus/mixin/ae2/autopattern/CraftingTreeNodeMixin.java +++ b/src/main/java/com/extendedae_plus/mixin/ae2/autopattern/CraftingTreeNodeMixin.java @@ -20,11 +20,4 @@ public class CraftingTreeNodeMixin { // push the requestedAmount before addContainerItems is called RequestedAmountHolder.push(requestedAmount); } - - @Inject(method = "request(Lappeng/crafting/inv/CraftingSimulationState;JLappeng/api/stacks/KeyCounter;)V", - at = @At(value = "RETURN")) - private void clearRequestedAmountOnReturn(CraftingSimulationState inv, long requestedAmount, KeyCounter containerItems, CallbackInfo ci) { - // pop the pushed requested amount on return - RequestedAmountHolder.pop(); - } } diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/autopattern/CraftingTreeProcessMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/autopattern/CraftingTreeProcessMixin.java index 68e510e..fb0d122 100644 --- a/src/main/java/com/extendedae_plus/mixin/ae2/autopattern/CraftingTreeProcessMixin.java +++ b/src/main/java/com/extendedae_plus/mixin/ae2/autopattern/CraftingTreeProcessMixin.java @@ -55,6 +55,7 @@ public abstract class CraftingTreeProcessMixin { CraftingTreeNodeAccessor parentAcc = (CraftingTreeNodeAccessor) craftingTreeNode; AEKey parentTarget = parentAcc.eap$getWhat(); long requested = RequestedAmountHolder.get(); + RequestedAmountHolder.pop(); // 根据配置决定是否在 provider 间轮询分配请求量(默认开启) long perProvider = 1L; From a95389e5fa0c36a8990020e9ef1b144d8450ad0c Mon Sep 17 00:00:00 2001 From: C-H716 <1536152356@qq.com> Date: Sat, 27 Sep 2025 20:22:57 +0800 Subject: [PATCH 40/43] =?UTF-8?q?feat:=20=E5=85=81=E8=AE=B8=E6=97=A0?= =?UTF-8?q?=E9=99=90=E7=9B=98=E4=B8=AD=E6=94=BE=E5=85=A5=E7=A9=BA=E7=9A=84?= =?UTF-8?q?=E6=97=A0=E9=99=90=E7=9B=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ae/api/storage/InfinityBigIntegerCellInventory.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) 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 675c41e..f050184 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 @@ -343,7 +343,10 @@ public class InfinityBigIntegerCellInventory implements StorageCell { return 0; } // 不允许存储无限单元自身 - if (what instanceof AEItemKey itemKey && itemKey.getItem() instanceof InfinityBigIntegerCellItem) { + if (what instanceof AEItemKey itemKey && + itemKey.getItem() instanceof InfinityBigIntegerCellItem && + itemKey.get(DataComponents.CUSTOM_DATA) != null + ) { return 0; } From f9108851903b77b3529e308abea3605b13103c69 Mon Sep 17 00:00:00 2001 From: C-H716 <1536152356@qq.com> Date: Sat, 27 Sep 2025 20:59:17 +0800 Subject: [PATCH 41/43] =?UTF-8?q?feat:=20=E9=87=8D=E6=96=B0=E8=B0=83?= =?UTF-8?q?=E6=95=B4=E5=AE=9E=E4=BD=93=E5=8A=A0=E9=80=9F=E5=99=A8=E4=BB=A3?= =?UTF-8?q?=E7=A0=81=E7=BB=93=E6=9E=84=EF=BC=8C=E5=A2=9E=E5=8A=A0=E5=8F=AF?= =?UTF-8?q?=E8=AF=BB=E6=80=A7=EF=BC=9B=E6=B7=BB=E5=8A=A0=E5=AE=9E=E4=BD=93?= =?UTF-8?q?=E5=8A=A0=E9=80=9F=E5=99=A8=E5=AF=B9appflux=E5=AD=98=E5=82=A8?= =?UTF-8?q?=E7=94=B5=E9=87=8F=E7=9A=84=E6=89=A3=E9=99=A4=E6=94=AF=E6=8C=81?= =?UTF-8?q?=EF=BC=9B=E6=B7=BB=E5=8A=A0=E5=AE=9E=E4=BD=93=E5=8A=A0=E9=80=9F?= =?UTF-8?q?=E5=99=A8=E6=98=AF=E5=90=A6=E4=BC=98=E5=85=88=E4=BD=BF=E7=94=A8?= =?UTF-8?q?=E7=A3=81=E7=9B=98=E5=AD=98=E5=82=A8=E8=83=BD=E6=BA=90=E9=85=8D?= =?UTF-8?q?=E7=BD=AE=E9=A1=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ae/menu/EntitySpeedTickerMenu.java | 242 +++++++++--------- .../ae/parts/EntitySpeedTickerPart.java | 238 ++++++++++------- .../ae/screen/EntitySpeedTickerScreen.java | 142 +++++----- .../extendedae_plus/config/ModConfigs.java | 7 + .../util/FluxEnergyHelper.java | 41 +++ .../com/extendedae_plus/util/PowerUtils.java | 162 ------------ .../{ => entitySpeed}/ConfigParsingUtils.java | 99 ++++--- .../util/entitySpeed/PowerUtils.java | 98 +++++++ .../assets/extendedae_plus/lang/en_us.json | 1 + .../assets/extendedae_plus/lang/zh_cn.json | 1 + 10 files changed, 543 insertions(+), 488 deletions(-) create mode 100644 src/main/java/com/extendedae_plus/util/FluxEnergyHelper.java delete mode 100644 src/main/java/com/extendedae_plus/util/PowerUtils.java rename src/main/java/com/extendedae_plus/util/{ => entitySpeed}/ConfigParsingUtils.java (68%) create mode 100644 src/main/java/com/extendedae_plus/util/entitySpeed/PowerUtils.java 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 40e1e28..086a261 100644 --- a/src/main/java/com/extendedae_plus/ae/menu/EntitySpeedTickerMenu.java +++ b/src/main/java/com/extendedae_plus/ae/menu/EntitySpeedTickerMenu.java @@ -9,165 +9,167 @@ import com.extendedae_plus.ae.screen.EntitySpeedTickerScreen; import com.extendedae_plus.config.ModConfigs; import com.extendedae_plus.init.ModItems; import com.extendedae_plus.init.ModMenuTypes; -import com.extendedae_plus.util.ConfigParsingUtils; -import com.extendedae_plus.util.PowerUtils; +import com.extendedae_plus.util.entitySpeed.ConfigParsingUtils; +import com.extendedae_plus.util.entitySpeed.PowerUtils; import it.unimi.dsi.fastutil.shorts.ShortSet; import net.minecraft.client.Minecraft; -import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.world.entity.player.Inventory; -import net.minecraft.world.level.block.Block; +import net.minecraft.world.inventory.Slot; import net.minecraft.world.level.block.entity.BlockEntity; -// 实体加速器菜单,负责与客户端界面同步数据 +/** + * 实体加速器菜单,负责管理客户端与服务端的数据同步,处理加速卡、能量卡和目标方块的状态。 + */ public class EntitySpeedTickerMenu extends UpgradeableMenu { - @GuiSync(716) - public boolean accelerateEnabled = true; - // 已安装的实体加速卡数量(用于能耗计算) - @GuiSync(717) - public int entitySpeedCardCount; - // 已安装的能量卡数量 - @GuiSync(718) - public int energyCardCount; - // 当前生效的配置倍率(从配置中读取并同步) - // 当前计算出的生效速度(product of multipliers),同步给客户端用于显示 - @GuiSync(719) - public int effectiveSpeed = 1; - @GuiSync(720) - public double multiplier = 1.0; - @GuiSync(721) - public boolean targetBlacklisted = false; - // 来自部件的网络能量充足提示(服务端设置,客户端用于显示警告) - @GuiSync(722) public boolean networkEnergySufficient = true; + @GuiSync(716) public boolean accelerateEnabled = true; // 是否启用加速 + @GuiSync(717) public int entitySpeedCardCount; // 已安装的实体加速卡数量 + @GuiSync(718) public int energyCardCount; // 已安装的能量卡数量 + @GuiSync(719) public int effectiveSpeed = 1; // 当前生效的加速倍率 + @GuiSync(720) public double multiplier = 1.0; // 目标方块的配置倍率 + @GuiSync(721) public boolean targetBlacklisted = false; // 目标方块是否在黑名单中 + @GuiSync(722) public boolean networkEnergySufficient = true; // 网络能量是否充足 + /** + * 构造函数,初始化菜单并绑定部件。 + * @param id 菜单ID + * @param ip 玩家背包 + * @param host 关联的实体加速器部件 + */ + public EntitySpeedTickerMenu(int id, Inventory ip, EntitySpeedTickerPart host) { + super(ModMenuTypes.ENTITY_TICKER_MENU.get(), id, ip, host); + if (host != null) { + host.menu = this; // 绑定菜单到部件 + this.accelerateEnabled = host.getAccelerateEnabled(); // 同步初始开关状态 + } + } + /** + * 获取加速开关状态。 + * @return 是否启用加速 + */ public boolean getAccelerateEnabled() { return this.accelerateEnabled; } + /** + * 设置加速开关状态,并同步到部件。 + * @param enabled 是否启用加速 + */ public void setAccelerateEnabled(boolean enabled) { this.accelerateEnabled = enabled; + if (getHost() != null) { + getHost().setAccelerateEnabled(enabled); // 同步到部件 + } + broadcastChanges(); // 广播状态变化 } /** - * 从 Part 更新 networkEnergySufficient 的封装方法(由服务器调用) - * 该方法会更新 @GuiSync 字段并广播变化到客户端 + * 更新网络能量充足状态并广播到客户端。 + * @param sufficient 是否能量充足 */ public void setNetworkEnergySufficient(boolean sufficient) { this.networkEnergySufficient = sufficient; - // 触发一次数据广播,使客户端立即接收到最新状态 - this.broadcastChanges(); - } - - - // 构造方法,初始化菜单并与部件绑定 - public EntitySpeedTickerMenu(int id, Inventory ip, EntitySpeedTickerPart host) { - super(ModMenuTypes.ENTITY_TICKER_MENU.get(), id, ip, host); - // 让部件持有当前菜单实例,便于通信 - getHost().menu = this; - // 初始同步部件上的开关状态到菜单(服务器端构造时保证一致) - try { - this.accelerateEnabled = getHost().getAccelerateEnabled(); - } catch (Exception ignored) {} + broadcastChanges(); } + /** + * 服务端数据同步到客户端时调用,更新卡数量、目标状态和生效速度。 + */ @Override public void onServerDataSync(ShortSet updatedFields) { super.onServerDataSync(updatedFields); - // 重新统计实体加速卡和能量卡数量 - this.entitySpeedCardCount = this.getUpgrades().getInstalledUpgrades(ModItems.ENTITY_SPEED_CARD.get()); - this.energyCardCount = this.getUpgrades().getInstalledUpgrades(AEItems.ENERGY_CARD); - - // 计算当前面向方块的倍率(服务器端),并同步给客户端 - double mult = 1.0; - try { - BlockEntity target = getHost().getLevel().getBlockEntity( - getHost().getBlockEntity().getBlockPos().relative(getHost().getSide()) - ); - if (target != null) { - String blockId = BuiltInRegistries.BLOCK.getKey(target.getBlockState().getBlock()).toString(); - for (ConfigParsingUtils.MultiplierEntry me : - ConfigParsingUtils.getCachedMultiplierEntries(ModConfigs.ENTITY_TICKER_MULTIPLIERS.get())) { - if (me.pattern.matcher(blockId).matches()) { - mult = Math.max(mult, me.multiplier); - } - } - } - } catch (Exception ignored) { - } - this.multiplier = mult; - - // 检查目标是否在黑名单中,如果是则标记并将生效速度设为 0(服务器端计算) - boolean blacklisted = false; - try { - BlockEntity target = getHost().getLevel().getBlockEntity( - getHost().getBlockEntity().getBlockPos().relative(getHost().getSide()) - ); - if (target != null) { - Block block = target.getBlockState().getBlock(); - String blockId = BuiltInRegistries.BLOCK.getKey(block).toString(); // 直接拿到 "minecraft:stone" - - for (java.util.regex.Pattern p : ConfigParsingUtils.getCachedBlacklist( - ModConfigs.ENTITY_TICKER_BLACK_LIST.get())) { - if (p.matcher(blockId).matches()) { - blacklisted = true; - break; - } - } - } - } catch (Exception ignored) { - } - this.targetBlacklisted = blacklisted; - - // 计算生效速度:如果被黑名单则为 0,否则进行正常计算(使用工具类从菜单直接计算 product with cap,最多 8 张) - if (this.targetBlacklisted) { - this.effectiveSpeed = 0; - } else { - this.effectiveSpeed = (int) PowerUtils.computeProductWithCapFromMenu(this, 8); - } - - // 从部件同步网络能量充足状态:仅在服务器端从部件读取,客户端应使用由 @GuiSync 同步过来的值 - try { - EntitySpeedTickerPart host = getHost(); - if (host != null && !isClientSide()) { - this.networkEnergySufficient = host.isNetworkEnergySufficient(); - } - } catch (Exception ignored) {} - - // 如果在客户端,刷新界面 + updateCardCounts(); // 更新卡数量 + updateTargetStatus(); // 更新目标方块的黑名单和倍率 + updateEffectiveSpeed(); // 计算生效速度 + updateNetworkEnergyStatus(); // 同步能量状态 if (isClientSide()) { - if (Minecraft.getInstance().screen instanceof EntitySpeedTickerScreen screen) { - screen.refreshGui(); - } + refreshClientGui(); // 客户端刷新界面 } } - // 当任意槽位发生变化时调用 + /** + * 当槽位内容变化时调用,客户端更新卡数量和生效速度。 + * @param slot 发生变化的槽位 + */ @Override - public void onSlotChange(net.minecraft.world.inventory.Slot slot) { + public void onSlotChange(Slot slot) { super.onSlotChange(slot); - // 客户端重新统计卡数量并刷新界面 if (isClientSide()) { - this.entitySpeedCardCount = this.getUpgrades().getInstalledUpgrades(ModItems.ENTITY_SPEED_CARD.get()); - this.energyCardCount = this.getUpgrades().getInstalledUpgrades(AEItems.ENERGY_CARD); - // 立即在客户端计算生效速度以便界面即时反馈(使用与服务端相同的工具方法,最多 8 张卡) - this.effectiveSpeed = (int) PowerUtils.computeProductWithCapFromMenu(this, 8); - if (Minecraft.getInstance().screen instanceof EntitySpeedTickerScreen screen) { - screen.refreshGui(); - } + updateCardCounts(); + updateEffectiveSpeed(); + refreshClientGui(); } } + /** + * 广播数据变化,清理未启用槽位的显示堆栈。 + */ @Override public void broadcastChanges() { - // 遍历所有槽位,清理未启用但有物品显示的 OptionalFakeSlot for (Object o : this.slots) { - if (o instanceof OptionalFakeSlot fs) { - if (!fs.isSlotEnabled() && !fs.getDisplayStack().isEmpty()) { - fs.clearStack(); - } + if (o instanceof OptionalFakeSlot fs && !fs.isSlotEnabled() && !fs.getDisplayStack().isEmpty()) { + fs.clearStack(); // 清理未启用槽位的显示 } } - // 调用标准的同步方法,通知监听者数据已更新 - this.standardDetectAndSendChanges(); + standardDetectAndSendChanges(); + } + + /** + * 更新加速卡和能量卡的数量。 + */ + private void updateCardCounts() { + this.entitySpeedCardCount = this.getUpgrades().getInstalledUpgrades(ModItems.ENTITY_SPEED_CARD.get()); + this.energyCardCount = this.getUpgrades().getInstalledUpgrades(AEItems.ENERGY_CARD); + } + + /** + * 更新目标方块的黑名单状态和倍率。 + */ + private void updateTargetStatus() { + BlockEntity target = getTargetBlockEntity(); + if (target == null) { + this.multiplier = 1.0; + this.targetBlacklisted = false; + return; + } + String blockId = net.minecraft.core.registries.BuiltInRegistries.BLOCK.getKey(target.getBlockState().getBlock()).toString(); + this.multiplier = ConfigParsingUtils.getMultiplierForBlock(blockId, ModConfigs.ENTITY_TICKER_MULTIPLIERS.get()); + this.targetBlacklisted = ConfigParsingUtils.isBlockBlacklisted(blockId, ModConfigs.ENTITY_TICKER_BLACK_LIST.get()); + } + + /** + * 计算生效速度(考虑黑名单和卡数量)。 + */ + private void updateEffectiveSpeed() { + this.effectiveSpeed = targetBlacklisted ? 0 : (int) PowerUtils.computeProductWithCap(getUpgrades(), 8); + } + + /** + * 同步网络能量状态(仅服务端)。 + */ + private void updateNetworkEnergyStatus() { + if (!isClientSide() && getHost() != null) { + this.networkEnergySufficient = getHost().isNetworkEnergySufficient(); + } + } + + /** + * 客户端刷新界面。 + */ + private void refreshClientGui() { + if (Minecraft.getInstance().screen instanceof EntitySpeedTickerScreen screen) { + screen.refreshGui(); + } + } + + /** + * 获取目标方块实体。 + * @return 目标方块实体或 null + */ + private BlockEntity getTargetBlockEntity() { + return getHost() != null ? + getHost().getLevel().getBlockEntity( + getHost().getBlockEntity().getBlockPos().relative(getHost().getSide()) + ) : null; } } \ No newline at end of file diff --git a/src/main/java/com/extendedae_plus/ae/parts/EntitySpeedTickerPart.java b/src/main/java/com/extendedae_plus/ae/parts/EntitySpeedTickerPart.java index a6fa886..293d576 100644 --- a/src/main/java/com/extendedae_plus/ae/parts/EntitySpeedTickerPart.java +++ b/src/main/java/com/extendedae_plus/ae/parts/EntitySpeedTickerPart.java @@ -4,12 +4,15 @@ import appeng.api.config.Actionable; import appeng.api.config.PowerMultiplier; import appeng.api.networking.GridFlags; import appeng.api.networking.IGridNode; +import appeng.api.networking.energy.IEnergyService; +import appeng.api.networking.security.IActionSource; import appeng.api.networking.ticking.IGridTickable; import appeng.api.networking.ticking.TickRateModulation; import appeng.api.networking.ticking.TickingRequest; import appeng.api.parts.IPartCollisionHelper; import appeng.api.parts.IPartItem; import appeng.api.parts.IPartModel; +import appeng.api.storage.MEStorage; import appeng.api.upgrades.IUpgradeableObject; import appeng.core.definitions.AEItems; import appeng.items.parts.PartModels; @@ -22,9 +25,8 @@ import com.extendedae_plus.ae.menu.EntitySpeedTickerMenu; import com.extendedae_plus.config.ModConfigs; import com.extendedae_plus.init.ModItems; import com.extendedae_plus.init.ModMenuTypes; -import com.extendedae_plus.util.ConfigParsingUtils; -import com.extendedae_plus.util.PowerUtils; -import net.minecraft.core.BlockPos; +import com.extendedae_plus.util.entitySpeed.ConfigParsingUtils; +import com.extendedae_plus.util.entitySpeed.PowerUtils; import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.network.chat.Component; import net.minecraft.resources.ResourceLocation; @@ -36,11 +38,11 @@ import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.entity.BlockEntityTicker; import net.minecraft.world.level.block.entity.BlockEntityType; import net.minecraft.world.phys.Vec3; +import net.neoforged.fml.ModList; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.List; -import java.util.regex.Pattern; +import java.lang.reflect.Method; /** * EntitySpeedTickerPart 是一个可升级的 AE2 部件

@@ -63,24 +65,22 @@ public class EntitySpeedTickerPart extends UpgradeablePart implements IGridTicka MODELS_HAS_CHANNEL = new PartModel(MODEL_BASE, ResourceLocation.fromNamespaceAndPath(ExtendedAEPlus.MODID, "part/entity_speed_ticker_has_channel")); } + public EntitySpeedTickerMenu menu; // 当前打开的菜单实例 + private boolean accelerateEnabled = true; // 是否启用加速 + private boolean networkEnergySufficient = true; // 网络能量是否充足 + + /** - * 构造函数,初始化部件并设置网络节点属性 + * 构造函数,初始化部件并设置网络节点属性。 * @param partItem 部件物品 */ public EntitySpeedTickerPart(IPartItem partItem) { super(partItem); - // 设置网络节点属性:需要通道、空闲功耗为1,并注册为 IGridTickable 服务 this.getMainNode() .setFlags(GridFlags.REQUIRE_CHANNEL) .setIdlePowerUsage(1) .addService(IGridTickable.class, this); } - // 当前打开的菜单实例(如果有) - public EntitySpeedTickerMenu menu; - // 控制是否启用加速(默认启用) - private boolean accelerateEnabled = true; - // 标记网络中能量是否充足(用于 GUI 提示,默认充足) - private boolean networkEnergySufficient = true; public boolean getAccelerateEnabled() { return this.accelerateEnabled; @@ -90,29 +90,33 @@ public class EntitySpeedTickerPart extends UpgradeablePart implements IGridTicka return this.networkEnergySufficient; } - public void setAccelerateEnabled(boolean accelerateEnabled) { - this.accelerateEnabled = accelerateEnabled; - } - /** - * 更新网络能量充足标记并在菜单存在且状态变化时触发同步 - * @param sufficient 是否能量充足 + * 设置加速开关状态并通知菜单。 + * @param enabled 是否启用加速 */ - private void updateNetworkEnergySufficient(boolean sufficient) { - // 保持部件内部状态一致(部件为权威来源) - this.networkEnergySufficient = sufficient; - if (this.menu != null) { - try { - // 使用菜单的封装方法更新并广播,以保持封装性 - this.menu.setNetworkEnergySufficient(sufficient); - } catch (Exception ignored) {} + public void setAccelerateEnabled(boolean enabled) { + this.accelerateEnabled = enabled; + if (menu != null) { + menu.setAccelerateEnabled(enabled); } } /** - * 获取当前状态下的静态模型(用于渲染) + * 更新网络能量充足状态并通知菜单。 + * @param sufficient 是否能量充足 + */ + private void updateNetworkEnergySufficient(boolean sufficient) { + this.networkEnergySufficient = sufficient; + if (menu != null) { + menu.setNetworkEnergySufficient(sufficient); + } + } + + /** + * 获取当前状态的渲染模型。 * @return 当前状态的模型 */ + @Override public IPartModel getStaticModels() { if (this.isActive() && this.isPowered()) { return MODELS_HAS_CHANNEL; @@ -196,87 +200,149 @@ public class EntitySpeedTickerPart extends UpgradeablePart implements IGridTicka } /** - * 以指定速度对目标方块实体进行 tick 操作 - * - * @param blockEntity 需要被 tick 的方块实体 - * @param 方块实体类型 + * 对目标方块实体执行加速 tick 操作。 + * @param blockEntity 目标方块实体 + * @param 方块实体类型 */ private void ticker(@NotNull T blockEntity) { - if (this.getGridNode() == null - || this.getMainNode() == null - || this.getMainNode().getGrid() == null) { + if (!isValidForTicking()) { return; } - // 获取方块实体的位置 - BlockPos pos = blockEntity.getBlockPos(); - if (blockEntity.getLevel() == null) return; - - - // 检查黑名单(支持通配符/正则) String blockId = BuiltInRegistries.BLOCK.getKey(blockEntity.getBlockState().getBlock()).toString(); - - // 使用工具类的缓存接口(工具类内部负责懒加载/线程安全) - List compiledBlacklist = ConfigParsingUtils.getCachedBlacklist(ModConfigs.ENTITY_TICKER_BLACK_LIST.get()); - for (Pattern p : compiledBlacklist) { - if (p.matcher(blockId).matches()) return; + if (ConfigParsingUtils.isBlockBlacklisted(blockId, ModConfigs.ENTITY_TICKER_BLACK_LIST.get())) { + return; } - // 获取该方块实体的 Ticker - @SuppressWarnings("unchecked") - BlockEntityTicker blockEntityTicker = this.getLevel() - .getBlockState(pos) - .getTicker(this.getLevel(), (BlockEntityType) blockEntity.getType()); - if (blockEntityTicker == null) return; + BlockEntityTicker ticker = getTicker(blockEntity); + if (ticker == null) { + return; + } - // 使用集中定义的 CardDef 列表,支持以后添加等级或改倍率而无需修改此逻辑 - int energyCardCount = getUpgrades().getInstalledUpgrades(AEItems.ENERGY_CARD); + int speed = calculateSpeed(); + if (speed <= 0) { + return; + } - // 使用已注册的单一 Item 计算已安装卡数量(总计,用于能耗计算) + double requiredPower = calculateRequiredPower(speed, blockId); + if (!extractPower(requiredPower)) { + return; + } + + performTicks(blockEntity, ticker, speed); + } + + /** + * 检查网络节点是否有效。 + * @return 是否可以执行 tick + */ + private boolean isValidForTicking() { + return getGridNode() != null && getMainNode() != null && getMainNode().getGrid() != null; + } + + /** + * 获取目标方块实体的 ticker。 + * @param blockEntity 目标方块实体 + * @return ticker 或 null + */ + private BlockEntityTicker getTicker(T blockEntity) { + return getLevel().getBlockState(blockEntity.getBlockPos()) + .getTicker(getLevel(), (BlockEntityType) blockEntity.getType()); + } + + /** + * 计算加速倍率。 + * @return 生效的加速倍率 + */ + private int calculateSpeed() { int entitySpeedCardCount = getUpgrades().getInstalledUpgrades(ModItems.ENTITY_SPEED_CARD.get()); + if (entitySpeedCardCount <= 0) return 0; + return (int) PowerUtils.computeProductWithCap(getUpgrades(), 8); + } - // 使用工具方法从槽位直接计算乘积并应用 cap(最多 8 张卡) - long product = PowerUtils.computeProductWithCapFromStacks(this.getUpgrades(), 8); + /** + * 计算所需能量。 + * @param speed 加速倍率 + * @param blockId 目标方块ID + * @return 所需能量 + */ + private double calculateRequiredPower(int speed, String blockId) { + int energyCardCount = getUpgrades().getInstalledUpgrades(AEItems.ENERGY_CARD); + double multiplier = ConfigParsingUtils.getMultiplierForBlock(blockId, ModConfigs.ENTITY_TICKER_MULTIPLIERS.get()); + return PowerUtils.computeFinalPowerForProduct(speed, energyCardCount) * multiplier; + } - // 如果没有任何实体加速卡,则不进行加速且不消耗额外能量(只保留部件的被动功耗) - if (entitySpeedCardCount <= 0) return; + /** + * 提取网络能量并更新状态,优先从 AE2 网络提取 AE 能量,不足时从磁盘提取 FE 能量。 + * @param requiredPower 所需能量(AE 单位) + * @return 是否成功提取足够能量 + */ + private boolean extractPower(double requiredPower) { + IEnergyService energyService = getMainNode().getGrid().getEnergyService(); + MEStorage storage = getMainNode().getGrid().getStorageService().getInventory(); + IActionSource source = IActionSource.ofMachine(this); + boolean appFluxLoaded = ModList.get().isLoaded("appflux"); + boolean preferDiskEnergy = appFluxLoaded && ModConfigs.PRIORITIZE_DISK_ENERGY.get(); - // 计算本次 tick 所需能量:使用工具类根据 product 计算最终能耗 - double requiredPower = PowerUtils.computeFinalPowerForProduct(product, energyCardCount); - - int speed = (int) product; - - double multiplier = 1.0; - for (ConfigParsingUtils.MultiplierEntry me : ConfigParsingUtils.getCachedMultiplierEntries(ModConfigs.ENTITY_TICKER_MULTIPLIERS.get())) { - if (me.pattern.matcher(blockId).matches()) { - multiplier = Math.max(multiplier, me.multiplier); + // 如果 appflux 存在且优先磁盘能量,尝试提取 FE 能量 + if (appFluxLoaded && preferDiskEnergy) { + if (tryExtractFE(energyService, storage, requiredPower, source)) { + return true; } } - requiredPower *= multiplier; - -// 先模拟提取以检查网络中是否有足够能量,再真正抽取 - double simulated = getMainNode().getGrid().getEnergyService() - .extractAEPower(requiredPower, Actionable.SIMULATE, PowerMultiplier.CONFIG); - if (simulated < requiredPower) { - updateNetworkEnergySufficient(false); // 能量不足 - return; + // 尝试提取 AE 能量(当 appflux 不存在、优先 AE 能量或 FE 提取失败时) + double simulated = energyService.extractAEPower(requiredPower, Actionable.SIMULATE, PowerMultiplier.CONFIG); + if (simulated >= requiredPower) { + double extracted = energyService.extractAEPower(requiredPower, Actionable.MODULATE, PowerMultiplier.CONFIG); + boolean sufficient = extracted >= requiredPower; + updateNetworkEnergySufficient(sufficient); + return sufficient; } + updateNetworkEnergySufficient(false); - double extractedPower = getMainNode().getGrid().getEnergyService() - .extractAEPower(requiredPower, Actionable.MODULATE, PowerMultiplier.CONFIG); - if (extractedPower < requiredPower) { - updateNetworkEnergySufficient(false); // 能量不足 - return; + // 如果 appflux 存在且优先 AE 能量,尝试提取 FE 能量作为备用 + if (appFluxLoaded && !preferDiskEnergy) { + return tryExtractFE(energyService, storage, requiredPower, source); } - updateNetworkEnergySufficient(true); // 能量充足 + return false; + } - // 计算加速倍数:基于 2 的次方,并把 8 张映射到最大 1024x(2^10) - // 已由 product 计算得到 speed;上面已在没有卡时提前返回 + private boolean tryExtractFE(IEnergyService energyService, MEStorage storage, double requiredPower, IActionSource source) { + try { + Class helperClass = Class.forName("com.extendedae_plus.util.FluxEnergyHelper"); + Method extractMethod = helperClass.getMethod( + "extractFE", + IEnergyService.class, + MEStorage.class, + long.class, + IActionSource.class + ); + long feRequired = (long) requiredPower << 1; // 1 AE = 2 FE + long feExtracted = (long) extractMethod.invoke(null, energyService, storage, feRequired, source); + if (feExtracted >= feRequired) { + updateNetworkEnergySufficient(true); + return true; + } + } catch (Exception e) { + // 如果反射失败,视为 FE 不可用 + } + updateNetworkEnergySufficient(false); + return false; + } - // 执行 tick 操作 + /** + * 执行加速 tick 操作。 + * @param blockEntity 目标方块实体 + * @param ticker 方块实体 ticker + * @param speed 加速倍率 + */ + private void performTicks(T blockEntity, + BlockEntityTicker ticker, + int speed) { + // 执行 speed-1 次额外 tick(原生 tick 已包含 1 次) for (int i = 0; i < speed - 1; i++) { - blockEntityTicker.tick( + ticker.tick( blockEntity.getLevel(), blockEntity.getBlockPos(), blockEntity.getBlockState(), diff --git a/src/main/java/com/extendedae_plus/ae/screen/EntitySpeedTickerScreen.java b/src/main/java/com/extendedae_plus/ae/screen/EntitySpeedTickerScreen.java index 30605bb..b9a4352 100644 --- a/src/main/java/com/extendedae_plus/ae/screen/EntitySpeedTickerScreen.java +++ b/src/main/java/com/extendedae_plus/ae/screen/EntitySpeedTickerScreen.java @@ -10,28 +10,32 @@ import appeng.client.gui.widgets.SettingToggleButton; import appeng.util.Platform; import com.extendedae_plus.ae.menu.EntitySpeedTickerMenu; import com.extendedae_plus.network.ToggleEntityTickerC2SPacket; -import com.extendedae_plus.util.PowerUtils; +import com.extendedae_plus.util.entitySpeed.PowerUtils; import net.minecraft.client.Minecraft; import net.minecraft.network.chat.Component; import net.minecraft.world.entity.player.Inventory; +import java.util.HashMap; import java.util.List; +import java.util.Map; public class EntitySpeedTickerScreen extends UpgradeableScreen { - private boolean eap$entitySpeedTickerEnabled = false; - private SettingToggleButton eap$entitySpeedTickerToggle; + private boolean eap$entitySpeedTickerEnabled = false; // 本地缓存的加速开关状态 + private final SettingToggleButton eap$entitySpeedTickerToggle; // 加速开关按钮 - public EntitySpeedTickerScreen( - EntitySpeedTickerMenu menu, Inventory playerInventory, Component title, ScreenStyle style) { + /** + * 构造函数,初始化界面和控件。 + * @param menu 实体加速器菜单 + * @param playerInventory 玩家背包 + * @param title 界面标题 + * @param style 界面样式 + */ + public EntitySpeedTickerScreen(EntitySpeedTickerMenu menu, Inventory playerInventory, Component title, ScreenStyle style) { super((C) menu, playerInventory, title, style); - this.addToLeftToolbar(CommonButtons.togglePowerUnit()); + this.addToLeftToolbar(CommonButtons.togglePowerUnit()); // 添加功率单位切换按钮 + this.eap$entitySpeedTickerEnabled = menu.getAccelerateEnabled(); - try{ - this.eap$entitySpeedTickerEnabled = menu.getAccelerateEnabled(); - }catch (Exception ignored){} - - // 使用 SettingToggleButton 的外观(原版图标),但自定义悬停描述为“智能阻挡” - // 不做本地切换,点击仅发送自定义C2S,显示由@GuiSync回传 + // 初始化加速开关按钮 eap$entitySpeedTickerToggle = new SettingToggleButton<>( Settings.BLOCKING_MODE, this.eap$entitySpeedTickerEnabled ? YesNo.YES : YesNo.NO, @@ -43,65 +47,38 @@ public class EntitySpeedTickerScreen extends Up ) { @Override public List getTooltipMessage() { - // 如果目标在黑名单中,直接显示已禁用的提示 - try { - if (menu != null && menu.targetBlacklisted) { - var title = Component.literal("实体加速"); - var stateLine = Component.literal("已禁用(目标在黑名单)"); - return List.of(title, stateLine); - } - } catch (Exception ignored) {} - + if (menu.targetBlacklisted) { + return List.of( + Component.literal("实体加速"), + Component.literal("已禁用(目标在黑名单)") + ); + } boolean enabled = eap$entitySpeedTickerEnabled; - var title = Component.literal("实体加速"); - var stateLine = enabled - ? Component.literal("已启用: 将加速目标方块实体的tick") - : Component.literal("已关闭: 不会对目标方块实体进行加速"); - return List.of(title, stateLine); + return List.of( + Component.literal("实体加速"), + enabled ? Component.literal("已启用: 将加速目标方块实体的tick") : + Component.literal("已关闭: 不会对目标方块实体进行加速") + ); } @Override protected Icon getIcon() { - try { - if (menu != null && menu.targetBlacklisted) { - // 黑名单时显示禁用图标 - return Icon.INVALID; - } - } catch (Exception ignored) {} - - // 根据当前值显示不同图标(可按需替换 Icon 常量) - if (this.getCurrentValue() == YesNo.YES) { - return Icon.VALID; - } else { - return Icon.INVALID; - } + if (menu.targetBlacklisted) return Icon.INVALID; + return this.getCurrentValue() == YesNo.YES ? Icon.VALID : Icon.INVALID; } }; - // 初始化后立刻对齐当前@GuiSync状态,避免首帧显示不一致 eap$entitySpeedTickerToggle.set(this.eap$entitySpeedTickerEnabled ? YesNo.YES : YesNo.NO); - this.addToLeftToolbar(eap$entitySpeedTickerToggle); } @Override protected void updateBeforeRender() { super.updateBeforeRender(); - - if (this.eap$entitySpeedTickerToggle != null) { - boolean desired = this.eap$entitySpeedTickerEnabled; - if (this.menu != null) { - desired = this.menu.getAccelerateEnabled(); - } - - this.eap$entitySpeedTickerEnabled = desired; - // 如果目标在黑名单中,禁用切换并强制显示为关闭 - if (this.menu != null && this.menu.targetBlacklisted) { - this.eap$entitySpeedTickerToggle.set(YesNo.NO); - this.eap$entitySpeedTickerToggle.active = false; - } else { - this.eap$entitySpeedTickerToggle.set(desired ? YesNo.YES : YesNo.NO); - this.eap$entitySpeedTickerToggle.active = true; - } + if (eap$entitySpeedTickerToggle != null && menu != null) { + eap$entitySpeedTickerEnabled = menu.getAccelerateEnabled(); + // 如果目标在黑名单,禁用按钮并显示关闭状态 + eap$entitySpeedTickerToggle.set(menu.targetBlacklisted ? YesNo.NO : (eap$entitySpeedTickerEnabled ? YesNo.YES : YesNo.NO)); + eap$entitySpeedTickerToggle.active = !menu.targetBlacklisted; } textData(); } @@ -110,34 +87,33 @@ public class EntitySpeedTickerScreen extends Up textData(); } + /** + * 更新界面文本内容,包括加速状态、速度、能耗和倍率。 + */ private void textData() { - // 如果目标被黑名单禁止,则显示禁用状态并把数值显示为 0 + Map textContents = new HashMap<>(); if (getMenu().targetBlacklisted) { - setTextContent("enable", Component.translatable("screen.extendedae_plus.entity_speed_ticker.enable")); - setTextContent("speed", Component.translatable("screen.extendedae_plus.entity_speed_ticker.speed", 0)); - setTextContent("energy", Component.translatable("screen.extendedae_plus.entity_speed_ticker.energy", Platform.formatPower(0.0, false))); - setTextContent("power_ratio", Component.translatable("screen.extendedae_plus.entity_speed_ticker.power_ratio", PowerUtils.formatPercentage(0.0))); - setTextContent("multiplier", Component.translatable("screen.extendedae_plus.entity_speed_ticker.multiplier", String.format("%.2fx", 0.0))); - return; + // 黑名单禁用时的默认显示 + textContents.put("enable", Component.translatable("screen.extendedae_plus.entity_speed_ticker.enable")); + textContents.put("speed", Component.translatable("screen.extendedae_plus.entity_speed_ticker.speed", 0)); + textContents.put("energy", Component.translatable("screen.extendedae_plus.entity_speed_ticker.energy", Platform.formatPower(0.0, false))); + textContents.put("power_ratio", Component.translatable("screen.extendedae_plus.entity_speed_ticker.power_ratio", PowerUtils.formatPercentage(0.0))); + textContents.put("multiplier", Component.translatable("screen.extendedae_plus.entity_speed_ticker.multiplier", String.format("%.2fx", 0.0))); + } else { + // 正常状态下显示实际数据 + int energyCardCount = getMenu().energyCardCount; + double multiplier = getMenu().multiplier; + int effectiveSpeed = getMenu().effectiveSpeed; + double finalPower = PowerUtils.computeFinalPowerForProduct(effectiveSpeed, energyCardCount); + double remainingRatio = PowerUtils.getRemainingRatio(energyCardCount); + + textContents.put("enable", getMenu().networkEnergySufficient ? null : + Component.translatable("screen.extendedae_plus.entity_speed_ticker.warning_network_energy_insufficient")); + textContents.put("speed", Component.translatable("screen.extendedae_plus.entity_speed_ticker.speed", effectiveSpeed)); + textContents.put("energy", Component.translatable("screen.extendedae_plus.entity_speed_ticker.energy", Platform.formatPower(finalPower, false))); + textContents.put("power_ratio", Component.translatable("screen.extendedae_plus.entity_speed_ticker.power_ratio", PowerUtils.formatPercentage(remainingRatio))); + textContents.put("multiplier", Component.translatable("screen.extendedae_plus.entity_speed_ticker.multiplier", String.format("%.2fx", multiplier))); } - - 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().networkEnergySufficient) { - setTextContent("enable", Component.translatable("screen.extendedae_plus.entity_speed_ticker.warning_network_energy_insufficient")); - }else { - setTextContent("enable", null); - } - - 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))); - setTextContent("multiplier", Component.translatable("screen.extendedae_plus.entity_speed_ticker.multiplier", String.format("%.2fx", multiplier))); + textContents.forEach(this::setTextContent); } } \ No newline at end of file diff --git a/src/main/java/com/extendedae_plus/config/ModConfigs.java b/src/main/java/com/extendedae_plus/config/ModConfigs.java index bd6665e..9681b90 100644 --- a/src/main/java/com/extendedae_plus/config/ModConfigs.java +++ b/src/main/java/com/extendedae_plus/config/ModConfigs.java @@ -13,6 +13,7 @@ public final class ModConfigs { public static final ModConfigSpec CLIENT_SPEC; public static final ModConfigSpec.BooleanValue SHOW_ENCODER_PATTERN_PLAYER; public static final ModConfigSpec.BooleanValue PATTERN_TERMINAL_SHOW_SLOTS_DEFAULT; + public static final ModConfigSpec.BooleanValue PRIORITIZE_DISK_ENERGY; // Server 配置 public static final ModConfigSpec SERVER_SPEC; @@ -124,6 +125,12 @@ public final class ModConfigs { () -> "", obj -> obj instanceof String ); + PRIORITIZE_DISK_ENERGY = serverBuilder + .comment( + "是否优先从磁盘提取FE能量(仅当Applied Flux模组存在时生效)", + "开启后,将优先尝试从磁盘提取FE能量;反之优先消耗AE网络中的能量" + ) + .define("prioritizeDiskEnergy", true); serverBuilder.pop(); SERVER_SPEC = serverBuilder.build(); } diff --git a/src/main/java/com/extendedae_plus/util/FluxEnergyHelper.java b/src/main/java/com/extendedae_plus/util/FluxEnergyHelper.java new file mode 100644 index 0000000..6e5a57a --- /dev/null +++ b/src/main/java/com/extendedae_plus/util/FluxEnergyHelper.java @@ -0,0 +1,41 @@ +package com.extendedae_plus.util; + +import appeng.api.config.Actionable; +import appeng.api.networking.energy.IEnergyService; +import appeng.api.networking.security.IActionSource; +import appeng.api.storage.MEStorage; +import appeng.api.storage.StorageHelper; +import com.glodblock.github.appflux.common.me.key.FluxKey; +import com.glodblock.github.appflux.common.me.key.type.EnergyType; + +public class FluxEnergyHelper { + /** + * 尝试从 ME 存储提取 FE 能量。 + * @param energyService AE2 能量服务 + * @param storage ME 存储 + * @param feRequired 所需 FE 量 + * @param source 操作来源 + * @return 提取的 FE 量 + */ + public static long extractFE( + IEnergyService energyService, + MEStorage storage, + long feRequired, + IActionSource source + ) { + FluxKey feKey = FluxKey.of(EnergyType.FE); + + // 模拟提取 FE + long feExtracted = StorageHelper.poweredExtraction( + energyService, storage, feKey, feRequired, source, Actionable.SIMULATE + ); + + // 执行实际提取 + if (feExtracted >= feRequired) { + return StorageHelper.poweredExtraction( + energyService, storage, feKey, feRequired, source, Actionable.MODULATE + ); + } + return 0; + } +} \ No newline at end of file diff --git a/src/main/java/com/extendedae_plus/util/PowerUtils.java b/src/main/java/com/extendedae_plus/util/PowerUtils.java deleted file mode 100644 index 4da3f2c..0000000 --- a/src/main/java/com/extendedae_plus/util/PowerUtils.java +++ /dev/null @@ -1,162 +0,0 @@ -package com.extendedae_plus.util; - -import com.extendedae_plus.ae.definitions.upgrades.EntitySpeedCardItem; -import com.extendedae_plus.config.ModConfigs; - - -/** - * 用于计算实体加速器的能耗与加速倍率的工具类 - */ -public final class PowerUtils { - private PowerUtils() {} - // ---- 重构后的 API ---- - /** - * 将 card multipliers(按插槽序)计算乘积并应用 cap 规则(见 capForHighestMultiplier) - * @param multipliers iterable of per-card multipliers - * @param maxCards 最多计入的卡数 - * @return 被 cap 约束后的乘积 - */ - public static long computeProductWithCap(Iterable multipliers, int maxCards) { - long product = 1L; - int considered = 0; - int highest = 1; - for (Integer m : multipliers) { - if (m == null) continue; - if (considered >= maxCards) break; - int mult = m.intValue(); - if (mult <= 0) mult = 1; - product *= mult; - highest = Math.max(highest, mult); - considered++; - } - long cap = capForHighestMultiplier(highest); - return Math.min(product, cap); - } - - /** - * 根据最高单卡 multiplier 返回 cap 值 - */ - public static long capForHighestMultiplier(int highestMultiplier) { - if (highestMultiplier >= 16) return 1024L; - if (highestMultiplier >= 8) return 256L; - if (highestMultiplier >= 4) return 64L; - if (highestMultiplier >= 2) return 8L; - return 1L; - } - - /** - * 从菜单对象读取前 maxCards 个加速卡的 multiplier 并计算 product with cap - */ - public static long computeProductWithCapFromMenu(appeng.menu.implementations.UpgradeableMenu menu, int maxCards) { - java.util.List list = new java.util.ArrayList<>(); - int considered = 0; - for (var stack : menu.getUpgrades()) { - if (considered >= maxCards) break; - if (stack != null && !stack.isEmpty() && stack.getItem() instanceof EntitySpeedCardItem) { - int multVal = EntitySpeedCardItem.readMultiplier(stack); - int count = Math.min(stack.getCount(), maxCards - considered); - for (int i = 0; i < count; i++) { - list.add(multVal); - considered++; - if (considered >= maxCards) break; - } - } - } - return computeProductWithCap(list, maxCards); - } - - /** - * 从一组 ItemStack(升级槽)直接计算 product with cap(最多 maxCards) - */ - public static long computeProductWithCapFromStacks(Iterable stacks, int maxCards) { - java.util.List list = new java.util.ArrayList<>(); - int considered = 0; - for (var stack : stacks) { - if (considered >= maxCards) break; - if (stack != null && !stack.isEmpty() && stack.getItem() instanceof EntitySpeedCardItem) { - int multVal = EntitySpeedCardItem.readMultiplier(stack); - int count = Math.min(stack.getCount(), maxCards - considered); - for (int i = 0; i < count; i++) { - list.add(multVal); - considered++; - if (considered >= maxCards) break; - } - } - } - return computeProductWithCap(list, maxCards); - } - - /** - * 计算最终消耗:把 product 转换为等效卡数(log2)并调用 getFinalPower - */ - public static double computeFinalPowerForProduct(long product, int energyCardCount) { - if (product <= 1L) return 0.0; - double base = ModConfigs.ENTITY_TICKER_COST.getAsInt(); - - // 计算以2为底的对数(用于分档与公式) - double log2 = Math.log(product) / Math.log(2.0); - - // 分档:product==2 为一档;4..256 为中档;512..1024 为高档 - double raw; - if (product == 2L) { - // 轻量档:线性小幅增长 - raw = base * 4; - } else if (product <= 256L) { - // 中档:增长放缓(使用 1.5 * log2) - raw = base * Math.pow(2.0, 1.5 * log2) * 2; - } else { - // 高档:增长较快(使用 2.5 * log2) - raw = base * Math.pow(2.0, 2.5 * log2); - } - - double reduction = getReductionPercent(energyCardCount); - return raw * (1.0 - reduction) / 8.0; - } - - /* ----------------- legacy helpers (restored) ----------------- */ - public static double getGrowthFactor(int speedCardCount) { - if (speedCardCount <= 0) return 1.0; - if (speedCardCount == 1) return 2.0; - if (speedCardCount <= 6) return Math.pow(2.0, 2.0 * speedCardCount); - return Math.pow(2.0, 3.0 * speedCardCount); - } - - public static double getRawPower(int speedCardCount) { - double base = ModConfigs.ENTITY_TICKER_COST.getAsInt(); - return base * getGrowthFactor(speedCardCount); - } - - public static double getReductionPercent(int energyCardCount) { - if (energyCardCount <= 0) return 0.0; - if (energyCardCount == 1) return 0.1; - if (energyCardCount >= 8) return 0.5; - return 0.5 * (1.0 - Math.pow(0.7, energyCardCount)); - } - - public static double getFinalPower(int speedCardCount, int energyCardCount) { - double raw = getRawPower(speedCardCount); - double reduction = getReductionPercent(energyCardCount); - return raw * (1.0 - reduction) / 4.0; - } - - /** - * 返回能源卡减免后剩余的功耗比率(例如 1 张能源卡 -> 0.9) - * @param energyCardCount 能源卡数量 - * @return 剩余功耗比率 - */ - public static double getRemainingRatio(int energyCardCount) { - return 1.0 - getReductionPercent(energyCardCount); - } - - /** - * 将剩余功耗比率格式化为百分比字符串(例如 0.9 -> "90%") - */ - public static String formatPercentage(double ratio) { - double pct = ratio * 100.0; - // 如果为整数则无小数 - if (Math.abs(pct - Math.round(pct)) < 1e-9) { - return String.format("%d%%", Math.round(pct)); - } - return String.format("%.2f%%", pct); - } -} diff --git a/src/main/java/com/extendedae_plus/util/ConfigParsingUtils.java b/src/main/java/com/extendedae_plus/util/entitySpeed/ConfigParsingUtils.java similarity index 68% rename from src/main/java/com/extendedae_plus/util/ConfigParsingUtils.java rename to src/main/java/com/extendedae_plus/util/entitySpeed/ConfigParsingUtils.java index 0cf6ddc..6fd3b68 100644 --- a/src/main/java/com/extendedae_plus/util/ConfigParsingUtils.java +++ b/src/main/java/com/extendedae_plus/util/entitySpeed/ConfigParsingUtils.java @@ -1,10 +1,13 @@ -package com.extendedae_plus.util; +package com.extendedae_plus.util.entitySpeed; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.regex.Pattern; import java.util.regex.PatternSyntaxException; + +import static com.extendedae_plus.util.ExtendedAELogger.LOGGER; + /** * 配置解析工具类:用于解析黑名单与倍率配置的字符串 */ @@ -23,35 +26,32 @@ public final class ConfigParsingUtils { /** * 编译用户提供的匹配串。支持简单的 glob 语法('*' 和 '?')以及完整的正则表达式。 - * 规则: - * - 如果字符串包含 ".*" 或其他正则元字符,优先尝试按正则编译(若失败则回退到 glob 转换)。 - * - 否则若包含 '*' 或 '?',将按 glob 语法转换为正则。 - * - 否则按字面量匹配处理。 */ public static Pattern compilePattern(String raw) { - if (raw == null) throw new IllegalArgumentException("pattern is null"); + if (raw == null || raw.trim().isEmpty()) { + LOGGER.warn("Invalid pattern: {}", raw); + throw new IllegalArgumentException("Pattern is null or empty"); + } raw = raw.trim(); - if (raw.isEmpty()) throw new IllegalArgumentException("pattern is empty"); - // If it looks like regex (contains '.*' or regex metachar), try regex first + // Try regex first if it contains regex metacharacters if (raw.contains(".*") || raw.matches(".*[\\[\\(\\+\\{\\\\].*")) { try { return Pattern.compile("^" + raw + "$"); - } catch (PatternSyntaxException ignored) { - // fallback to glob below + } catch (PatternSyntaxException e) { + LOGGER.warn("Failed to compile regex pattern '{}': {}", raw, e.getMessage()); + // Fallback to glob } } - // If contains glob chars, convert to regex + // Convert glob to regex if (raw.contains("*") || raw.contains("?")) { - StringBuilder sb = new StringBuilder(); - sb.append('^'); + StringBuilder sb = new StringBuilder("^"); for (char c : raw.toCharArray()) { switch (c) { case '*': sb.append(".*"); break; case '?': sb.append('.'); break; default: - // escape regex special chars if (".\\+[]{}()^$|".indexOf(c) >= 0) { sb.append('\\'); } @@ -62,41 +62,71 @@ public final class ConfigParsingUtils { return Pattern.compile(sb.toString()); } - // Otherwise treat as a literal (match exact) + // Literal match return Pattern.compile("^" + Pattern.quote(raw) + "$"); } /** - * Parse multiplier entries like 'modid:block 2x' into MultiplierEntry objects. - * Accepts values with optional trailing 'x' (case-insensitive). + * 解析倍率条目,如 'modid:block 2x'。 */ public static MultiplierEntry parseMultiplierEntry(String entry) { - if (entry == null) return null; + if (entry == null || entry.trim().isEmpty()) return null; String[] parts = entry.trim().split("\\s+"); - if (parts.length < 2) return null; + if (parts.length < 2) { + LOGGER.warn("Invalid multiplier entry: {}", entry); + return null; + } String key = parts[0]; String val = parts[1].toLowerCase(); if (val.endsWith("x")) val = val.substring(0, val.length() - 1); - double m; + double multiplier; try { - m = Double.parseDouble(val); - } catch (NumberFormatException ex) { + multiplier = Double.parseDouble(val); + } catch (NumberFormatException e) { + LOGGER.warn("Invalid multiplier value in '{}': {}", entry, val); return null; } try { - Pattern p = compilePattern(key); - return new MultiplierEntry(p, m); + Pattern pattern = compilePattern(key); + return new MultiplierEntry(pattern, multiplier); } catch (IllegalArgumentException e) { + LOGGER.warn("Failed to compile pattern in '{}': {}", entry, e.getMessage()); return null; } } + /** + * 检查方块是否在黑名单中。 + */ + public static boolean isBlockBlacklisted(String blockId, List blacklist) { + if (blockId == null) return false; + return getCachedBlacklist(blacklist).stream().anyMatch(p -> p.matcher(blockId).matches()); + } + + /** + * 获取方块的倍率。 + */ + public static double getMultiplierForBlock(String blockId, List multipliers) { + if (blockId == null) return 1.0; + double maxMultiplier = 1.0; + for (MultiplierEntry me : getCachedMultiplierEntries(multipliers)) { + if (me.pattern.matcher(blockId).matches()) { + maxMultiplier = Math.max(maxMultiplier, me.multiplier); + } + } + return maxMultiplier; + } + public static List compilePatterns(List raw) { List out = new ArrayList<>(); if (raw == null) return out; for (String s : raw) { - if (s == null || s.isBlank()) continue; - try { out.add(compilePattern(s)); } catch (IllegalArgumentException ignored) {} + if (s == null || s.trim().isEmpty()) continue; + try { + out.add(compilePattern(s)); + } catch (IllegalArgumentException e) { + LOGGER.warn("Failed to compile pattern '{}': {}", s, e.getMessage()); + } } return out; } @@ -121,14 +151,11 @@ public final class ConfigParsingUtils { /** * 获取已解析并缓存的黑名单(线程安全、懒加载)。 */ - public static List getCachedBlacklist(java.util.List source) { + public static List getCachedBlacklist(List 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); @@ -141,13 +168,11 @@ public final class ConfigParsingUtils { /** * 获取已解析并缓存的倍率列表(线程安全、懒加载)。 */ - public static List getCachedMultiplierEntries(java.util.List source) { + public static List getCachedMultiplierEntries(List source) { List normalized = normalizeSource(source); - if (cachedMultiplierEntries != null && listEquals(cachedMultiplierSourceSnapshot, normalized)) { return Collections.unmodifiableList(cachedMultiplierEntries); } - synchronized (CACHE_LOCK) { if (cachedMultiplierEntries == null || !listEquals(cachedMultiplierSourceSnapshot, normalized)) { cachedMultiplierEntries = parseMultiplierList(normalized); @@ -158,7 +183,7 @@ public final class ConfigParsingUtils { } // Normalize the incoming source list: trim entries, drop blanks, keep stable ordering - private static List normalizeSource(java.util.List source) { + private static List normalizeSource(List source) { List out = new ArrayList<>(); if (source == null) return out; for (String s : source) { @@ -188,8 +213,8 @@ public final class ConfigParsingUtils { synchronized (CACHE_LOCK) { cachedBlacklist = null; cachedMultiplierEntries = null; + cachedBlacklistSourceSnapshot = null; + cachedMultiplierSourceSnapshot = null; } } -} - - +} \ No newline at end of file diff --git a/src/main/java/com/extendedae_plus/util/entitySpeed/PowerUtils.java b/src/main/java/com/extendedae_plus/util/entitySpeed/PowerUtils.java new file mode 100644 index 0000000..f095dc6 --- /dev/null +++ b/src/main/java/com/extendedae_plus/util/entitySpeed/PowerUtils.java @@ -0,0 +1,98 @@ +package com.extendedae_plus.util.entitySpeed; + +import appeng.api.upgrades.IUpgradeInventory; +import com.extendedae_plus.ae.definitions.upgrades.EntitySpeedCardItem; +import com.extendedae_plus.config.ModConfigs; +import net.minecraft.world.item.ItemStack; + +import java.util.ArrayList; +import java.util.List; + +/** + * 用于计算实体加速器的能耗与加速倍率的工具类 + */ +public final class PowerUtils { + private PowerUtils() {} + + /** + * 计算加速卡的乘积并应用上限。 + * @param upgrades 升级槽位 + * @param maxCards 最大计入的卡数 + * @return 被上限约束后的乘积 + */ + public static long computeProductWithCap(IUpgradeInventory upgrades, int maxCards) { + List multipliers = new ArrayList<>(); + int considered = 0; + for (ItemStack stack : upgrades) { + if (considered >= maxCards) break; + if (stack != null && !stack.isEmpty() && stack.getItem() instanceof EntitySpeedCardItem) { + int multVal = EntitySpeedCardItem.readMultiplier(stack); + int count = Math.min(stack.getCount(), maxCards - considered); + for (int i = 0; i < count; i++) { + multipliers.add(multVal); + considered++; + } + } + } + long product = 1L; + int highest = 1; + for (Integer m : multipliers) { + if (m == null || m <= 0) continue; + product *= m; + highest = Math.max(highest, m); + } + return Math.min(product, capForHighestMultiplier(highest)); + } + + /** + * 根据最高单卡倍率返回上限值。 + */ + public static long capForHighestMultiplier(int highestMultiplier) { + if (highestMultiplier >= 16) return 1024L; + if (highestMultiplier >= 8) return 256L; + if (highestMultiplier >= 4) return 64L; + if (highestMultiplier >= 2) return 8L; + return 1L; + } + + /** + * 计算最终能耗。 + * @param product 加速卡乘积 + * @param energyCardCount 能量卡数量 + * @return 最终能耗值 + */ + public static double computeFinalPowerForProduct(long product, int energyCardCount) { + if (product <= 1L) return 0.0; + double base = ModConfigs.ENTITY_TICKER_COST.get(); + double log2 = Math.log(product) / Math.log(2.0); + double raw; + if (product == 2L) { + raw = base * 4; + } else if (product <= 256L) { + raw = base * Math.pow(2.0, 1.5 * log2) * 2; + } else { + raw = base * Math.pow(2.0, 2.5 * log2); + } + return raw * getRemainingRatio(energyCardCount) / 8.0; + } + + /** + * 计算能量卡减免后的剩余功耗比率。 + */ + public static double getRemainingRatio(int energyCardCount) { + if (energyCardCount <= 0) return 1.0; + if (energyCardCount == 1) return 0.9; + if (energyCardCount >= 8) return 0.5; + return 1.0 - 0.5 * (1.0 - Math.pow(0.7, energyCardCount)); + } + + /** + * 将剩余功耗比率格式化为百分比字符串。 + */ + public static String formatPercentage(double ratio) { + double pct = ratio * 100.0; + return Math.abs(pct - Math.round(pct)) < 1e-9 ? + String.format("%d%%", Math.round(pct)) : + String.format("%.2f%%", pct); + } +} \ No newline at end of file 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 c8d3754..43a24bf 100644 --- a/src/main/resources/assets/extendedae_plus/lang/en_us.json +++ b/src/main/resources/assets/extendedae_plus/lang/en_us.json @@ -94,6 +94,7 @@ "extendedae_plus.configuration.entityTickerCost": "Entity Ticker Base Energy Cost", "extendedae_plus.configuration.entityTickerBlackList": "Entity Ticker Blacklist", "extendedae_plus.configuration.entityTickerMultipliers": "Entity Ticker Extra Cost Multipliers", + "extendedae_plus.configuration.prioritizeDiskEnergy": "Prioritize FE energy from disk (requires Applied Flux)", "extendedae_plus.configuration.state_on": "On", "extendedae_plus.configuration.state_off": "Off" 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 3df2054..195ba86 100644 --- a/src/main/resources/assets/extendedae_plus/lang/zh_cn.json +++ b/src/main/resources/assets/extendedae_plus/lang/zh_cn.json @@ -94,6 +94,7 @@ "extendedae_plus.configuration.entityTickerCost": "实体加速器能量消耗基础值", "extendedae_plus.configuration.entityTickerBlackList": "实体加速器黑名单", "extendedae_plus.configuration.entityTickerMultipliers": "实体加速器额外消耗倍率", + "extendedae_plus.configuration.prioritizeDiskEnergy": "优先从磁盘提取FE能量(仅当Applied Flux模组存在时生效)", "extendedae_plus.configuration.state_on": "开", "extendedae_plus.configuration.state_off": "关" From f222410f9544a5c23e6ab61faa315fc413f3b770 Mon Sep 17 00:00:00 2001 From: GaLicn <133291877+GaLicn@users.noreply.github.com> Date: Sat, 27 Sep 2025 23:54:03 +0800 Subject: [PATCH 42/43] =?UTF-8?q?jei=E8=BF=90=E8=A1=8C=E6=97=B6=E6=A3=80?= =?UTF-8?q?=E6=9F=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 3 +- .../modMetadata/META-INF/neoforge.mods.toml | 2 +- gradle.properties | 2 +- .../extendedae_plus/ExtendedAEPlusClient.java | 36 +- .../extendedae_plus/client/InputEvents.java | 234 +++++---- .../integration/jei/JeiClientBootstrap.java | 10 + .../integration/jei/JeiRuntimeProxy.java | 461 +++++++++--------- .../mixin/ExtendedAEPlusMixinPlugin.java | 48 ++ .../mixin/jei/EncodingHelperMixin.java | 4 +- .../resources/extendedae_plus.mixins.json | 2 +- 10 files changed, 451 insertions(+), 351 deletions(-) create mode 100644 src/main/java/com/extendedae_plus/integration/jei/JeiClientBootstrap.java create mode 100644 src/main/java/com/extendedae_plus/mixin/ExtendedAEPlusMixinPlugin.java diff --git a/.gitignore b/.gitignore index 9b070cd..7583cea 100644 --- a/.gitignore +++ b/.gitignore @@ -25,4 +25,5 @@ run-data repo -othermods \ No newline at end of file +othermods +crashreport \ No newline at end of file diff --git a/build/generated/sources/modMetadata/META-INF/neoforge.mods.toml b/build/generated/sources/modMetadata/META-INF/neoforge.mods.toml index 1ad0ea3..d8c7372 100644 --- a/build/generated/sources/modMetadata/META-INF/neoforge.mods.toml +++ b/build/generated/sources/modMetadata/META-INF/neoforge.mods.toml @@ -24,7 +24,7 @@ license="All Rights Reserved" modId="extendedae_plus" #mandatory # The version number of the mod -version="1.21.1-1.4.2" #mandatory +version="1.21.1-1.4.4" #mandatory # A display name for the mod displayName="ExtendedAE-Plus" #mandatory diff --git a/gradle.properties b/gradle.properties index ebafd76..7c01284 100644 --- a/gradle.properties +++ b/gradle.properties @@ -32,7 +32,7 @@ mod_name=ExtendedAE-Plus # The license of the mod. Review your options at https://choosealicense.com/. All Rights Reserved is the default. mod_license=All Rights Reserved # The mod version. See https://semver.org/ -mod_version=1.21.1-1.4.2 +mod_version=1.21.1-1.4.3 # The group ID for the mod. It is only important when publishing as an artifact to a Maven repository. # This should match the base package used for the mod sources. # See https://maven.apache.org/guides/mini/guide-naming-conventions.html diff --git a/src/main/java/com/extendedae_plus/ExtendedAEPlusClient.java b/src/main/java/com/extendedae_plus/ExtendedAEPlusClient.java index f324adb..beb68c3 100644 --- a/src/main/java/com/extendedae_plus/ExtendedAEPlusClient.java +++ b/src/main/java/com/extendedae_plus/ExtendedAEPlusClient.java @@ -9,23 +9,35 @@ import net.neoforged.fml.common.Mod; import net.neoforged.fml.event.lifecycle.FMLClientSetupEvent; import net.neoforged.neoforge.client.gui.ConfigurationScreen; import net.neoforged.neoforge.client.gui.IConfigScreenFactory; +import net.neoforged.fml.ModList; // This class will not load on dedicated servers. Accessing client side code from here is safe. @Mod(value = ExtendedAEPlus.MODID, dist = Dist.CLIENT) // You can use EventBusSubscriber to automatically register all static methods in the class annotated with @SubscribeEvent @EventBusSubscriber(modid = ExtendedAEPlus.MODID, value = Dist.CLIENT) public class ExtendedAEPlusClient { - public ExtendedAEPlusClient(ModContainer container) { - // Allows NeoForge to create a config screen for this mod's configs. - // The config screen is accessed by going to the Mods screen > clicking on your mod > clicking on config. - // Do not forget to add translations for your config options to the en_us.json file. - container.registerExtensionPoint(IConfigScreenFactory.class, ConfigurationScreen::new); - } + public ExtendedAEPlusClient(ModContainer container) { + // Allows NeoForge to create a config screen for this mod's configs. + // The config screen is accessed by going to the Mods screen > clicking on your mod > clicking on config. + // Do not forget to add translations for your config options to the en_us.json file. + container.registerExtensionPoint(IConfigScreenFactory.class, ConfigurationScreen::new); + } - @SubscribeEvent - static void onClientSetup(FMLClientSetupEvent event) { - // Some client setup code - ExtendedAEPlus.LOGGER.info("HELLO FROM CLIENT SETUP"); - ExtendedAEPlus.LOGGER.info("MINECRAFT NAME >> {}", Minecraft.getInstance().getUser().getName()); - } + @SubscribeEvent + static void onClientSetup(FMLClientSetupEvent event) { + // Some client setup code + ExtendedAEPlus.LOGGER.info("HELLO FROM CLIENT SETUP"); + ExtendedAEPlus.LOGGER.info("MINECRAFT NAME >> {}", Minecraft.getInstance().getUser().getName()); + + // Register JEI-dependent input handlers only when JEI is present + if (ModList.get().isLoaded("jei")) { + try { + Class bootstrap = Class.forName("com.extendedae_plus.integration.jei.JeiClientBootstrap"); + java.lang.reflect.Method m = bootstrap.getMethod("register"); + m.invoke(null); + } catch (Throwable t) { + ExtendedAEPlus.LOGGER.warn("Failed to register JEI client listeners: {}", t.toString()); + } + } + } } diff --git a/src/main/java/com/extendedae_plus/client/InputEvents.java b/src/main/java/com/extendedae_plus/client/InputEvents.java index 48c81e5..755e988 100644 --- a/src/main/java/com/extendedae_plus/client/InputEvents.java +++ b/src/main/java/com/extendedae_plus/client/InputEvents.java @@ -2,133 +2,163 @@ package com.extendedae_plus.client; import appeng.api.stacks.GenericStack; import appeng.client.gui.me.common.MEStorageScreen; -import com.extendedae_plus.ExtendedAEPlus; -import com.extendedae_plus.integration.jei.JeiRuntimeProxy; import com.extendedae_plus.mixin.ae2.accessor.MEStorageScreenAccessor; import com.extendedae_plus.mixin.extendedae.accessor.GuiExPatternTerminalAccessor; import com.extendedae_plus.network.OpenCraftFromJeiC2SPacket; import com.extendedae_plus.network.PullFromJeiOrCraftC2SPacket; import com.glodblock.github.extendedae.client.gui.GuiExPatternTerminal; -import mezz.jei.api.ingredients.ITypedIngredient; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.screens.Screen; import net.minecraft.world.item.ItemStack; -import net.neoforged.api.distmarker.Dist; import net.neoforged.bus.api.SubscribeEvent; -import net.neoforged.fml.common.EventBusSubscriber; import net.neoforged.neoforge.client.event.ScreenEvent; import net.neoforged.neoforge.network.PacketDistributor; import org.lwjgl.glfw.GLFW; +import java.lang.reflect.Method; import java.util.Optional; -@EventBusSubscriber(modid = ExtendedAEPlus.MODID, value = Dist.CLIENT) public final class InputEvents { - private InputEvents() {} + private InputEvents() {} - // 临时适配:在缺少 AE2 的 JEI 辅助类时,仅尝试从 JEI 提供的原生 ItemStack 获取;否则不处理。 - private static GenericStack toGenericStack(ITypedIngredient typed) { - try { - Optional maybe = typed.getItemStack(); - if (maybe.isPresent()) { - ItemStack is = maybe.get(); - // 尝试使用 AE2 的通用构造(若不可用则返回 null) - try { - return GenericStack.fromItemStack(is); - } catch (Throwable ignored) { - return null; - } - } - } catch (Throwable ignored) { - } - return null; - } + private static Optional getIngredientUnderMouse() { + try { + Class cls = Class.forName("com.extendedae_plus.integration.jei.JeiRuntimeProxy"); + Method m = cls.getMethod("getIngredientUnderMouse"); + Object r = m.invoke(null); + return (Optional) r; + } catch (Throwable ignored) { + return Optional.empty(); + } + } - @SubscribeEvent - public static void onMouseButtonPre(ScreenEvent.MouseButtonPressed.Pre event) { - // 优先处理: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; - } - ITypedIngredient typed = hovered.get(); - GenericStack stack = toGenericStack(typed); - if (stack != null) { - // 发送到服务端:若网络有库存则拉取一组到空槽,否则若可合成则打开下单界面 - PacketDistributor.sendToServer(new PullFromJeiOrCraftC2SPacket(stack)); - // 消费此次点击,避免 JEI/原版对左键的其它处理 - event.setCanceled(true); - return; - } - } - } + private static Optional getIngredientUnderMouse(double mouseX, double mouseY) { + try { + Class cls = Class.forName("com.extendedae_plus.integration.jei.JeiRuntimeProxy"); + Method m = cls.getMethod("getIngredientUnderMouse", double.class, double.class); + Object r = m.invoke(null, mouseX, mouseY); + return (Optional) r; + } catch (Throwable ignored) { + return Optional.empty(); + } + } - // 中键:打开 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(); - } - if (hovered.isEmpty()) return; + private static boolean isJeiCheatModeEnabled() { + try { + Class cls = Class.forName("com.extendedae_plus.integration.jei.JeiRuntimeProxy"); + Method m = cls.getMethod("isJeiCheatModeEnabled"); + Object r = m.invoke(null); + return r instanceof Boolean b && b; + } catch (Throwable ignored) { + return false; + } + } - ITypedIngredient typed = hovered.get(); - // 若 JEI 作弊模式开启,则放行给 JEI 处理(中键=一组) - if (JeiRuntimeProxy.isJeiCheatModeEnabled()) { - return; - } - GenericStack stack = toGenericStack(typed); - if (stack == null) return; + private static String getTypedIngredientDisplayName(Object typed) { + try { + Class cls = Class.forName("com.extendedae_plus.integration.jei.JeiRuntimeProxy"); + Method m = cls.getMethod("getTypedIngredientDisplayName", Object.class); + Object r = m.invoke(null, typed); + return r instanceof String s ? s : ""; + } catch (Throwable ignored) { + return ""; + } + } - // 发送到服务端,让其验证并打开 CraftAmountMenu - PacketDistributor.sendToServer(new OpenCraftFromJeiC2SPacket(stack)); + // 在缺少 AE2 的 JEI 辅助类时,仅尝试从 JEI 提供的原生 ItemStack 获取;否则不处理。 + private static GenericStack toGenericStack(Object typed) { + try { + // typed.getItemStack(): Optional + Method getItemStack = typed.getClass().getMethod("getItemStack"); + Object maybe = getItemStack.invoke(typed); + if (maybe instanceof Optional opt && opt.isPresent()) { + Object val = opt.get(); + if (val instanceof ItemStack is) { + try { + return GenericStack.fromItemStack(is); + } catch (Throwable ignored) { + return null; + } + } + } + } catch (Throwable ignored) { + } + return null; + } - // 消费此次点击,避免 JEI/原版对中键的其它处理 - event.setCanceled(true); - } - } + @SubscribeEvent + public static void onMouseButtonPre(ScreenEvent.MouseButtonPressed.Pre event) { + // 优先处理:Shift + 左键(拉取或下单) + if (event.getButton() == GLFW.GLFW_MOUSE_BUTTON_LEFT && Screen.hasShiftDown()) { + double mouseX = event.getMouseX(); + double mouseY = event.getMouseY(); + Optional hovered = getIngredientUnderMouse(mouseX, mouseY); + if (hovered.isEmpty()) { + hovered = getIngredientUnderMouse(); + } + if (hovered.isPresent()) { + if (isJeiCheatModeEnabled()) { + return; + } + Object typed = hovered.get(); + GenericStack stack = toGenericStack(typed); + if (stack != null) { + PacketDistributor.sendToServer(new PullFromJeiOrCraftC2SPacket(stack)); + event.setCanceled(true); + return; + } + } + } - @SubscribeEvent - public static void onKeyPressedPre(ScreenEvent.KeyPressed.Pre event) { - if (event.getKeyCode() != GLFW.GLFW_KEY_F) return; + // 中键:打开 AE 下单界面(保持原有功能) + if (event.getButton() == GLFW.GLFW_MOUSE_BUTTON_MIDDLE) { + double mouseX = event.getMouseX(); + double mouseY = event.getMouseY(); + Optional hovered = getIngredientUnderMouse(mouseX, mouseY); + if (hovered.isEmpty()) { + hovered = getIngredientUnderMouse(); + } + if (hovered.isEmpty()) return; - // 仅当鼠标确实悬停在 JEI 配料上时触发 - Optional> hovered = JeiRuntimeProxy.getIngredientUnderMouse(); - if (hovered.isEmpty()) return; + if (isJeiCheatModeEnabled()) { + return; + } + Object typed = hovered.get(); + GenericStack stack = toGenericStack(typed); + if (stack == null) return; - ITypedIngredient typed = hovered.get(); + PacketDistributor.sendToServer(new OpenCraftFromJeiC2SPacket(stack)); + event.setCanceled(true); + } + } - // 通用获取显示名称(兼容物品/流体等) - String name = JeiRuntimeProxy.getTypedIngredientDisplayName(typed); - if (name == null || name.isEmpty()) return; + @SubscribeEvent + public static void onKeyPressedPre(ScreenEvent.KeyPressed.Pre event) { + if (event.getKeyCode() != GLFW.GLFW_KEY_F) return; - // 写入 AE2 终端的搜索框 - var screen = Minecraft.getInstance().screen; - if (screen instanceof MEStorageScreen me) { - try { - MEStorageScreenAccessor acc = (MEStorageScreenAccessor) (Object) me; - acc.eap$getSearchField().setValue(name); - acc.eap$setSearchText(name); // 同步到 Repo 并刷新 - event.setCanceled(true); - return; - } catch (Throwable ignored) { - } - }else if (screen instanceof GuiExPatternTerminal gpt) { - try { - GuiExPatternTerminalAccessor acc = (GuiExPatternTerminalAccessor) gpt; - acc.getSearchField().setValue(name); - event.setCanceled(true); - }catch (Throwable ignored) {} - } - } + Optional hovered = getIngredientUnderMouse(); + if (hovered.isEmpty()) return; + + Object typed = hovered.get(); + String name = getTypedIngredientDisplayName(typed); + if (name == null || name.isEmpty()) return; + + var screen = Minecraft.getInstance().screen; + if (screen instanceof MEStorageScreen me) { + try { + MEStorageScreenAccessor acc = (MEStorageScreenAccessor) (Object) me; + acc.eap$getSearchField().setValue(name); + acc.eap$setSearchText(name); + event.setCanceled(true); + return; + } catch (Throwable ignored) { + } + }else if (screen instanceof GuiExPatternTerminal gpt) { + try { + GuiExPatternTerminalAccessor acc = (GuiExPatternTerminalAccessor) gpt; + acc.getSearchField().setValue(name); + event.setCanceled(true); + }catch (Throwable ignored) {} + } + } } diff --git a/src/main/java/com/extendedae_plus/integration/jei/JeiClientBootstrap.java b/src/main/java/com/extendedae_plus/integration/jei/JeiClientBootstrap.java new file mode 100644 index 0000000..3b8c7c4 --- /dev/null +++ b/src/main/java/com/extendedae_plus/integration/jei/JeiClientBootstrap.java @@ -0,0 +1,10 @@ +package com.extendedae_plus.integration.jei; + +public final class JeiClientBootstrap { + private JeiClientBootstrap() {} + + public static void register() { + net.neoforged.neoforge.common.NeoForge.EVENT_BUS.addListener(com.extendedae_plus.client.InputEvents::onMouseButtonPre); + net.neoforged.neoforge.common.NeoForge.EVENT_BUS.addListener(com.extendedae_plus.client.InputEvents::onKeyPressedPre); + } +} \ No newline at end of file diff --git a/src/main/java/com/extendedae_plus/integration/jei/JeiRuntimeProxy.java b/src/main/java/com/extendedae_plus/integration/jei/JeiRuntimeProxy.java index d5f2de3..c53d4b8 100644 --- a/src/main/java/com/extendedae_plus/integration/jei/JeiRuntimeProxy.java +++ b/src/main/java/com/extendedae_plus/integration/jei/JeiRuntimeProxy.java @@ -1,16 +1,5 @@ package com.extendedae_plus.integration.jei; -import com.extendedae_plus.mixin.jei.accessor.BookmarkOverlayAccessor; -import mezz.jei.api.constants.VanillaTypes; -import mezz.jei.api.ingredients.IIngredientType; -import mezz.jei.api.ingredients.ITypedIngredient; -import mezz.jei.api.neoforge.NeoForgeTypes; -import mezz.jei.api.runtime.IBookmarkOverlay; -import mezz.jei.api.runtime.IIngredientListOverlay; -import mezz.jei.api.runtime.IJeiRuntime; -import mezz.jei.gui.bookmarks.BookmarkList; -import mezz.jei.gui.bookmarks.IngredientBookmark; -import mezz.jei.gui.overlay.elements.IElement; import net.minecraft.world.item.ItemStack; import net.neoforged.fml.ModList; import net.neoforged.neoforge.fluids.FluidStack; @@ -23,238 +12,248 @@ import java.util.List; import java.util.Optional; /** - * 线程安全地缓存并访问 JEI Runtime。 + * 线程安全地缓存并访问 JEI Runtime(纯反射,避免在未安装 JEI 时触发类加载)。 */ public final class JeiRuntimeProxy { - private static volatile IJeiRuntime RUNTIME; + // 持有为 Object,避免类加载 + private static volatile Object RUNTIME; - private JeiRuntimeProxy() {} + private JeiRuntimeProxy() {} - static void setRuntime(IJeiRuntime runtime) { - RUNTIME = runtime; - } + static void setRuntime(Object runtime) { + RUNTIME = runtime; + } - @Nullable - public static IJeiRuntime get() { - return RUNTIME; - } + @Nullable + public static Object get() { + return RUNTIME; + } - public static Optional> getIngredientUnderMouse() { - IJeiRuntime rt = RUNTIME; - if (rt == null) return Optional.empty(); + public static Optional getIngredientUnderMouse() { + Object rt = RUNTIME; + if (rt == null) return Optional.empty(); + try { + Method getIngredientListOverlay = rt.getClass().getMethod("getIngredientListOverlay"); + Object list = getIngredientListOverlay.invoke(rt); + if (list != null) { + Method m = list.getClass().getMethod("getIngredientUnderMouse"); + Object opt = m.invoke(list); + if (opt instanceof Optional o && o.isPresent()) return o; + } + Method getBookmarkOverlay = rt.getClass().getMethod("getBookmarkOverlay"); + Object bm = getBookmarkOverlay.invoke(rt); + if (bm != null) { + Method m = bm.getClass().getMethod("getIngredientUnderMouse"); + Object opt = m.invoke(bm); + if (opt instanceof Optional o && o.isPresent()) return o; + } + } catch (Throwable ignored) {} + return Optional.empty(); + } - IIngredientListOverlay list = rt.getIngredientListOverlay(); - if (list != null) { - var ing = list.getIngredientUnderMouse(); - if (ing.isPresent()) return ing.map(i -> (ITypedIngredient) i); - } - IBookmarkOverlay bm = rt.getBookmarkOverlay(); - if (bm != null) { - var ing = bm.getIngredientUnderMouse(); - if (ing.isPresent()) return ing.map(i -> (ITypedIngredient) i); - } - return Optional.empty(); - } + public static Optional getIngredientUnderMouse(double mouseX, double mouseY) { + Object rt = RUNTIME; + if (rt == null) return Optional.empty(); + try { + Method getRecipesGui = rt.getClass().getMethod("getRecipesGui"); + Object gui = getRecipesGui.invoke(rt); + if (gui == null) return Optional.empty(); + Object ingredientManager = rt.getClass().getMethod("getIngredientManager").invoke(rt); + Class vanillaTypes = Class.forName("mezz.jei.api.constants.VanillaTypes"); + Object itemType = vanillaTypes.getField("ITEM_STACK").get(null); + Method getUnder = gui.getClass().getMethod("getIngredientUnderMouse", itemType.getClass()); + Object valueOpt = getUnder.invoke(gui, itemType); + if (!(valueOpt instanceof Optional value) || value.isEmpty()) return Optional.empty(); + Method createTyped = ingredientManager.getClass().getMethod("createTypedIngredient", itemType.getClass(), Object.class); + Object typedOpt = createTyped.invoke(ingredientManager, itemType, value.get()); + return typedOpt instanceof Optional o ? o : Optional.empty(); + } catch (Throwable ignored) {} + return Optional.empty(); + } - /** - * 在 JEI 配方界面区域内,基于屏幕坐标查询鼠标下的配料(优先物品,其次流体)。 - */ - public static Optional> getIngredientUnderMouse(double mouseX, double mouseY) { - IJeiRuntime rt = RUNTIME; - if (rt == null || rt.getRecipesGui() == null) return Optional.empty(); + public static boolean isJeiCheatModeEnabled() { + try { + Class internal = Class.forName("mezz.jei.common.Internal"); + Object toggle = internal.getMethod("getClientToggleState").invoke(null); + Method isCheat = toggle.getClass().getMethod("isCheatItemsEnabled"); + Object r = isCheat.invoke(toggle); + return r instanceof Boolean b && b; + } catch (Throwable t) { + return false; + } + } - var ingredientManager = rt.getIngredientManager(); + public static String getTypedIngredientDisplayName(Object typed) { + Object rt = RUNTIME; + if (rt == null || typed == null) return ""; + try { + Object manager = rt.getClass().getMethod("getIngredientManager").invoke(rt); + Method getType = typed.getClass().getMethod("getType"); + Object type = getType.invoke(typed); + Method getHelper = manager.getClass().getMethod("getIngredientHelper", type.getClass()); + Object helper = getHelper.invoke(manager, type); + Method getIngredient = typed.getClass().getMethod("getIngredient"); + Object ingredient = getIngredient.invoke(typed); + Object display = helper.getClass().getMethod("getDisplayName", ingredient.getClass()).invoke(helper, ingredient); + if (display == null) return ""; + try { + Class comp = Class.forName("net.minecraft.network.chat.Component"); + if (comp.isInstance(display)) { + Method getString = comp.getMethod("getString"); + Object s = getString.invoke(display); + return s == null ? "" : s.toString(); + } + } catch (Throwable ignored) {} + return display.toString(); + } catch (Throwable ignored) {} + return ""; + } - // 支持物品(通用且所有版本可用)。如需流体可后续按版本判断再扩展 - var item = rt.getRecipesGui().getIngredientUnderMouse(VanillaTypes.ITEM_STACK) - .flatMap(v -> ingredientManager.createTypedIngredient(VanillaTypes.ITEM_STACK, v)) - .map(x -> (ITypedIngredient) x); - if (item.isPresent()) return Optional.of(item.get()); + public static List getBookmarkList() { + Object rt = RUNTIME; + if (rt == null) return Collections.emptyList(); + try { + Object overlay = rt.getClass().getMethod("getBookmarkOverlay").invoke(rt); + if (overlay == null) return Collections.emptyList(); + try { + Field f = overlay.getClass().getDeclaredField("bookmarkList"); + f.setAccessible(true); + Object list = f.get(overlay); + Method getElements = list.getClass().getMethod("getElements"); + Object elements = getElements.invoke(list); + if (elements instanceof List l) { + // map(IElement::getTypedIngredient) + try { + Method getTyped = Class.forName("mezz.jei.gui.overlay.elements.IElement").getMethod("getTypedIngredient"); + return l.stream().map(e -> { + try { return getTyped.invoke(e); } catch (Throwable ignored) { return null; } + }).filter(x -> x != null).toList(); + } catch (Throwable ignored) {} + } + } catch (Throwable ignored) {} + } catch (Throwable ignored) {} + return Collections.emptyList(); + } - return Optional.empty(); - } + public static void addBookmark(ItemStack stack) { + Object rt = RUNTIME; + if (rt == null || stack == null || stack.isEmpty()) return; + try { + Object overlay = rt.getClass().getMethod("getBookmarkOverlay").invoke(rt); + if (overlay == null) return; + Field f = overlay.getClass().getDeclaredField("bookmarkList"); + f.setAccessible(true); + Object list = f.get(overlay); + Object manager = rt.getClass().getMethod("getIngredientManager").invoke(rt); + Object itemType = Class.forName("mezz.jei.api.constants.VanillaTypes").getField("ITEM_STACK").get(null); + Method createTyped = manager.getClass().getMethod("createTypedIngredient", itemType.getClass(), Object.class); + Object typedOpt = createTyped.invoke(manager, itemType, stack); + if (typedOpt instanceof Optional opt && opt.isPresent()) { + Object typed = opt.get(); + Class ibCls = Class.forName("mezz.jei.gui.bookmarks.IngredientBookmark"); + Method create = null; + for (Method m : ibCls.getMethods()) { + if (m.getName().equals("create") && m.getParameterCount() == 2) { + create = m; break; + } + } + if (create != null) { + Object bookmark = create.invoke(null, typed, manager); + list.getClass().getMethod("add", ibCls).invoke(list, bookmark); + } + } + } catch (Throwable ignored) {} + } - /** - * 检测 JEI 是否开启了作弊模式(给物品)。 - * 使用 JEI 内部开关,若 JEI 未初始化或异常则返回 false。 - */ - public static boolean isJeiCheatModeEnabled() { - try { - // 使用完全限定名以避免在源码缺失时的编译依赖问题 - return mezz.jei.common.Internal.getClientToggleState().isCheatItemsEnabled(); - } catch (Throwable t) { - return false; - } - } + public static void addBookmark(FluidStack fluidStack) { + Object rt = RUNTIME; + if (rt == null) return; + try { + Object overlay = rt.getClass().getMethod("getBookmarkOverlay").invoke(rt); + if (overlay == null) return; + Field f = overlay.getClass().getDeclaredField("bookmarkList"); + f.setAccessible(true); + Object list = f.get(overlay); + Object manager = rt.getClass().getMethod("getIngredientManager").invoke(rt); + Object fluidType = Class.forName("mezz.jei.api.neoforge.NeoForgeTypes").getField("FLUID_STACK").get(null); + Method createTyped = manager.getClass().getMethod("createTypedIngredient", fluidType.getClass(), Object.class); + Object typedOpt = createTyped.invoke(manager, fluidType, fluidStack); + if (typedOpt instanceof Optional opt && opt.isPresent()) { + Object typed = opt.get(); + Class ibCls = Class.forName("mezz.jei.gui.bookmarks.IngredientBookmark"); + Method create = null; + for (Method m : ibCls.getMethods()) { + if (m.getName().equals("create") && m.getParameterCount() == 2) { create = m; break; } + } + if (create != null) { + Object bookmark = create.invoke(null, typed, manager); + list.getClass().getMethod("add", ibCls).invoke(list, bookmark); + } + } + } catch (Throwable ignored) {} + } - /** - * 将文本写入 JEI 的搜索过滤框。 - * 若 JEI runtime 不可用则静默返回。 - */ - public static void setIngredientFilterText(String text) { - IJeiRuntime rt = RUNTIME; - if (rt == null) return; - try { - rt.getIngredientFilter().setFilterText(text == null ? "" : text); - } catch (Throwable ignored) { - // 兼容不同 JEI 版本或在启动阶段尚未就绪 - } - } + public static void addBookmark(Object chemicalStack) { + if (!ModList.get().isLoaded("mekanism") && !ModList.get().isLoaded("appmek")) return; + Object rt = RUNTIME; + if (rt == null || chemicalStack == null) return; + try { + Object overlay = rt.getClass().getMethod("getBookmarkOverlay").invoke(rt); + if (overlay == null) return; + Field f = overlay.getClass().getDeclaredField("bookmarkList"); + f.setAccessible(true); + Object list = f.get(overlay); + Object manager = rt.getClass().getMethod("getIngredientManager").invoke(rt); + String mekanismJeiClass = "mekanism.client.recipe_viewer.jei.MekanismJEI"; + Class jeiCls = Class.forName(mekanismJeiClass); + Field typeField = null; + if ("mekanism.api.chemical.ChemicalStack".equals(chemicalStack.getClass().getName())) { + typeField = jeiCls.getField("TYPE_CHEMICAL"); + } + if (typeField == null) return; + Object typeConst = typeField.get(null); + Method createTyped = manager.getClass().getMethod("createTypedIngredient", typeConst.getClass(), Object.class); + Object typedOpt = createTyped.invoke(manager, typeConst, chemicalStack); + if (typedOpt instanceof Optional opt && opt.isPresent()) { + Object typed = opt.get(); + Class ibCls = Class.forName("mezz.jei.gui.bookmarks.IngredientBookmark"); + Method create = null; + for (Method m : ibCls.getMethods()) { + if (m.getName().equals("create") && m.getParameterCount() == 2) { create = m; break; } + } + if (create != null) { + Object bookmark = create.invoke(null, typed, manager); + list.getClass().getMethod("add", ibCls).invoke(list, bookmark); + } + } + } catch (Throwable ignored) {} + } - /** - * 通用获取 JEI 悬浮配料的本地化显示名称(适配物品/流体等)。 - * 若无法安全获取,则返回空字符串。 - */ - public static String getTypedIngredientDisplayName(ITypedIngredient typed) { - IJeiRuntime rt = RUNTIME; - if (rt == null || typed == null) return ""; - try { - var manager = rt.getIngredientManager(); - var helper = manager.getIngredientHelper(typed.getType()); - // JEI 的 IIngredientHelper#getDisplayName 返回 Component(新版本)或 String(旧版本) - // 统一转为字符串,使用 toString() 兜底 - Object display = helper.getDisplayName(typed.getIngredient()); - if (display == null) return ""; - // 新版:net.minecraft.network.chat.Component - if (display instanceof net.minecraft.network.chat.Component comp) { - String s = comp.getString(); - return s == null ? "" : s; - } - String s = display.toString(); - return s == null ? "" : s; - } catch (Throwable ignored) { - } - return ""; - } - - /** - * 获取JEI书签列表 - */ - public static List> getBookmarkList() { - IJeiRuntime rt = RUNTIME; - if (rt == null) return Collections.emptyList(); - IBookmarkOverlay bookmarkOverlay = rt.getBookmarkOverlay(); - if (bookmarkOverlay instanceof BookmarkOverlayAccessor accessor) { - BookmarkList bookmarkList = accessor.eap$getBookmarkList(); - return bookmarkList.getElements().stream().map(IElement::getTypedIngredient).toList(); - } - return Collections.emptyList(); - } - - /** - * 将物品添加到 JEI 书签 - */ - public static void addBookmark(ItemStack stack) { - IJeiRuntime rt = RUNTIME; - if (rt == null || stack == null || stack.isEmpty()) return; - - IBookmarkOverlay overlay = rt.getBookmarkOverlay(); - if (overlay instanceof BookmarkOverlayAccessor accessor) { - BookmarkList list = accessor.eap$getBookmarkList(); - try { - var typedOpt = rt.getIngredientManager().createTypedIngredient(VanillaTypes.ITEM_STACK, stack); - typedOpt.ifPresent(typed -> { - IngredientBookmark bookmark = IngredientBookmark.create(typed, rt.getIngredientManager()); - list.add(bookmark); // add 内部会自动保存到配置 - }); - } catch (Throwable ignored) {} - } - } - - public static void addBookmark(FluidStack fluidStack) { - IJeiRuntime rt = RUNTIME; - if (rt == null) return; - - IBookmarkOverlay overlay = rt.getBookmarkOverlay(); - if (overlay instanceof BookmarkOverlayAccessor accessor) { - BookmarkList list = accessor.eap$getBookmarkList(); - Optional> typedOpt = rt.getIngredientManager() - .createTypedIngredient(NeoForgeTypes.FLUID_STACK, fluidStack); - typedOpt.ifPresent(typed -> { - IngredientBookmark bookmark = IngredientBookmark.create(typed, rt.getIngredientManager()); - list.add(bookmark); // add 内部会自动保存到配置 - }); - } - } - - /** - * 如果存在 Mekanism/appmek,则将 Mekanism 化学堆栈添加到 JEI 书签。 - */ - public static void addBookmark(Object chemicalStack) { - if (!ModList.get().isLoaded("mekanism") && !ModList.get().isLoaded("appmek")) return; - - IJeiRuntime rt = RUNTIME; - if (rt == null) return; - - IBookmarkOverlay overlay = rt.getBookmarkOverlay(); - if (overlay instanceof BookmarkOverlayAccessor accessor) { - BookmarkList list = accessor.eap$getBookmarkList(); - try { - if (chemicalStack == null) return; - - // Determine Mekanism JEI ingredient type constant by runtime class name - String clsName = chemicalStack.getClass().getName(); - String mekanismJeiClass = "mekanism.client.recipe_viewer.jei.MekanismJEI"; - Class jeiCls = Class.forName(mekanismJeiClass); - Field typeField = null; - if ("mekanism.api.chemical.ChemicalStack".equals(clsName)) { - typeField = jeiCls.getField("TYPE_CHEMICAL"); - } - if (typeField == null) return; - Object typeConst = typeField.get(null); - - // Use ingredient manager reflectively to create a typed ingredient - Object ingredientManager = rt.getIngredientManager(); - Method createTypedIngredient = ingredientManager.getClass().getMethod("createTypedIngredient", IIngredientType.class, Object.class); - Object opt = createTypedIngredient.invoke(ingredientManager, typeConst, chemicalStack); - if (!(opt instanceof Optional typedOpt)) return; - if (typedOpt.isPresent()) { - Object typed = typedOpt.get(); - // Find a compatible static create(...) method on IngredientBookmark where - // the second parameter is assignable from the actual ingredientManager instance. - Method createMethod = null; - for (Method m : IngredientBookmark.class.getMethods()) { - if (!m.getName().equals("create")) continue; - Class[] params = m.getParameterTypes(); - if (params.length != 2) continue; - // first param should accept the typed ingredient - boolean firstOk = params[0].isAssignableFrom(typed.getClass()) || params[0].isAssignableFrom(ITypedIngredient.class); - boolean secondOk = params[1].isAssignableFrom(ingredientManager.getClass()); - if (firstOk && secondOk) { - createMethod = m; - break; - } - } - if (createMethod != null) { - Object bookmark = createMethod.invoke(null, typed, ingredientManager); - if (bookmark != null) { - list.add((IngredientBookmark) bookmark); - } - } - } - } catch (Throwable e) { - e.printStackTrace(); - } - } - } - - /** - * 从 JEI 书签移除物品 - */ - public static void removeBookmark(ItemStack stack) { - IJeiRuntime rt = RUNTIME; - if (rt == null || stack == null || stack.isEmpty()) return; - - IBookmarkOverlay overlay = rt.getBookmarkOverlay(); - if (overlay instanceof BookmarkOverlayAccessor accessor) { - BookmarkList list = accessor.eap$getBookmarkList(); - try { - var typedOpt = rt.getIngredientManager().createTypedIngredient(VanillaTypes.ITEM_STACK, stack); - typedOpt.ifPresent(typed -> { - IngredientBookmark bookmark = IngredientBookmark.create(typed, rt.getIngredientManager()); - list.remove(bookmark); - }); - } catch (Throwable ignored) {} - } - } + public static void removeBookmark(ItemStack stack) { + Object rt = RUNTIME; + if (rt == null || stack == null || stack.isEmpty()) return; + try { + Object overlay = rt.getClass().getMethod("getBookmarkOverlay").invoke(rt); + if (overlay == null) return; + Field f = overlay.getClass().getDeclaredField("bookmarkList"); + f.setAccessible(true); + Object list = f.get(overlay); + Object manager = rt.getClass().getMethod("getIngredientManager").invoke(rt); + Object itemType = Class.forName("mezz.jei.api.constants.VanillaTypes").getField("ITEM_STACK").get(null); + Method createTyped = manager.getClass().getMethod("createTypedIngredient", itemType.getClass(), Object.class); + Object typedOpt = createTyped.invoke(manager, itemType, stack); + if (typedOpt instanceof Optional opt && opt.isPresent()) { + Object typed = opt.get(); + Class ibCls = Class.forName("mezz.jei.gui.bookmarks.IngredientBookmark"); + Method create = null; + for (Method m : ibCls.getMethods()) { + if (m.getName().equals("create") && m.getParameterCount() == 2) { create = m; break; } + } + if (create != null) { + Object bookmark = create.invoke(null, typed, manager); + list.getClass().getMethod("remove", ibCls).invoke(list, bookmark); + } + } + } catch (Throwable ignored) {} + } } diff --git a/src/main/java/com/extendedae_plus/mixin/ExtendedAEPlusMixinPlugin.java b/src/main/java/com/extendedae_plus/mixin/ExtendedAEPlusMixinPlugin.java new file mode 100644 index 0000000..aefb88e --- /dev/null +++ b/src/main/java/com/extendedae_plus/mixin/ExtendedAEPlusMixinPlugin.java @@ -0,0 +1,48 @@ +package com.extendedae_plus.mixin; + +import org.objectweb.asm.tree.ClassNode; +import org.spongepowered.asm.mixin.extensibility.IMixinConfigPlugin; +import org.spongepowered.asm.mixin.extensibility.IMixinInfo; + +import java.util.List; +import java.util.Set; + +public class ExtendedAEPlusMixinPlugin implements IMixinConfigPlugin { + private static boolean isJeiPresent() { + try { + ClassLoader cl = Thread.currentThread().getContextClassLoader(); + Class.forName("mezz.jei.api.IModPlugin", false, cl); + return true; + } catch (Throwable ignored) { + return false; + } + } + + @Override + public void onLoad(String mixinPackage) { } + + @Override + public String getRefMapperConfig() { return null; } + + @Override + public boolean shouldApplyMixin(String targetClassName, String mixinClassName) { + if (!isJeiPresent()) { + // Disable all JEI package mixins and any mixins that reference JEI-only helpers + if (mixinClassName.startsWith("com.extendedae_plus.mixin.jei")) return false; + if (mixinClassName.equals("com.extendedae_plus.mixin.ae2.menu.CraftConfirmMenuGoBackMixin")) return false; + } + return true; + } + + @Override + public void acceptTargets(Set myTargets, Set otherTargets) { } + + @Override + public List getMixins() { return null; } + + @Override + public void preApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) { } + + @Override + public void postApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) { } +} \ No newline at end of file diff --git a/src/main/java/com/extendedae_plus/mixin/jei/EncodingHelperMixin.java b/src/main/java/com/extendedae_plus/mixin/jei/EncodingHelperMixin.java index eb563d3..51ab800 100644 --- a/src/main/java/com/extendedae_plus/mixin/jei/EncodingHelperMixin.java +++ b/src/main/java/com/extendedae_plus/mixin/jei/EncodingHelperMixin.java @@ -27,8 +27,8 @@ public class EncodingHelperMixin { private static void epp$addJeiIngredientPriorities(MEStorageMenu menu, Comparator comparator, CallbackInfoReturnable> cir){ Map result = cir.getReturnValue(); AtomicInteger index = new AtomicInteger(Integer.MAX_VALUE); - List> list = JeiRuntimeProxy.getBookmarkList(); - for (ITypedIngredient ingredient : list) { + List> list = (List>) (List) JeiRuntimeProxy.getBookmarkList(); + for (ITypedIngredient ingredient : list) { ingredient.getIngredient(VanillaTypes.ITEM_STACK).ifPresent(itemStack -> result.put(AEItemKey.of(itemStack), index.getAndDecrement())); ingredient.getIngredient(NeoForgeTypes.FLUID_STACK).ifPresent(fluidStack -> result.put(AEFluidKey.of(fluidStack), index.getAndDecrement())); } diff --git a/src/main/resources/extendedae_plus.mixins.json b/src/main/resources/extendedae_plus.mixins.json index dfda552..e3a794a 100644 --- a/src/main/resources/extendedae_plus.mixins.json +++ b/src/main/resources/extendedae_plus.mixins.json @@ -2,6 +2,7 @@ "required": true, "package": "com.extendedae_plus.mixin", "compatibilityLevel": "JAVA_21", + "plugin": "com.extendedae_plus.mixin.ExtendedAEPlusMixinPlugin", "mixins": [ "advancedae.AdvPatternProviderLogicContainsRedirectMixin", "advancedae.accessor.AdvPatternProviderLogicPatternsAccessor", @@ -9,7 +10,6 @@ "advancedae.helpers.AdvPatternProviderLogicAdvancedMixin", "advancedae.helpers.AdvPatternProviderLogicDoublingMixin", "advancedae.menu.AdvPatternProviderMenuAdvancedMixin", - "advancedae.menu.AdvPatternProviderMenuDoublingMixin", "ae2.AEProcessingPatternMixin", "ae2.CraftingCalculationMixin", "ae2.CraftingCPUClusterMixin", From 1449df8f8be5a4a5866b88acc9d61d617e163e55 Mon Sep 17 00:00:00 2001 From: GaLicn <133291877+GaLicn@users.noreply.github.com> Date: Sun, 28 Sep 2025 00:30:14 +0800 Subject: [PATCH 43/43] 1.4.3 --- .gitignore | 3 ++- .../modMetadata/META-INF/neoforge.mods.toml | 2 +- .../recipe/assembler_matrix_upload_core.json | 8 ++++++++ .../{recipes => recipe}/channel_card.json | 4 ++-- .../recipe/infinity_biginteger_cell.json | 15 +++++++++++++++ .../recipes/assembler_matrix_upload_core.json | 11 ----------- 6 files changed, 28 insertions(+), 15 deletions(-) create mode 100644 src/main/resources/data/extendedae_plus/recipe/assembler_matrix_upload_core.json rename src/main/resources/data/extendedae_plus/{recipes => recipe}/channel_card.json (69%) create mode 100644 src/main/resources/data/extendedae_plus/recipe/infinity_biginteger_cell.json delete mode 100644 src/main/resources/data/extendedae_plus/recipes/assembler_matrix_upload_core.json diff --git a/.gitignore b/.gitignore index 7583cea..34a8beb 100644 --- a/.gitignore +++ b/.gitignore @@ -26,4 +26,5 @@ run-data repo othermods -crashreport \ No newline at end of file +crashreport +.cursor \ No newline at end of file diff --git a/build/generated/sources/modMetadata/META-INF/neoforge.mods.toml b/build/generated/sources/modMetadata/META-INF/neoforge.mods.toml index d8c7372..4eba2b3 100644 --- a/build/generated/sources/modMetadata/META-INF/neoforge.mods.toml +++ b/build/generated/sources/modMetadata/META-INF/neoforge.mods.toml @@ -24,7 +24,7 @@ license="All Rights Reserved" modId="extendedae_plus" #mandatory # The version number of the mod -version="1.21.1-1.4.4" #mandatory +version="1.21.1-1.4.3" #mandatory # A display name for the mod displayName="ExtendedAE-Plus" #mandatory diff --git a/src/main/resources/data/extendedae_plus/recipe/assembler_matrix_upload_core.json b/src/main/resources/data/extendedae_plus/recipe/assembler_matrix_upload_core.json new file mode 100644 index 0000000..d3931e0 --- /dev/null +++ b/src/main/resources/data/extendedae_plus/recipe/assembler_matrix_upload_core.json @@ -0,0 +1,8 @@ +{ + "type": "minecraft:crafting_shapeless", + "ingredients": [ + { "item": "extendedae:assembler_matrix_wall" }, + { "item": "minecraft:lever" } + ], + "result": { "id": "extendedae_plus:assembler_matrix_upload_core", "count": 1 } +} \ No newline at end of file diff --git a/src/main/resources/data/extendedae_plus/recipes/channel_card.json b/src/main/resources/data/extendedae_plus/recipe/channel_card.json similarity index 69% rename from src/main/resources/data/extendedae_plus/recipes/channel_card.json rename to src/main/resources/data/extendedae_plus/recipe/channel_card.json index d6b23ce..e379671 100644 --- a/src/main/resources/data/extendedae_plus/recipes/channel_card.json +++ b/src/main/resources/data/extendedae_plus/recipe/channel_card.json @@ -4,5 +4,5 @@ { "item": "ae2:advanced_card" }, { "item": "extendedae_plus:wireless_transceiver" } ], - "result": { "item": "extendedae_plus:channel_card", "count": 1 } -} + "result": { "id": "extendedae_plus:channel_card", "count": 1 } +} \ No newline at end of file diff --git a/src/main/resources/data/extendedae_plus/recipe/infinity_biginteger_cell.json b/src/main/resources/data/extendedae_plus/recipe/infinity_biginteger_cell.json new file mode 100644 index 0000000..2b10715 --- /dev/null +++ b/src/main/resources/data/extendedae_plus/recipe/infinity_biginteger_cell.json @@ -0,0 +1,15 @@ +{ + "type": "minecraft:crafting_shapeless", + "ingredients": [ + { "item": "minecraft:turtle_helmet" }, + { "item": "minecraft:dragon_head" }, + { "item": "minecraft:totem_of_undying" }, + { "item": "minecraft:echo_shard" }, + { "item": "ae2:cell_component_256k" }, + { "item": "minecraft:heart_of_the_sea" }, + { "item": "minecraft:nether_star" }, + { "item": "minecraft:netherite_block" }, + { "item": "minecraft:enchanted_golden_apple" } + ], + "result": { "id": "extendedae_plus:infinity_biginteger_cell", "count": 1 } +} \ No newline at end of file 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 deleted file mode 100644 index 12ca196..0000000 --- a/src/main/resources/data/extendedae_plus/recipes/assembler_matrix_upload_core.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "type": "minecraft:crafting_shapeless", - "ingredients": [ - { "item": "expatternprovider:assembler_matrix_wall" }, - { "item": "minecraft:lever" } - ], - "result": { - "item": "extendedae_plus:assembler_matrix_upload_core", - "count": 1 - } -}