From 74309779fd752c230c6aef334039cd4fb49df29b Mon Sep 17 00:00:00 2001 From: GaLicn <133291877+GaLicn@users.noreply.github.com> Date: Fri, 26 Sep 2025 23:01:08 +0800 Subject: [PATCH 01/51] =?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 --- .../extendedae_plus/client/InputEvents.java | 9 +- .../integration/jei/JeiRuntimeProxy.java | 149 ++++-------------- 2 files changed, 33 insertions(+), 125 deletions(-) diff --git a/src/main/java/com/extendedae_plus/client/InputEvents.java b/src/main/java/com/extendedae_plus/client/InputEvents.java index 6a921a4..e52cb36 100644 --- a/src/main/java/com/extendedae_plus/client/InputEvents.java +++ b/src/main/java/com/extendedae_plus/client/InputEvents.java @@ -33,10 +33,7 @@ public final class InputEvents { if (!ModList.get().isLoaded("jei")) { return; } - // 若 JEI 运行时尚未就绪,跳过 - if (JeiRuntimeProxy.get() == null) { - return; - } + // 注意:不要在 try/catch 之外直接访问 JEI 运行时,避免类加载崩溃 // 优先处理:Shift + 左键(拉取或下单) if (event.getButton() == GLFW.GLFW_MOUSE_BUTTON_LEFT && Screen.hasShiftDown()) { try { @@ -103,9 +100,7 @@ public final class InputEvents { if (!ModList.get().isLoaded("jei")) { return; } - if (JeiRuntimeProxy.get() == null) { - return; - } + // 注意:不要在 try/catch 之外直接访问 JEI 运行时,避免类加载崩溃 if (event.getKeyCode() != GLFW.GLFW_KEY_F) return; // 仅当鼠标确实悬停在 JEI 配料上时触发 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 bc5276e..38c3541 100644 --- a/src/main/java/com/extendedae_plus/integration/jei/JeiRuntimeProxy.java +++ b/src/main/java/com/extendedae_plus/integration/jei/JeiRuntimeProxy.java @@ -1,24 +1,16 @@ package com.extendedae_plus.integration.jei; -import com.extendedae_plus.mixin.jei.accessor.BookmarkOverlayAccessor; import mezz.jei.api.constants.VanillaTypes; -import mezz.jei.api.forge.ForgeTypes; -import mezz.jei.api.ingredients.IIngredientType; import mezz.jei.api.ingredients.ITypedIngredient; import mezz.jei.api.runtime.IBookmarkOverlay; import mezz.jei.api.runtime.IIngredientListOverlay; 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.minecraftforge.fluids.FluidStack; import net.minecraftforge.fml.ModList; import org.spongepowered.asm.mixin.Pseudo; import javax.annotation.Nullable; -import java.lang.reflect.Field; -import java.lang.reflect.Method; import java.util.Collections; import java.util.List; import java.util.Optional; @@ -131,48 +123,35 @@ public final class JeiRuntimeProxy { } /** - * 获取JEI书签列表 + * 获取 JEI 书签列表。为避免在未安装 JEI GUI 时崩溃,使用反射委托到桥接类。 */ 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(); + try { + Class bridge = Class.forName("com.extendedae_plus.integration.jei.JeiBookmarkBridge"); + var m = bridge.getMethod("getBookmarkList"); + @SuppressWarnings("unchecked") + List> list = (List>) m.invoke(null); + return list == null ? Collections.emptyList() : list; + } catch (Throwable ignored) { + return Collections.emptyList(); } - return Collections.emptyList(); } public static void addBookmark(ItemStack stack) { - 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(VanillaTypes.ITEM_STACK, stack); - typedOpt.ifPresent(typed -> { - IngredientBookmark bookmark = IngredientBookmark.create(typed, rt.getIngredientManager()); - list.add(bookmark); // add 内部会自动保存到配置 - }); + try { + Class bridge = Class.forName("com.extendedae_plus.integration.jei.JeiBookmarkBridge"); + var m = bridge.getMethod("addBookmark", ItemStack.class); + m.invoke(null, stack); + } 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(ForgeTypes.FLUID_STACK, fluidStack); - typedOpt.ifPresent(typed -> { - IngredientBookmark bookmark = IngredientBookmark.create(typed, rt.getIngredientManager()); - list.add(bookmark); // add 内部会自动保存到配置 - }); + try { + Class bridge = Class.forName("com.extendedae_plus.integration.jei.JeiBookmarkBridge"); + var m = bridge.getMethod("addBookmark", FluidStack.class); + m.invoke(null, fluidStack); + } catch (Throwable ignored) { } } @@ -180,91 +159,25 @@ public final class JeiRuntimeProxy { * 如果存在 Mekanism/appmek,则将 Mekanism 化学堆栈添加到 JEI 书签。 */ public static void addBookmark(Object chemicalStack) { - if (!ModList.get().isLoaded("mekanism") && !ModList.get().isLoaded("appmek")) return; - - IJeiRuntime rt = RUNTIME; - if (rt == null) return; - - IBookmarkOverlay overlay = rt.getBookmarkOverlay(); - if (overlay instanceof BookmarkOverlayAccessor accessor) { - BookmarkList list = accessor.eap$getBookmarkList(); - try { - if (chemicalStack == null) return; - - // Determine Mekanism JEI ingredient type constant by runtime class name - String clsName = chemicalStack.getClass().getName(); - String mekanismJeiClass = "mekanism.client.jei.MekanismJEI"; - Class jeiCls = Class.forName(mekanismJeiClass); - Field typeField = getField(clsName, jeiCls); - - if (typeField == null) return; - Object typeConst = typeField.get(null); - - // Use ingredient manager reflectively to create a typed ingredient - Object ingredientManager = rt.getIngredientManager(); - Method createTypedIngredient = ingredientManager.getClass().getMethod("createTypedIngredient", IIngredientType.class, Object.class); - Object opt = createTypedIngredient.invoke(ingredientManager, typeConst, chemicalStack); - if (!(opt instanceof Optional typedOpt)) return; - if (typedOpt.isPresent()) { - Object typed = typedOpt.get(); - // Find a compatible static create(...) method on IngredientBookmark where - // the second parameter is assignable from the actual ingredientManager instance. - Method createMethod = null; - for (Method m : IngredientBookmark.class.getMethods()) { - if (!m.getName().equals("create")) continue; - Class[] params = m.getParameterTypes(); - if (params.length != 2) continue; - // first param should accept the typed ingredient - boolean firstOk = params[0].isAssignableFrom(typed.getClass()) || params[0].isAssignableFrom(ITypedIngredient.class); - boolean secondOk = params[1].isAssignableFrom(ingredientManager.getClass()); - if (firstOk && secondOk) { - createMethod = m; - break; - } - } - if (createMethod != null) { - Object bookmark = createMethod.invoke(null, typed, ingredientManager); - if (bookmark != null) { - list.add((IngredientBookmark) bookmark); - } - } - } - } catch (Throwable e) { - e.printStackTrace(); - } + try { + Class bridge = Class.forName("com.extendedae_plus.integration.jei.JeiBookmarkBridge"); + var m = bridge.getMethod("addBookmark", Object.class); + m.invoke(null, chemicalStack); + } catch (Throwable ignored) { } } - private static @Nullable Field getField(String clsName, Class jeiCls) throws NoSuchFieldException { - Field typeField = null; - if ("mekanism.api.chemical.gas.GasStack".equals(clsName)) { - typeField = jeiCls.getField("TYPE_GAS"); - } else if ("mekanism.api.chemical.infuse.InfusionStack".equals(clsName)) { - typeField = jeiCls.getField("TYPE_INFUSION"); - } else if ("mekanism.api.chemical.pigment.PigmentStack".equals(clsName)) { - typeField = jeiCls.getField("TYPE_PIGMENT"); - } else if ("mekanism.api.chemical.slurry.SlurryStack".equals(clsName)) { - typeField = jeiCls.getField("TYPE_SLURRY"); - } - return typeField; - } + // Note: helper methods moved to bridge to avoid referencing JEI GUI at class load time. /** - * 从 JEI 书签移除物品 + * 从 JEI 书签移除物品(反射委托) */ public static void removeBookmark(ItemStack stack) { - 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(VanillaTypes.ITEM_STACK, stack); - typedOpt.ifPresent(typed -> { - IngredientBookmark bookmark = IngredientBookmark.create(typed, rt.getIngredientManager()); - list.remove(bookmark); - }); + try { + Class bridge = Class.forName("com.extendedae_plus.integration.jei.JeiBookmarkBridge"); + var m = bridge.getMethod("removeBookmark", ItemStack.class); + m.invoke(null, stack); + } catch (Throwable ignored) { } } } From 47c4b2cab95efbe4f1e3c7468c0ff561a3ff5145 Mon Sep 17 00:00:00 2001 From: C-H716 <1536152356@qq.com> Date: Sat, 27 Sep 2025 00:19:50 +0800 Subject: [PATCH 02/51] =?UTF-8?q?=E9=87=8D=E6=96=B0=E8=B0=83=E6=95=B4?= =?UTF-8?q?=E5=AE=9E=E4=BD=93=E5=8A=A0=E9=80=9F=E5=99=A8=E4=BB=A3=E7=A0=81?= =?UTF-8?q?=E7=BB=93=E6=9E=84=EF=BC=8C=E6=8F=90=E5=8D=87=E5=8F=AF=E8=AF=BB?= =?UTF-8?q?=E6=80=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ae/menu/EntitySpeedTickerMenu.java | 221 ++++++++------- .../ae/parts/EntitySpeedTickerPart.java | 253 +++++++++--------- .../ae/screen/EntitySpeedTickerScreen.java | 150 +++++------ .../util/ConfigParsingUtils.java | 94 ++++--- .../com/extendedae_plus/util/PowerUtils.java | 151 +++-------- 5 files changed, 416 insertions(+), 453 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 d74f897..753b97a 100644 --- a/src/main/java/com/extendedae_plus/ae/menu/EntitySpeedTickerMenu.java +++ b/src/main/java/com/extendedae_plus/ae/menu/EntitySpeedTickerMenu.java @@ -18,140 +18,159 @@ import net.minecraftforge.registries.ForgeRegistries; import java.util.List; -// 实体加速器菜单,负责与客户端界面同步数据 +/** + * 实体加速器菜单,负责管理客户端与服务端的数据同步,处理加速卡、能量卡和目标方块的状态。 + */ 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(); + 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) {} - } - - // 当服务器数据同步到客户端时调用 + /** + * 服务端数据同步到客户端时调用,更新卡数量、目标状态和生效速度。 + */ @Override public void onServerDataSync() { super.onServerDataSync(); - // 重新统计实体加速卡和能量卡数量 - 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 = ForgeRegistries.BLOCKS.getKey(target.getBlockState().getBlock()).toString(); - for (ConfigParsingUtils.MultiplierEntry me : ConfigParsingUtils.getCachedMultiplierEntries(List.of(ModConfig.INSTANCE.entityTickerMultipliers))) { - 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) { - String blockId = ForgeRegistries.BLOCKS.getKey(target.getBlockState().getBlock()).toString(); - for (java.util.regex.Pattern p : ConfigParsingUtils.getCachedBlacklist(List.of(ModConfig.INSTANCE.entityTickerBlackList))) { - 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) { 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 + public void broadcastChanges() { 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 = ForgeRegistries.BLOCKS.getKey(target.getBlockState().getBlock()).toString(); + this.multiplier = ConfigParsingUtils.getMultiplierForBlock(blockId, List.of(ModConfig.INSTANCE.entityTickerMultipliers)); + this.targetBlacklisted = ConfigParsingUtils.isBlockBlacklisted(blockId, List.of(ModConfig.INSTANCE.entityTickerBlackList)); + } + + /** + * 计算生效速度(考虑黑名单和卡数量)。 + */ + 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 654d050..91fa27f 100644 --- a/src/main/java/com/extendedae_plus/ae/parts/EntitySpeedTickerPart.java +++ b/src/main/java/com/extendedae_plus/ae/parts/EntitySpeedTickerPart.java @@ -24,7 +24,6 @@ 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.network.chat.Component; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.InteractionHand; @@ -41,52 +40,38 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.List; -import java.util.Objects; -import java.util.regex.Pattern; /** - * EntitySpeedTickerPart 是一个可升级的 AE2 部件

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

- * 功能受Crazy AE2 Addons启发 + * 实体加速器部件,用于加速目标方块实体的 tick 速率,消耗 AE 网络能量,支持加速卡和能量卡升级。 + * 灵感来源于 Crazy AE2 Addons。 */ public class EntitySpeedTickerPart extends UpgradeablePart implements IGridTickable, MenuProvider, IUpgradeableObject { - // 当前打开的菜单实例(如果有) - public EntitySpeedTickerMenu menu; - public static final ResourceLocation MODEL_BASE = new ResourceLocation( ExtendedAEPlus.MODID, "part/entity_speed_ticker_part"); @PartModels - public static final PartModel MODELS_OFF; + public static final PartModel MODELS_OFF = new PartModel(MODEL_BASE, new ResourceLocation(ExtendedAEPlus.MODID, "part/entity_speed_ticker_off")); @PartModels - public static final PartModel MODELS_ON; + public static final PartModel MODELS_ON = new PartModel(MODEL_BASE, new ResourceLocation(ExtendedAEPlus.MODID, "part/entity_speed_ticker_on")); @PartModels - public static final PartModel MODELS_HAS_CHANNEL; + public static final PartModel MODELS_HAS_CHANNEL = new PartModel(MODEL_BASE, new ResourceLocation(ExtendedAEPlus.MODID, "part/entity_speed_ticker_has_channel")); - static { - MODELS_OFF = new PartModel(MODEL_BASE, new ResourceLocation(ExtendedAEPlus.MODID, "part/entity_speed_ticker_off")); - MODELS_ON = new PartModel(MODEL_BASE, new ResourceLocation(ExtendedAEPlus.MODID, "part/entity_speed_ticker_on")); - MODELS_HAS_CHANNEL = new PartModel(MODEL_BASE, new ResourceLocation(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); } - // 控制是否启用加速(默认启用) - private boolean accelerateEnabled = true; - // 标记网络中能量是否充足(用于 GUI 提示,默认充足) - private boolean networkEnergySufficient = true; - public boolean getAccelerateEnabled() { return this.accelerateEnabled; } @@ -95,29 +80,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; @@ -129,15 +118,14 @@ public class EntitySpeedTickerPart extends UpgradeablePart implements IGridTicka } /** - * 当玩家激活部件(右键)时调用,打开自定义菜单 + * 处理玩家右键激活部件,打开菜单。 * @param player 玩家 * @param hand 手 * @param pos 点击位置 - * @return 总是返回 true,表示激活成功 + * @return 总是返回 true */ @Override public boolean onPartActivate(Player player, InteractionHand hand, Vec3 pos) { - // 仅在服务端打开菜单 if (!player.getCommandSenderWorld().isClientSide()) { MenuOpener.open(ModMenuTypes.ENTITY_TICKER_MENU.get(), player, MenuLocators.forPart(this)); } @@ -145,7 +133,7 @@ public class EntitySpeedTickerPart extends UpgradeablePart implements IGridTicka } /** - * 定义部件的碰撞箱(用于物理碰撞和渲染) + * 定义部件的碰撞箱。 * @param bch 碰撞辅助器 */ @Override @@ -155,43 +143,37 @@ public class EntitySpeedTickerPart extends UpgradeablePart implements IGridTicka } /** - * 获取定时请求,决定本部件多久 tick 一次 + * 获取定时请求,指定 tick 频率。 * @param iGridNode 网络节点 * @return TickingRequest 对象 */ @Override public TickingRequest getTickingRequest(IGridNode iGridNode) { - // 每 1 tick 执行一次 return new TickingRequest(1, 1, false, true); } /** - * 当升级卡数量发生变化时调用,通知菜单更新 + * 当升级卡变化时通知菜单更新。 */ @Override public void upgradesChanged() { - if (this.menu != null) { - // 使用 AE2 风格:当升级发生变化时让菜单广播变化(槽/数据会被同步),客户端会基于槽内容重新计算并刷新界面 - this.menu.broadcastChanges(); + if (menu != null) { + menu.broadcastChanges(); } } /** - * 网络定时回调,每次 tick 时调用 + * 网络定时回调,处理目标方块实体的加速。 * @param iGridNode 网络节点 - * @param ticksSinceLastCall 距离上次调用经过的 tick 数 - * @return TickRateModulation.IDLE 表示继续保持当前 tick 速率 + * @param ticksSinceLastCall 距离上次调用的 tick 数 + * @return TickRateModulation.IDLE */ @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); } @@ -199,85 +181,108 @@ public class EntitySpeedTickerPart extends UpgradeablePart implements IGridTicka } /** - * 以指定速度对目标方块实体进行 tick 操作 - * @param blockEntity 需要被 tick 的方块实体 + * 对目标方块实体执行加速 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 = Objects.requireNonNull(ForgeRegistries.BLOCKS.getKey(blockEntity.getBlockState().getBlock())).toString(); - - // 使用工具类的缓存接口(工具类内部负责懒加载/线程安全) - List compiledBlacklist = ConfigParsingUtils.getCachedBlacklist(List.of(ModConfig.INSTANCE.entityTickerBlackList)); - for (Pattern p : compiledBlacklist) { - if (p.matcher(blockId).matches()) return; + String blockId = ForgeRegistries.BLOCKS.getKey(blockEntity.getBlockState().getBlock()).toString(); + if (ConfigParsingUtils.isBlockBlacklisted(blockId, List.of(ModConfig.INSTANCE.entityTickerBlackList))) { + 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, List.of(ModConfig.INSTANCE.entityTickerMultipliers)); + return PowerUtils.computeFinalPowerForProduct(speed, energyCardCount) * multiplier; + } - // 如果没有任何实体加速卡,则不进行加速且不消耗额外能量(只保留部件的被动功耗) - 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(List.of(ModConfig.INSTANCE.entityTickerMultipliers))) { - 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); + /** + * 提取网络能量并更新状态。 + * @param requiredPower 所需能量 + * @return 是否成功提取 + */ + private boolean extractPower(double requiredPower) { + var energyService = getMainNode().getGrid().getEnergyService(); + double simulated = energyService.extractAEPower(requiredPower, Actionable.SIMULATE, PowerMultiplier.CONFIG); if (simulated < requiredPower) { - updateNetworkEnergySufficient(false); // 能量不足 - return; + updateNetworkEnergySufficient(false); + return false; } + double extracted = energyService.extractAEPower(requiredPower, Actionable.MODULATE, PowerMultiplier.CONFIG); + boolean sufficient = extracted >= requiredPower; + updateNetworkEnergySufficient(sufficient); + return sufficient; + } - double extractedPower = getMainNode().getGrid().getEnergyService() - .extractAEPower(requiredPower, Actionable.MODULATE, PowerMultiplier.CONFIG); - if (extractedPower < requiredPower) { - updateNetworkEnergySufficient(false); // 能量不足 - return; - } - updateNetworkEnergySufficient(true); // 能量充足 - - // 计算加速倍数:基于 2 的次方,并把 8 张映射到最大 1024x(2^10) - // 已由 product 计算得到 speed;上面已在没有卡时提前返回 - - // 执行 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(), @@ -286,26 +291,18 @@ public class EntitySpeedTickerPart extends UpgradeablePart implements IGridTicka } } - /** - * 判断部件是否有自定义名称 - * @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 玩家 @@ -319,7 +316,7 @@ public class EntitySpeedTickerPart extends UpgradeablePart implements IGridTicka } /** - * 获取可用的升级卡槽数量 + * 获取升级卡槽数量。 * @return 升级卡槽数量 */ @Override 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 04fc9b1..0f6ae50 100644 --- a/src/main/java/com/extendedae_plus/ae/screen/EntitySpeedTickerScreen.java +++ b/src/main/java/com/extendedae_plus/ae/screen/EntitySpeedTickerScreen.java @@ -15,125 +15,111 @@ import com.extendedae_plus.util.PowerUtils; 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, - (btn, backwards) -> { - // 不做本地切换,点击仅发送自定义C2S,显示由@GuiSync回传 - ModNetwork.CHANNEL.sendToServer(new ToggleEntityTickerC2SPacket()); - } + (btn, backwards) -> + ModNetwork.CHANNEL.sendToServer(new ToggleEntityTickerC2SPacket()) ) { @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(); } + /** + * 刷新界面显示。 + */ public void refreshGui() { 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); - int energyCardCount = getMenu().energyCardCount; - double multiplier = getMenu().multiplier; - int effectiveSpeed = getMenu().effectiveSpeed; - double finalPower = PowerUtils.computeFinalPowerForProduct(effectiveSpeed, energyCardCount); - double remainingRatio = PowerUtils.getRemainingRatio(energyCardCount); - - if (!getMenu().networkEnergySufficient) { - setTextContent("enable", Component.translatable("screen.extendedae_plus.entity_speed_ticker.warning_network_energy_insufficient")); - }else { - setTextContent("enable", null); + 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))); } - 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/util/ConfigParsingUtils.java b/src/main/java/com/extendedae_plus/util/ConfigParsingUtils.java index 6323978..f74da49 100644 --- a/src/main/java/com/extendedae_plus/util/ConfigParsingUtils.java +++ b/src/main/java/com/extendedae_plus/util/ConfigParsingUtils.java @@ -6,6 +6,8 @@ import java.util.List; import java.util.regex.Pattern; import java.util.regex.PatternSyntaxException; +import static com.extendedae_plus.util.ExtendedAELogger.LOGGER; + /** * 配置解析工具类:用于解析黑名单与倍率配置的字符串 */ @@ -24,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('\\'); } @@ -63,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; } @@ -122,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); @@ -142,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); @@ -189,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/PowerUtils.java b/src/main/java/com/extendedae_plus/util/PowerUtils.java index 93c5001..e8c2776 100644 --- a/src/main/java/com/extendedae_plus/util/PowerUtils.java +++ b/src/main/java/com/extendedae_plus/util/PowerUtils.java @@ -1,39 +1,51 @@ package com.extendedae_plus.util; +import appeng.api.upgrades.IUpgradeInventory; import com.extendedae_plus.ae.definitions.upgrades.EntitySpeedCardItem; import com.extendedae_plus.config.ModConfig; +import net.minecraft.world.item.ItemStack; + +import java.util.ArrayList; +import java.util.List; /** * 用于计算实体加速器的能耗与加速倍率的工具类 */ public final class PowerUtils { private PowerUtils() {} - // ---- 重构后的 API ---- + /** - * 将 card multipliers(按插槽序)计算乘积并应用 cap 规则(见 capForHighestMultiplier) - * @param multipliers iterable of per-card multipliers - * @param maxCards 最多计入的卡数 - * @return 被 cap 约束后的乘积 + * 计算加速卡的乘积并应用上限。 + * @param upgrades 升级槽位 + * @param maxCards 最大计入的卡数 + * @return 被上限约束后的乘积 */ - public static long computeProductWithCap(Iterable multipliers, int maxCards) { - long product = 1L; + 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) continue; - if (considered >= maxCards) break; - int mult = m.intValue(); - if (mult <= 0) mult = 1; - product *= mult; - highest = Math.max(highest, mult); - considered++; + if (m == null || m <= 0) continue; + product *= m; + highest = Math.max(highest, m); } - long cap = capForHighestMultiplier(highest); - return Math.min(product, cap); + return Math.min(product, capForHighestMultiplier(highest)); } /** - * 根据最高单卡 multiplier 返回 cap 值 + * 根据最高单卡倍率返回上限值。 */ public static long capForHighestMultiplier(int highestMultiplier) { if (highestMultiplier >= 16) return 1024L; @@ -44,118 +56,43 @@ public final class PowerUtils { } /** - * 从菜单对象读取前 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 + * 计算最终能耗。 + * @param product 加速卡乘积 + * @param energyCardCount 能量卡数量 + * @return 最终能耗值 */ public static double computeFinalPowerForProduct(long product, int energyCardCount) { if (product <= 1L) return 0.0; double base = ModConfig.INSTANCE.entityTickerCost; - - // 计算以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 = ModConfig.INSTANCE.entityTickerCost; - 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; + return raw * getRemainingRatio(energyCardCount) / 8.0; } /** - * 返回能源卡减免后剩余的功耗比率(例如 1 张能源卡 -> 0.9) - * @param energyCardCount 能源卡数量 - * @return 剩余功耗比率 + * 计算能量卡减免后的剩余功耗比率。 */ public static double getRemainingRatio(int energyCardCount) { - return 1.0 - getReductionPercent(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)); } /** - * 将剩余功耗比率格式化为百分比字符串(例如 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); + 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 From e1bd547590a5e2e7b72162266eb735fdf2261420 Mon Sep 17 00:00:00 2001 From: C-H716 <1536152356@qq.com> Date: Sat, 27 Sep 2025 00:56:35 +0800 Subject: [PATCH 03/51] =?UTF-8?q?=E8=A7=84=E8=8C=83=E6=8E=A5=E5=8F=A3?= =?UTF-8?q?=E7=B1=BB=E5=91=BD=E5=90=8D=E5=8F=8A=E7=BB=93=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...lockingHolder.java => IAdvancedBlocking.java} | 2 +- ...essor.java => IExPatternButtonsAccessor.java} | 2 +- ...Accessor.java => IExPatternPageAccessor.java} | 2 +- ...ava => IPatternProviderMenuAdvancedSync.java} | 2 +- .../api/PatternProviderMenuDoublingSync.java | 5 ----- .../api/SmartDoublingAwarePattern.java | 6 ------ .../extendedae_plus/api/SmartDoublingHolder.java | 6 ------ .../bridge/IInterfaceWirelessLinkBridge.java} | 4 ++-- .../{ => api}/bridge/IUpgradableMenu.java | 2 +- .../IPatternProviderMenuDoublingSync.java | 5 +++++ .../ISmartDoublingAwarePattern.java | 6 ++++++ .../api/smartDoubling/ISmartDoublingHolder.java | 6 ++++++ .../gui/AdvPatternProviderScreenMixin.java | 16 ++++++++-------- .../gui/SmallAdvPatternProviderScreenMixin.java | 16 ++++++++-------- .../AdvPatternProviderLogicAdvancedMixin.java | 4 ++-- .../AdvPatternProviderLogicDoublingMixin.java | 10 +++++----- .../AdvPatternProviderMenuAdvancedMixin.java | 12 ++++++------ .../AdvPatternProviderMenuDoublingMixin.java | 12 ++++++------ .../mixin/ae2/AEProcessingPatternMixin.java | 4 ++-- .../autopattern/CraftingTreeProcessMixin.java | 6 +++--- .../mixin/ae2/client/gui/AEBaseScreenMixin.java | 4 ++-- .../client/gui/PatternProviderScreenMixin.java | 16 ++++++++-------- .../gui/PatternProviderScreenUpgradesMixin.java | 2 +- .../ae2/client/gui/SlotGridLayoutMixin.java | 3 ++- .../compat/PatternProviderLogicCompatMixin.java | 4 ++-- .../helpers/InterfaceLogicChannelCardMixin.java | 5 ++--- .../helpers/InterfaceLogicChannelLinkBridge.java | 2 +- .../ae2/helpers/InterfaceLogicTickerMixin.java | 7 +++---- .../PatternProviderLogicAdvancedMixin.java | 4 ++-- .../PatternProviderLogicDoublingMixin.java | 10 +++++----- .../PatternProviderLogicTickerMixin.java | 6 +++--- .../menu/PatternProviderMenuAdvancedMixin.java | 12 ++++++------ .../menu/PatternProviderMenuDoublingMixin.java | 12 ++++++------ .../menu/PatternProviderMenuUpgradesMixin.java | 2 +- .../ae2/parts/AEBasePartClientSyncMixin.java | 10 +++++----- .../automation/IOBusPartChannelCardMixin.java | 10 ++++------ .../IOBusPartTickerChannelCardMixin.java | 4 ++-- .../StorageBusPartChannelCardMixin.java | 9 ++++----- .../StorageBusPartTickerChannelCardMixin.java | 4 ++-- .../client/gui/GuiExPatternProviderMixin.java | 5 +++-- .../GlobalToggleProviderModesC2SPacket.java | 14 +++++++------- .../network/ToggleAdvancedBlockingC2SPacket.java | 6 +++--- .../network/ToggleSmartDoublingC2SPacket.java | 6 +++--- .../com/extendedae_plus/util/PatternScaler.java | 4 ++-- 44 files changed, 143 insertions(+), 146 deletions(-) rename src/main/java/com/extendedae_plus/api/{AdvancedBlockingHolder.java => IAdvancedBlocking.java} (74%) rename src/main/java/com/extendedae_plus/api/{ExPatternButtonsAccessor.java => IExPatternButtonsAccessor.java} (87%) rename src/main/java/com/extendedae_plus/api/{ExPatternPageAccessor.java => IExPatternPageAccessor.java} (82%) rename src/main/java/com/extendedae_plus/api/{PatternProviderMenuAdvancedSync.java => IPatternProviderMenuAdvancedSync.java} (60%) delete mode 100644 src/main/java/com/extendedae_plus/api/PatternProviderMenuDoublingSync.java delete mode 100644 src/main/java/com/extendedae_plus/api/SmartDoublingAwarePattern.java delete mode 100644 src/main/java/com/extendedae_plus/api/SmartDoublingHolder.java rename src/main/java/com/extendedae_plus/{bridge/InterfaceWirelessLinkBridge.java => api/bridge/IInterfaceWirelessLinkBridge.java} (92%) rename src/main/java/com/extendedae_plus/{ => api}/bridge/IUpgradableMenu.java (89%) create mode 100644 src/main/java/com/extendedae_plus/api/smartDoubling/IPatternProviderMenuDoublingSync.java create mode 100644 src/main/java/com/extendedae_plus/api/smartDoubling/ISmartDoublingAwarePattern.java create mode 100644 src/main/java/com/extendedae_plus/api/smartDoubling/ISmartDoublingHolder.java diff --git a/src/main/java/com/extendedae_plus/api/AdvancedBlockingHolder.java b/src/main/java/com/extendedae_plus/api/IAdvancedBlocking.java similarity index 74% rename from src/main/java/com/extendedae_plus/api/AdvancedBlockingHolder.java rename to src/main/java/com/extendedae_plus/api/IAdvancedBlocking.java index 52b8aaa..1895a7d 100644 --- a/src/main/java/com/extendedae_plus/api/AdvancedBlockingHolder.java +++ b/src/main/java/com/extendedae_plus/api/IAdvancedBlocking.java @@ -1,6 +1,6 @@ package com.extendedae_plus.api; -public interface AdvancedBlockingHolder { +public interface IAdvancedBlocking { boolean eap$getAdvancedBlocking(); void eap$setAdvancedBlocking(boolean value); diff --git a/src/main/java/com/extendedae_plus/api/ExPatternButtonsAccessor.java b/src/main/java/com/extendedae_plus/api/IExPatternButtonsAccessor.java similarity index 87% rename from src/main/java/com/extendedae_plus/api/ExPatternButtonsAccessor.java rename to src/main/java/com/extendedae_plus/api/IExPatternButtonsAccessor.java index d97f448..ac4d1ec 100644 --- a/src/main/java/com/extendedae_plus/api/ExPatternButtonsAccessor.java +++ b/src/main/java/com/extendedae_plus/api/IExPatternButtonsAccessor.java @@ -3,7 +3,7 @@ package com.extendedae_plus.api; /** * 由 {@code GuiExPatternProviderMixin} 实现,用于从通用的 Screen Mixin 中更新按钮布局。 */ -public interface ExPatternButtonsAccessor { +public interface IExPatternButtonsAccessor { /** * 在每帧调用以维护扩展样板供应器右侧按钮的可见性、重注册(窗口尺寸变化)与定位。 */ diff --git a/src/main/java/com/extendedae_plus/api/ExPatternPageAccessor.java b/src/main/java/com/extendedae_plus/api/IExPatternPageAccessor.java similarity index 82% rename from src/main/java/com/extendedae_plus/api/ExPatternPageAccessor.java rename to src/main/java/com/extendedae_plus/api/IExPatternPageAccessor.java index fca633f..c368e26 100644 --- a/src/main/java/com/extendedae_plus/api/ExPatternPageAccessor.java +++ b/src/main/java/com/extendedae_plus/api/IExPatternPageAccessor.java @@ -3,6 +3,6 @@ package com.extendedae_plus.api; /** * 由 GuiExPatternProviderMixin 实现,用于在客户端侧提供当前页号,避免反射读取 AE2 内部字段失败。 */ -public interface ExPatternPageAccessor { +public interface IExPatternPageAccessor { int eap$getCurrentPage(); } diff --git a/src/main/java/com/extendedae_plus/api/PatternProviderMenuAdvancedSync.java b/src/main/java/com/extendedae_plus/api/IPatternProviderMenuAdvancedSync.java similarity index 60% rename from src/main/java/com/extendedae_plus/api/PatternProviderMenuAdvancedSync.java rename to src/main/java/com/extendedae_plus/api/IPatternProviderMenuAdvancedSync.java index f43f52c..5d5147d 100644 --- a/src/main/java/com/extendedae_plus/api/PatternProviderMenuAdvancedSync.java +++ b/src/main/java/com/extendedae_plus/api/IPatternProviderMenuAdvancedSync.java @@ -1,5 +1,5 @@ package com.extendedae_plus.api; -public interface PatternProviderMenuAdvancedSync { +public interface IPatternProviderMenuAdvancedSync { boolean eap$getAdvancedBlockingSynced(); } diff --git a/src/main/java/com/extendedae_plus/api/PatternProviderMenuDoublingSync.java b/src/main/java/com/extendedae_plus/api/PatternProviderMenuDoublingSync.java deleted file mode 100644 index a9d1492..0000000 --- a/src/main/java/com/extendedae_plus/api/PatternProviderMenuDoublingSync.java +++ /dev/null @@ -1,5 +0,0 @@ -package com.extendedae_plus.api; - -public interface PatternProviderMenuDoublingSync { - boolean eap$getSmartDoublingSynced(); -} diff --git a/src/main/java/com/extendedae_plus/api/SmartDoublingAwarePattern.java b/src/main/java/com/extendedae_plus/api/SmartDoublingAwarePattern.java deleted file mode 100644 index 5c18658..0000000 --- a/src/main/java/com/extendedae_plus/api/SmartDoublingAwarePattern.java +++ /dev/null @@ -1,6 +0,0 @@ -package com.extendedae_plus.api; - -public interface SmartDoublingAwarePattern { - boolean eap$allowScaling(); - void eap$setAllowScaling(boolean allow); -} diff --git a/src/main/java/com/extendedae_plus/api/SmartDoublingHolder.java b/src/main/java/com/extendedae_plus/api/SmartDoublingHolder.java deleted file mode 100644 index b5af4ef..0000000 --- a/src/main/java/com/extendedae_plus/api/SmartDoublingHolder.java +++ /dev/null @@ -1,6 +0,0 @@ -package com.extendedae_plus.api; - -public interface SmartDoublingHolder { - boolean eap$getSmartDoubling(); - void eap$setSmartDoubling(boolean value); -} diff --git a/src/main/java/com/extendedae_plus/bridge/InterfaceWirelessLinkBridge.java b/src/main/java/com/extendedae_plus/api/bridge/IInterfaceWirelessLinkBridge.java similarity index 92% rename from src/main/java/com/extendedae_plus/bridge/InterfaceWirelessLinkBridge.java rename to src/main/java/com/extendedae_plus/api/bridge/IInterfaceWirelessLinkBridge.java index d200f77..f83aea5 100644 --- a/src/main/java/com/extendedae_plus/bridge/InterfaceWirelessLinkBridge.java +++ b/src/main/java/com/extendedae_plus/api/bridge/IInterfaceWirelessLinkBridge.java @@ -1,9 +1,9 @@ -package com.extendedae_plus.bridge; +package com.extendedae_plus.api.bridge; /** * 非 mixin 包下的桥接接口,供 mixin 进行 instanceof 检测和回调。 */ -public interface InterfaceWirelessLinkBridge { +public interface IInterfaceWirelessLinkBridge { void eap$updateWirelessLink(); /** diff --git a/src/main/java/com/extendedae_plus/bridge/IUpgradableMenu.java b/src/main/java/com/extendedae_plus/api/bridge/IUpgradableMenu.java similarity index 89% rename from src/main/java/com/extendedae_plus/bridge/IUpgradableMenu.java rename to src/main/java/com/extendedae_plus/api/bridge/IUpgradableMenu.java index 470ccd5..4bdbbb7 100644 --- a/src/main/java/com/extendedae_plus/bridge/IUpgradableMenu.java +++ b/src/main/java/com/extendedae_plus/api/bridge/IUpgradableMenu.java @@ -1,4 +1,4 @@ -package com.extendedae_plus.bridge; +package com.extendedae_plus.api.bridge; import appeng.api.upgrades.IUpgradeInventory; import appeng.menu.ToolboxMenu; diff --git a/src/main/java/com/extendedae_plus/api/smartDoubling/IPatternProviderMenuDoublingSync.java b/src/main/java/com/extendedae_plus/api/smartDoubling/IPatternProviderMenuDoublingSync.java new file mode 100644 index 0000000..8160d05 --- /dev/null +++ b/src/main/java/com/extendedae_plus/api/smartDoubling/IPatternProviderMenuDoublingSync.java @@ -0,0 +1,5 @@ +package com.extendedae_plus.api.smartDoubling; + +public interface IPatternProviderMenuDoublingSync { + boolean eap$getSmartDoublingSynced(); +} diff --git a/src/main/java/com/extendedae_plus/api/smartDoubling/ISmartDoublingAwarePattern.java b/src/main/java/com/extendedae_plus/api/smartDoubling/ISmartDoublingAwarePattern.java new file mode 100644 index 0000000..c62dc81 --- /dev/null +++ b/src/main/java/com/extendedae_plus/api/smartDoubling/ISmartDoublingAwarePattern.java @@ -0,0 +1,6 @@ +package com.extendedae_plus.api.smartDoubling; + +public interface ISmartDoublingAwarePattern { + boolean eap$allowScaling(); + void eap$setAllowScaling(boolean allow); +} diff --git a/src/main/java/com/extendedae_plus/api/smartDoubling/ISmartDoublingHolder.java b/src/main/java/com/extendedae_plus/api/smartDoubling/ISmartDoublingHolder.java new file mode 100644 index 0000000..a1f3cb0 --- /dev/null +++ b/src/main/java/com/extendedae_plus/api/smartDoubling/ISmartDoublingHolder.java @@ -0,0 +1,6 @@ +package com.extendedae_plus.api.smartDoubling; + +public interface ISmartDoublingHolder { + boolean eap$getSmartDoubling(); + void eap$setSmartDoubling(boolean value); +} 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 7db6811..8a64727 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 @@ -5,9 +5,9 @@ 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.ExPatternButtonsAccessor; -import com.extendedae_plus.api.PatternProviderMenuAdvancedSync; -import com.extendedae_plus.api.PatternProviderMenuDoublingSync; +import com.extendedae_plus.api.IExPatternButtonsAccessor; +import com.extendedae_plus.api.IPatternProviderMenuAdvancedSync; +import com.extendedae_plus.api.smartDoubling.IPatternProviderMenuDoublingSync; import com.extendedae_plus.init.ModNetwork; import com.extendedae_plus.network.ToggleAdvancedBlockingC2SPacket; import com.extendedae_plus.network.ToggleSmartDoublingC2SPacket; @@ -52,7 +52,7 @@ public abstract class AdvPatternProviderScreenMixin extends AEBaseScreen private void eap$initAdvancedBlocking(C menu, Inventory playerInventory, Component title, ScreenStyle style, CallbackInfo ci) { // 使用 @GuiSync 初始化 try { - if (menu instanceof PatternProviderMenuAdvancedSync sync) { + if (menu instanceof IPatternProviderMenuAdvancedSync sync) { this.eap$AdvancedBlockingEnabled = sync.eap$getAdvancedBlockingSynced(); } } catch (Throwable t) { @@ -85,7 +85,7 @@ public abstract class PatternProviderScreenMixin // 智能翻倍按钮:与高级阻挡同款样式,点击仅发送C2S,状态由@GuiSync驱动 try { - if (menu instanceof PatternProviderMenuDoublingSync sync2) { + if (menu instanceof IPatternProviderMenuDoublingSync sync2) { this.eap$SmartDoublingEnabled = sync2.eap$getSmartDoublingSynced(); } } catch (Throwable t) { @@ -119,7 +119,7 @@ public abstract class PatternProviderScreenMixin private void eap$updateAdvancedBlocking(CallbackInfo ci) { if (this.eap$AdvancedBlockingToggle != null) { boolean desired = this.eap$AdvancedBlockingEnabled; - if (this.menu instanceof PatternProviderMenuAdvancedSync sync) { + if (this.menu instanceof IPatternProviderMenuAdvancedSync sync) { desired = sync.eap$getAdvancedBlockingSynced(); } this.eap$AdvancedBlockingEnabled = desired; @@ -128,7 +128,7 @@ public abstract class PatternProviderScreenMixin if (this.eap$SmartDoublingToggle != null) { boolean desired2 = this.eap$SmartDoublingEnabled; - if (this.menu instanceof PatternProviderMenuDoublingSync sync2) { + if (this.menu instanceof IPatternProviderMenuDoublingSync sync2) { desired2 = sync2.eap$getSmartDoublingSynced(); } this.eap$SmartDoublingEnabled = desired2; @@ -137,7 +137,7 @@ public abstract class PatternProviderScreenMixin if ((Object) this instanceof GuiExPatternProvider) { try { - ((ExPatternButtonsAccessor) this).eap$updateButtonsLayout(); + ((IExPatternButtonsAccessor) this).eap$updateButtonsLayout(); } catch (Throwable t) { LOGGER.debug("[EAP] updateButtonsLayout skipped: {}", t.toString()); } 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 d02b4f9..1a061ae 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 @@ -9,7 +9,7 @@ import appeng.client.gui.widgets.UpgradesPanel; import appeng.core.localization.GuiText; import appeng.menu.SlotSemantics; import appeng.menu.implementations.PatternProviderMenu; -import com.extendedae_plus.bridge.IUpgradableMenu; +import com.extendedae_plus.api.bridge.IUpgradableMenu; import com.extendedae_plus.compat.UpgradeSlotCompat; import net.minecraft.network.chat.Component; import net.minecraft.world.entity.player.Inventory; diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/client/gui/SlotGridLayoutMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/client/gui/SlotGridLayoutMixin.java index aa2267c..b40d099 100644 --- a/src/main/java/com/extendedae_plus/mixin/ae2/client/gui/SlotGridLayoutMixin.java +++ b/src/main/java/com/extendedae_plus/mixin/ae2/client/gui/SlotGridLayoutMixin.java @@ -2,6 +2,7 @@ package com.extendedae_plus.mixin.ae2.client.gui; import appeng.client.Point; import appeng.client.gui.layout.SlotGridLayout; +import com.extendedae_plus.api.IExPatternPageAccessor; import net.minecraft.client.Minecraft; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Unique; @@ -32,7 +33,7 @@ public abstract class SlotGridLayoutMixin { // 读取实际当前页码:优先从 GUI accessor,其次反射容器,失败则为 0 int currentPage = 0; try { - if (screen instanceof com.extendedae_plus.api.ExPatternPageAccessor accessor) { + if (screen instanceof IExPatternPageAccessor accessor) { currentPage = accessor.eap$getCurrentPage(); } else { var menu = ((com.glodblock.github.extendedae.client.gui.GuiExPatternProvider) screen).getMenu(); 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 1b922e2..bad667d 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 @@ -8,7 +8,7 @@ import appeng.api.upgrades.UpgradeInventories; import appeng.helpers.patternprovider.PatternProviderLogic; import appeng.helpers.patternprovider.PatternProviderLogicHost; import com.extendedae_plus.ae.items.ChannelCardItem; -import com.extendedae_plus.bridge.InterfaceWirelessLinkBridge; +import com.extendedae_plus.api.bridge.IInterfaceWirelessLinkBridge; import com.extendedae_plus.compat.UpgradeSlotCompat; import com.extendedae_plus.init.ModItems; import com.extendedae_plus.wireless.WirelessSlaveLink; @@ -31,7 +31,7 @@ import java.util.List; * 根据appflux是否存在来决定是否实现IUpgradeableObject接口 */ @Mixin(value = PatternProviderLogic.class, priority = 500, remap = false) -public abstract class PatternProviderLogicCompatMixin implements IUpgradeableObject, InterfaceWirelessLinkBridge { +public abstract class PatternProviderLogicCompatMixin implements IUpgradeableObject, IInterfaceWirelessLinkBridge { @Unique private IUpgradeInventory eap$compatUpgrades = UpgradeInventories.empty(); 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 bb4b6cf..4e9b52a 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 @@ -4,11 +4,10 @@ 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.api.bridge.IInterfaceWirelessLinkBridge; 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; @@ -18,7 +17,7 @@ import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; @Mixin(InterfaceLogic.class) -public abstract class InterfaceLogicChannelCardMixin implements InterfaceWirelessLinkBridge { +public abstract class InterfaceLogicChannelCardMixin implements IInterfaceWirelessLinkBridge { @Shadow(remap = false) public abstract IUpgradeInventory getUpgrades(); diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/helpers/InterfaceLogicChannelLinkBridge.java b/src/main/java/com/extendedae_plus/mixin/ae2/helpers/InterfaceLogicChannelLinkBridge.java index 7dd6ad0..4822737 100644 --- a/src/main/java/com/extendedae_plus/mixin/ae2/helpers/InterfaceLogicChannelLinkBridge.java +++ b/src/main/java/com/extendedae_plus/mixin/ae2/helpers/InterfaceLogicChannelLinkBridge.java @@ -1,4 +1,4 @@ -package com.extendedae_plus.bridge; +package com.extendedae_plus.api.bridge; /** * 旧名兼容:已迁移到非 mixin 包,避免 Mixin 处理器禁止直接引用。 diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/helpers/InterfaceLogicTickerMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/helpers/InterfaceLogicTickerMixin.java index a2063d5..88bea12 100644 --- a/src/main/java/com/extendedae_plus/mixin/ae2/helpers/InterfaceLogicTickerMixin.java +++ b/src/main/java/com/extendedae_plus/mixin/ae2/helpers/InterfaceLogicTickerMixin.java @@ -1,8 +1,7 @@ package com.extendedae_plus.mixin.ae2.helpers; import appeng.helpers.InterfaceLogic; -import com.extendedae_plus.bridge.InterfaceWirelessLinkBridge; -import com.extendedae_plus.util.ExtendedAELogger; +import com.extendedae_plus.api.bridge.IInterfaceWirelessLinkBridge; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; @@ -29,7 +28,7 @@ public abstract class InterfaceLogicTickerMixin { return; } - if (this$0 instanceof InterfaceWirelessLinkBridge bridge) { + if (this$0 instanceof IInterfaceWirelessLinkBridge bridge) { // 处理延迟初始化 bridge.eap$handleDelayedInit(); } @@ -38,7 +37,7 @@ public abstract class InterfaceLogicTickerMixin { @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) { + if (this$0 instanceof IInterfaceWirelessLinkBridge bridge) { bridge.eap$updateWirelessLink(); } } 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 31b4654..bcaa70b 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 @@ -6,7 +6,7 @@ import appeng.api.stacks.AEKey; import appeng.api.stacks.GenericStack; import appeng.helpers.patternprovider.PatternProviderLogic; import appeng.helpers.patternprovider.PatternProviderTarget; -import com.extendedae_plus.api.AdvancedBlockingHolder; +import com.extendedae_plus.api.IAdvancedBlocking; import net.minecraft.nbt.CompoundTag; import net.minecraft.world.entity.player.Player; import org.spongepowered.asm.mixin.Mixin; @@ -20,7 +20,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import java.util.Collections; @Mixin(value = PatternProviderLogic.class, remap = false) -public class PatternProviderLogicAdvancedMixin implements AdvancedBlockingHolder { +public class PatternProviderLogicAdvancedMixin implements IAdvancedBlocking { @Unique private static final String EAP_ADV_BLOCKING_KEY = "eap_advanced_blocking"; 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 13806f2..df1e66a 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 @@ -3,8 +3,8 @@ package com.extendedae_plus.mixin.ae2.helpers; import appeng.api.crafting.IPatternDetails; import appeng.crafting.pattern.AEProcessingPattern; import appeng.helpers.patternprovider.PatternProviderLogic; -import com.extendedae_plus.api.SmartDoublingAwarePattern; -import com.extendedae_plus.api.SmartDoublingHolder; +import com.extendedae_plus.api.smartDoubling.ISmartDoublingAwarePattern; +import com.extendedae_plus.api.smartDoubling.ISmartDoublingHolder; import com.extendedae_plus.mixin.ae2.accessor.PatternProviderLogicPatternsAccessor; import net.minecraft.nbt.CompoundTag; import net.minecraft.world.entity.player.Player; @@ -16,7 +16,7 @@ import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; @Mixin(value = PatternProviderLogic.class, remap = false) -public class PatternProviderLogicDoublingMixin implements SmartDoublingHolder { +public class PatternProviderLogicDoublingMixin implements ISmartDoublingHolder { @Unique private static final String EAP_SMART_DOUBLING_KEY = "eap_smart_doubling"; @@ -35,7 +35,7 @@ public class PatternProviderLogicDoublingMixin implements SmartDoublingHolder { try { var list = ((PatternProviderLogicPatternsAccessor) this).eap$patterns(); for (IPatternDetails details : list) { - if (details instanceof AEProcessingPattern proc && proc instanceof SmartDoublingAwarePattern aware) { + if (details instanceof AEProcessingPattern proc && proc instanceof ISmartDoublingAwarePattern aware) { aware.eap$setAllowScaling(value); } } @@ -63,7 +63,7 @@ public class PatternProviderLogicDoublingMixin implements SmartDoublingHolder { var list = ((PatternProviderLogicPatternsAccessor) this).eap$patterns(); boolean allow = this.eap$smartDoubling; for (IPatternDetails details : list) { - if (details instanceof AEProcessingPattern proc && proc instanceof SmartDoublingAwarePattern aware) { + if (details instanceof AEProcessingPattern proc && proc instanceof ISmartDoublingAwarePattern aware) { aware.eap$setAllowScaling(allow); } } 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 e799173..27ecb7f 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 @@ -1,7 +1,7 @@ package com.extendedae_plus.mixin.ae2.helpers.patternprovider; import appeng.helpers.patternprovider.PatternProviderLogic; -import com.extendedae_plus.bridge.InterfaceWirelessLinkBridge; +import com.extendedae_plus.api.bridge.IInterfaceWirelessLinkBridge; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; @@ -27,7 +27,7 @@ public abstract class PatternProviderLogicTickerMixin { if (node != null && node.getLevel() != null && node.getLevel().isClientSide) { return; } - if (this$0 instanceof InterfaceWirelessLinkBridge bridge) { + if (this$0 instanceof IInterfaceWirelessLinkBridge bridge) { bridge.eap$handleDelayedInit(); } } @@ -35,7 +35,7 @@ public abstract class PatternProviderLogicTickerMixin { @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) { + if (this$0 instanceof IInterfaceWirelessLinkBridge bridge) { bridge.eap$updateWirelessLink(); } } diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/menu/PatternProviderMenuAdvancedMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/menu/PatternProviderMenuAdvancedMixin.java index 28174ac..c9e1271 100644 --- a/src/main/java/com/extendedae_plus/mixin/ae2/menu/PatternProviderMenuAdvancedMixin.java +++ b/src/main/java/com/extendedae_plus/mixin/ae2/menu/PatternProviderMenuAdvancedMixin.java @@ -5,8 +5,8 @@ import appeng.helpers.patternprovider.PatternProviderLogicHost; import appeng.menu.AEBaseMenu; import appeng.menu.guisync.GuiSync; import appeng.menu.implementations.PatternProviderMenu; -import com.extendedae_plus.api.AdvancedBlockingHolder; -import com.extendedae_plus.api.PatternProviderMenuAdvancedSync; +import com.extendedae_plus.api.IAdvancedBlocking; +import com.extendedae_plus.api.IPatternProviderMenuAdvancedSync; import net.minecraft.world.entity.player.Inventory; import net.minecraft.world.inventory.MenuType; import org.spongepowered.asm.mixin.Final; @@ -20,7 +20,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import static com.extendedae_plus.util.ExtendedAELogger.LOGGER; @Mixin(PatternProviderMenu.class) -public abstract class PatternProviderMenuAdvancedMixin implements PatternProviderMenuAdvancedSync { +public abstract class PatternProviderMenuAdvancedMixin implements IPatternProviderMenuAdvancedSync { @Final @Shadow(remap = false) protected PatternProviderLogic logic; @@ -35,7 +35,7 @@ public abstract class PatternProviderMenuAdvancedMixin implements PatternProvide // 避免@Shadow父类方法,改用公共API:AEBaseMenu#isClientSide() if (!((AEBaseMenu) (Object) this).isClientSide()) { var l = this.logic; - if (l instanceof AdvancedBlockingHolder holder) { + if (l instanceof IAdvancedBlocking holder) { this.eap$AdvancedBlocking = holder.eap$getAdvancedBlocking(); } } @@ -46,7 +46,7 @@ public abstract class PatternProviderMenuAdvancedMixin implements PatternProvide private void eap$initAdvancedSync_Public(int id, Inventory playerInventory, PatternProviderLogicHost host, CallbackInfo ci) { try { var l = this.logic; - if (l instanceof AdvancedBlockingHolder holder) { + if (l instanceof IAdvancedBlocking holder) { this.eap$AdvancedBlocking = holder.eap$getAdvancedBlocking(); } } catch (Throwable ignored) {} @@ -57,7 +57,7 @@ public abstract class PatternProviderMenuAdvancedMixin implements PatternProvide private void eap$initAdvancedSync_Protected(MenuType menuType, int id, Inventory playerInventory, PatternProviderLogicHost host, CallbackInfo ci) { try { var l = this.logic; - if (l instanceof AdvancedBlockingHolder holder) { + if (l instanceof IAdvancedBlocking holder) { this.eap$AdvancedBlocking = holder.eap$getAdvancedBlocking(); } } catch (Throwable t) { diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/menu/PatternProviderMenuDoublingMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/menu/PatternProviderMenuDoublingMixin.java index 3403b9d..c173633 100644 --- a/src/main/java/com/extendedae_plus/mixin/ae2/menu/PatternProviderMenuDoublingMixin.java +++ b/src/main/java/com/extendedae_plus/mixin/ae2/menu/PatternProviderMenuDoublingMixin.java @@ -5,8 +5,8 @@ import appeng.helpers.patternprovider.PatternProviderLogicHost; import appeng.menu.AEBaseMenu; import appeng.menu.guisync.GuiSync; import appeng.menu.implementations.PatternProviderMenu; -import com.extendedae_plus.api.PatternProviderMenuDoublingSync; -import com.extendedae_plus.api.SmartDoublingHolder; +import com.extendedae_plus.api.smartDoubling.IPatternProviderMenuDoublingSync; +import com.extendedae_plus.api.smartDoubling.ISmartDoublingHolder; import net.minecraft.world.entity.player.Inventory; import net.minecraft.world.inventory.MenuType; import org.spongepowered.asm.mixin.Final; @@ -20,7 +20,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import static com.extendedae_plus.util.ExtendedAELogger.LOGGER; @Mixin(PatternProviderMenu.class) -public abstract class PatternProviderMenuDoublingMixin implements PatternProviderMenuDoublingSync { +public abstract class PatternProviderMenuDoublingMixin implements IPatternProviderMenuDoublingSync { @Final @Shadow(remap = false) protected PatternProviderLogic logic; @@ -33,7 +33,7 @@ public abstract class PatternProviderMenuDoublingMixin implements PatternProvide private void eap$syncSmartDoubling(CallbackInfo ci) { if (!((AEBaseMenu) (Object) this).isClientSide()) { var l = this.logic; - if (l instanceof SmartDoublingHolder holder) { + if (l instanceof ISmartDoublingHolder holder) { this.eap$SmartDoubling = holder.eap$getSmartDoubling(); } } @@ -43,7 +43,7 @@ public abstract class PatternProviderMenuDoublingMixin implements PatternProvide private void eap$initSmartSync_Public(int id, Inventory playerInventory, PatternProviderLogicHost host, CallbackInfo ci) { try { var l = this.logic; - if (l instanceof SmartDoublingHolder holder) { + if (l instanceof ISmartDoublingHolder holder) { this.eap$SmartDoubling = holder.eap$getSmartDoubling(); } } catch (Throwable t) { @@ -55,7 +55,7 @@ public abstract class PatternProviderMenuDoublingMixin implements PatternProvide private void eap$initSmartSync_Protected(MenuType menuType, int id, Inventory playerInventory, PatternProviderLogicHost host, CallbackInfo ci) { try { var l = this.logic; - if (l instanceof SmartDoublingHolder holder) { + if (l instanceof ISmartDoublingHolder holder) { this.eap$SmartDoubling = holder.eap$getSmartDoubling(); } } catch (Throwable 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 c5ed6d7..01ef29c 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 @@ -7,7 +7,7 @@ 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.api.bridge.IUpgradableMenu; import com.extendedae_plus.compat.UpgradeSlotCompat; import net.minecraft.world.entity.player.Inventory; import net.minecraft.world.inventory.MenuType; diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/parts/AEBasePartClientSyncMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/parts/AEBasePartClientSyncMixin.java index b180e30..10b957b 100644 --- a/src/main/java/com/extendedae_plus/mixin/ae2/parts/AEBasePartClientSyncMixin.java +++ b/src/main/java/com/extendedae_plus/mixin/ae2/parts/AEBasePartClientSyncMixin.java @@ -1,7 +1,7 @@ package com.extendedae_plus.mixin.ae2.parts; import appeng.parts.AEBasePart; -import com.extendedae_plus.bridge.InterfaceWirelessLinkBridge; +import com.extendedae_plus.api.bridge.IInterfaceWirelessLinkBridge; import net.minecraft.network.FriendlyByteBuf; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; @@ -18,8 +18,8 @@ public class AEBasePartClientSyncMixin { @Inject(method = "writeToStream", at = @At("TAIL")) private void eap$writeWirelessState(FriendlyByteBuf data, CallbackInfo ci) { // 检查是否实现了无线链接桥接接口 - if (this instanceof InterfaceWirelessLinkBridge) { - InterfaceWirelessLinkBridge bridge = (InterfaceWirelessLinkBridge) this; + if (this instanceof IInterfaceWirelessLinkBridge) { + IInterfaceWirelessLinkBridge bridge = (IInterfaceWirelessLinkBridge) this; // 同步无线连接状态到客户端 boolean connected = false; try { @@ -44,8 +44,8 @@ public class AEBasePartClientSyncMixin { boolean connected = data.readBoolean(); // 检查是否实现了无线链接桥接接口 - if (this instanceof InterfaceWirelessLinkBridge) { - InterfaceWirelessLinkBridge bridge = (InterfaceWirelessLinkBridge) this; + if (this instanceof IInterfaceWirelessLinkBridge) { + IInterfaceWirelessLinkBridge bridge = (IInterfaceWirelessLinkBridge) this; try { // 更新客户端状态 bridge.eap$setClientWirelessState(connected); 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 b00c3e1..1ce2e5d 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 @@ -3,16 +3,14 @@ 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.api.bridge.IInterfaceWirelessLinkBridge; import com.extendedae_plus.init.ModItems; +import com.extendedae_plus.util.ExtendedAELogger; import com.extendedae_plus.wireless.WirelessSlaveLink; import com.extendedae_plus.wireless.endpoint.GenericNodeEndpointImpl; -import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.nbt.CompoundTag; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Unique; import org.spongepowered.asm.mixin.injection.At; @@ -23,7 +21,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; * 给 AE2 的 I/O 总线注入频道卡联动:在升级变更时读取频道并更新无线链接。 */ @Mixin(value = IOBusPart.class, remap = false) -public abstract class IOBusPartChannelCardMixin implements InterfaceWirelessLinkBridge, IUpgradeableObject { +public abstract class IOBusPartChannelCardMixin implements IInterfaceWirelessLinkBridge, IUpgradeableObject { @Unique private WirelessSlaveLink eap$link; diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/parts/automation/IOBusPartTickerChannelCardMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/parts/automation/IOBusPartTickerChannelCardMixin.java index d9ac8ba..a964e14 100644 --- a/src/main/java/com/extendedae_plus/mixin/ae2/parts/automation/IOBusPartTickerChannelCardMixin.java +++ b/src/main/java/com/extendedae_plus/mixin/ae2/parts/automation/IOBusPartTickerChannelCardMixin.java @@ -3,7 +3,7 @@ package com.extendedae_plus.mixin.ae2.parts.automation; import appeng.api.networking.IGridNode; import appeng.api.networking.ticking.TickRateModulation; import appeng.parts.automation.IOBusPart; -import com.extendedae_plus.bridge.InterfaceWirelessLinkBridge; +import com.extendedae_plus.api.bridge.IInterfaceWirelessLinkBridge; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; @@ -17,7 +17,7 @@ public abstract class IOBusPartTickerChannelCardMixin { @Inject(method = "tickingRequest", at = @At("TAIL")) private void eap$tickTail(IGridNode node, int ticksSinceLastCall, CallbackInfoReturnable cir) { - if (this instanceof InterfaceWirelessLinkBridge bridge) { + if (this instanceof IInterfaceWirelessLinkBridge bridge) { bridge.eap$updateWirelessLink(); } } diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/parts/storagebus/StorageBusPartChannelCardMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/parts/storagebus/StorageBusPartChannelCardMixin.java index 50e4a71..d1f4264 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 @@ -5,14 +5,13 @@ 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.api.bridge.IInterfaceWirelessLinkBridge; import com.extendedae_plus.init.ModItems; +import com.extendedae_plus.util.ExtendedAELogger; import com.extendedae_plus.wireless.WirelessSlaveLink; import com.extendedae_plus.wireless.endpoint.GenericNodeEndpointImpl; +import net.minecraft.nbt.CompoundTag; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Unique; import org.spongepowered.asm.mixin.injection.At; @@ -23,7 +22,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; * 给 AE2 的存储总线注入频道卡联动:在升级变更时读取频道并更新无线链接。 */ @Mixin(value = StorageBusPart.class, remap = false) -public abstract class StorageBusPartChannelCardMixin implements InterfaceWirelessLinkBridge, IUpgradeableObject { +public abstract class StorageBusPartChannelCardMixin implements IInterfaceWirelessLinkBridge, IUpgradeableObject { @Unique private WirelessSlaveLink eap$link; diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/parts/storagebus/StorageBusPartTickerChannelCardMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/parts/storagebus/StorageBusPartTickerChannelCardMixin.java index 202bc80..2227e45 100644 --- a/src/main/java/com/extendedae_plus/mixin/ae2/parts/storagebus/StorageBusPartTickerChannelCardMixin.java +++ b/src/main/java/com/extendedae_plus/mixin/ae2/parts/storagebus/StorageBusPartTickerChannelCardMixin.java @@ -3,7 +3,7 @@ package com.extendedae_plus.mixin.ae2.parts.storagebus; import appeng.api.networking.IGridNode; import appeng.api.networking.ticking.TickRateModulation; import appeng.parts.storagebus.StorageBusPart; -import com.extendedae_plus.bridge.InterfaceWirelessLinkBridge; +import com.extendedae_plus.api.bridge.IInterfaceWirelessLinkBridge; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; @@ -17,7 +17,7 @@ public abstract class StorageBusPartTickerChannelCardMixin { @Inject(method = "tickingRequest", at = @At("TAIL")) private void eap$tickTail(IGridNode node, int ticksSinceLastCall, CallbackInfoReturnable cir) { - if (this instanceof InterfaceWirelessLinkBridge bridge) { + if (this instanceof IInterfaceWirelessLinkBridge bridge) { bridge.eap$updateWirelessLink(); } } 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 f101fda..72101ae 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 @@ -5,7 +5,8 @@ import appeng.client.gui.implementations.PatternProviderScreen; import appeng.client.gui.style.ScreenStyle; import appeng.menu.SlotSemantics; import com.extendedae_plus.NewIcon; -import com.extendedae_plus.api.ExPatternButtonsAccessor; +import com.extendedae_plus.api.IExPatternButtonsAccessor; +import com.extendedae_plus.api.IExPatternPageAccessor; import com.extendedae_plus.config.ModConfig; import com.glodblock.github.extendedae.client.button.ActionEPPButton; import com.glodblock.github.extendedae.client.gui.GuiExPatternProvider; @@ -26,7 +27,7 @@ import java.lang.reflect.Method; import static com.extendedae_plus.util.ExtendedAELogger.LOGGER; @Mixin(GuiExPatternProvider.class) -public abstract class GuiExPatternProviderMixin extends PatternProviderScreen implements ExPatternButtonsAccessor, com.extendedae_plus.api.ExPatternPageAccessor { +public abstract class GuiExPatternProviderMixin extends PatternProviderScreen implements IExPatternButtonsAccessor, IExPatternPageAccessor { @Unique ScreenStyle eap$screenStyle; diff --git a/src/main/java/com/extendedae_plus/network/GlobalToggleProviderModesC2SPacket.java b/src/main/java/com/extendedae_plus/network/GlobalToggleProviderModesC2SPacket.java index c37d0bc..6d8ac8c 100644 --- a/src/main/java/com/extendedae_plus/network/GlobalToggleProviderModesC2SPacket.java +++ b/src/main/java/com/extendedae_plus/network/GlobalToggleProviderModesC2SPacket.java @@ -7,8 +7,8 @@ import appeng.blockentity.crafting.PatternProviderBlockEntity; import appeng.helpers.patternprovider.PatternProviderLogic; import appeng.helpers.patternprovider.PatternProviderLogicHost; import appeng.parts.crafting.PatternProviderPart; -import com.extendedae_plus.api.AdvancedBlockingHolder; -import com.extendedae_plus.api.SmartDoublingHolder; +import com.extendedae_plus.api.IAdvancedBlocking; +import com.extendedae_plus.api.smartDoubling.ISmartDoublingHolder; import com.extendedae_plus.content.controller.NetworkPatternControllerBlockEntity; import net.minecraft.core.BlockPos; import net.minecraft.network.FriendlyByteBuf; @@ -16,15 +16,15 @@ import net.minecraft.network.chat.Component; import net.minecraft.server.level.ServerPlayer; import net.minecraftforge.network.NetworkEvent; -import java.util.Set; import java.util.HashSet; +import java.util.Set; import java.util.function.Supplier; /** * C2S:全网批量切换样板供应器的三种模式: * - 阻挡模式(AE2 内置 BLOCKING_MODE 设置) - * - 高级阻挡模式(AdvancedBlockingHolder mixin) - * - 智能翻倍模式(SmartDoublingHolder mixin) + * - 高级阻挡模式(IAdvancedBlocking mixin) + * - 智能翻倍模式(ISmartDoublingHolder mixin) * * 负载为三个操作码(各1字节),分别对应:blocking、advancedBlocking、smartDoubling。 */ @@ -173,14 +173,14 @@ public class GlobalToggleProviderModesC2SPacket { } } // 2) 高级阻挡(mixin 接口) - if (msg.opAdvancedBlocking != Op.NOOP && logic instanceof AdvancedBlockingHolder adv) { + if (msg.opAdvancedBlocking != Op.NOOP && logic instanceof IAdvancedBlocking adv) { boolean current = adv.eap$getAdvancedBlocking(); boolean target = computeTarget(current, msg.opAdvancedBlocking); adv.eap$setAdvancedBlocking(target); changed = changed || (current != target); } // 3) 智能翻倍(mixin 接口) - if (msg.opSmartDoubling != Op.NOOP && logic instanceof SmartDoublingHolder sd) { + if (msg.opSmartDoubling != Op.NOOP && logic instanceof ISmartDoublingHolder sd) { boolean current = sd.eap$getSmartDoubling(); boolean target = computeTarget(current, msg.opSmartDoubling); sd.eap$setSmartDoubling(target); diff --git a/src/main/java/com/extendedae_plus/network/ToggleAdvancedBlockingC2SPacket.java b/src/main/java/com/extendedae_plus/network/ToggleAdvancedBlockingC2SPacket.java index 2711206..8a4f227 100644 --- a/src/main/java/com/extendedae_plus/network/ToggleAdvancedBlockingC2SPacket.java +++ b/src/main/java/com/extendedae_plus/network/ToggleAdvancedBlockingC2SPacket.java @@ -3,7 +3,7 @@ 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.api.IAdvancedBlocking; import com.extendedae_plus.mixin.advancedae.accessor.AdvPatternProviderMenuAdvancedAccessor; import com.extendedae_plus.mixin.ae2.accessor.PatternProviderMenuAdvancedAccessor; import net.minecraft.network.FriendlyByteBuf; @@ -36,7 +36,7 @@ public class ToggleAdvancedBlockingC2SPacket { if (containerMenu instanceof PatternProviderMenu menu) { var accessor = (PatternProviderMenuAdvancedAccessor) menu; var logic = accessor.eap$logic(); - if (logic instanceof AdvancedBlockingHolder holder) { + if (logic instanceof IAdvancedBlocking holder) { boolean current = holder.eap$getAdvancedBlocking(); boolean next = !current; holder.eap$setAdvancedBlocking(next); @@ -48,7 +48,7 @@ public class ToggleAdvancedBlockingC2SPacket { }else if (containerMenu instanceof AdvPatternProviderMenu menu){ var accessor = (AdvPatternProviderMenuAdvancedAccessor) menu; var logic = accessor.eap$logic(); - if (logic instanceof AdvancedBlockingHolder holder) { + if (logic instanceof IAdvancedBlocking holder) { boolean current = holder.eap$getAdvancedBlocking(); boolean next = !current; holder.eap$setAdvancedBlocking(next); diff --git a/src/main/java/com/extendedae_plus/network/ToggleSmartDoublingC2SPacket.java b/src/main/java/com/extendedae_plus/network/ToggleSmartDoublingC2SPacket.java index 88d832a..4792a92 100644 --- a/src/main/java/com/extendedae_plus/network/ToggleSmartDoublingC2SPacket.java +++ b/src/main/java/com/extendedae_plus/network/ToggleSmartDoublingC2SPacket.java @@ -1,7 +1,7 @@ package com.extendedae_plus.network; import appeng.menu.implementations.PatternProviderMenu; -import com.extendedae_plus.api.SmartDoublingHolder; +import com.extendedae_plus.api.smartDoubling.ISmartDoublingHolder; import com.extendedae_plus.mixin.advancedae.accessor.AdvPatternProviderMenuAdvancedAccessor; import com.extendedae_plus.mixin.ae2.accessor.PatternProviderMenuAdvancedAccessor; import net.minecraft.network.FriendlyByteBuf; @@ -33,7 +33,7 @@ public class ToggleSmartDoublingC2SPacket { if (containerMenu instanceof PatternProviderMenu menu) { var accessor = (PatternProviderMenuAdvancedAccessor) menu; var logic = accessor.eap$logic(); - if (logic instanceof SmartDoublingHolder holder) { + if (logic instanceof ISmartDoublingHolder holder) { boolean current = holder.eap$getSmartDoubling(); boolean next = !current; holder.eap$setSmartDoubling(next); @@ -42,7 +42,7 @@ public class ToggleSmartDoublingC2SPacket { }else if (containerMenu instanceof AdvPatternProviderMenu menu){ var accessor = (AdvPatternProviderMenuAdvancedAccessor) menu; var logic = accessor.eap$logic(); - if (logic instanceof SmartDoublingHolder holder) { + if (logic instanceof ISmartDoublingHolder holder) { boolean current = holder.eap$getSmartDoubling(); boolean next = !current; holder.eap$setSmartDoubling(next); diff --git a/src/main/java/com/extendedae_plus/util/PatternScaler.java b/src/main/java/com/extendedae_plus/util/PatternScaler.java index 11a454f..e14fce0 100644 --- a/src/main/java/com/extendedae_plus/util/PatternScaler.java +++ b/src/main/java/com/extendedae_plus/util/PatternScaler.java @@ -3,7 +3,7 @@ package com.extendedae_plus.util; import appeng.api.stacks.AEKey; import appeng.api.stacks.GenericStack; import appeng.crafting.pattern.AEProcessingPattern; -import com.extendedae_plus.api.SmartDoublingAwarePattern; +import com.extendedae_plus.api.smartDoubling.ISmartDoublingAwarePattern; import com.extendedae_plus.config.ModConfig; import com.extendedae_plus.content.ScaledProcessingPattern; @@ -16,7 +16,7 @@ public final class PatternScaler { if (target == null) throw new IllegalArgumentException("target"); // 双保险:若样板标记为不允许缩放,直接放弃缩放(返回 null 表示调用方应保持原样板) - if (base instanceof SmartDoublingAwarePattern aware && !aware.eap$allowScaling()) { + if (base instanceof ISmartDoublingAwarePattern aware && !aware.eap$allowScaling()) { return null; } From 5609777360f7e04e4a8b5f14a6bd4087eb42f904 Mon Sep 17 00:00:00 2001 From: C-H716 <1536152356@qq.com> Date: Sat, 27 Sep 2025 01:38:52 +0800 Subject: [PATCH 04/51] =?UTF-8?q?=E8=B0=83=E6=95=B4=E6=96=87=E6=A1=A3?= =?UTF-8?q?=E7=BB=93=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../crafting}/ScaledProcessingPattern.java | 2 +- .../{ => ae/client/gui}/NewIcon.java | 3 +- .../ae/menu/EntitySpeedTickerMenu.java | 4 +- .../ae/parts/EntitySpeedTickerPart.java | 4 +- .../ae/screen/EntitySpeedTickerScreen.java | 2 +- .../{ => ae}/wireless/IWirelessEndpoint.java | 2 +- .../{ => ae}/wireless/WirelessMasterLink.java | 2 +- .../wireless/WirelessMasterRegistry.java | 2 +- .../{ => ae}/wireless/WirelessSlaveLink.java | 2 +- .../endpoint/GenericNodeEndpointImpl.java | 4 +- .../endpoint/InterfaceNodeEndpointImpl.java | 4 +- .../WirelessTransceiverBlockEntity.java | 6 +- .../jade/WirelessTransceiverProvider.java | 4 +- ...ernProviderLogicContainsRedirectMixin.java | 2 +- .../CraftingServiceGetProvidersMixin.java | 2 +- .../autopattern/CraftingTreeNodeMixin.java | 2 +- .../autopattern/CraftingTreeProcessMixin.java | 6 +- ...ernProviderLogicContainsRedirectMixin.java | 2 +- .../ae2/client/gui/InterfaceScreenMixin.java | 10 +- .../PatternProviderLogicCompatMixin.java | 4 +- .../InterfaceLogicChannelCardMixin.java | 4 +- .../automation/IOBusPartChannelCardMixin.java | 4 +- .../StorageBusPartChannelCardMixin.java | 4 +- .../client/gui/GuiExPatternProviderMixin.java | 2 +- .../util/PatternProviderUIHelper.java | 127 ------------------ .../{ => entitySpeed}/ConfigParsingUtils.java | 2 +- .../util/{ => entitySpeed}/PowerUtils.java | 2 +- .../{ => smartDoubling}/PatternScaler.java | 4 +- .../RequestedAmountHolder.java | 2 +- 29 files changed, 46 insertions(+), 174 deletions(-) rename src/main/java/com/extendedae_plus/{content => ae/api/crafting}/ScaledProcessingPattern.java (99%) rename src/main/java/com/extendedae_plus/{ => ae/client/gui}/NewIcon.java (91%) rename src/main/java/com/extendedae_plus/{ => ae}/wireless/IWirelessEndpoint.java (94%) rename src/main/java/com/extendedae_plus/{ => ae}/wireless/WirelessMasterLink.java (97%) rename src/main/java/com/extendedae_plus/{ => ae}/wireless/WirelessMasterRegistry.java (98%) rename src/main/java/com/extendedae_plus/{ => ae}/wireless/WirelessSlaveLink.java (99%) rename src/main/java/com/extendedae_plus/{ => ae}/wireless/endpoint/GenericNodeEndpointImpl.java (93%) rename src/main/java/com/extendedae_plus/{ => ae}/wireless/endpoint/InterfaceNodeEndpointImpl.java (92%) delete mode 100644 src/main/java/com/extendedae_plus/util/PatternProviderUIHelper.java rename src/main/java/com/extendedae_plus/util/{ => entitySpeed}/ConfigParsingUtils.java (99%) rename src/main/java/com/extendedae_plus/util/{ => entitySpeed}/PowerUtils.java (98%) rename src/main/java/com/extendedae_plus/util/{ => smartDoubling}/PatternScaler.java (96%) rename src/main/java/com/extendedae_plus/util/{ => smartDoubling}/RequestedAmountHolder.java (95%) diff --git a/src/main/java/com/extendedae_plus/content/ScaledProcessingPattern.java b/src/main/java/com/extendedae_plus/ae/api/crafting/ScaledProcessingPattern.java similarity index 99% rename from src/main/java/com/extendedae_plus/content/ScaledProcessingPattern.java rename to src/main/java/com/extendedae_plus/ae/api/crafting/ScaledProcessingPattern.java index 0b672ff..ab8c924 100644 --- a/src/main/java/com/extendedae_plus/content/ScaledProcessingPattern.java +++ b/src/main/java/com/extendedae_plus/ae/api/crafting/ScaledProcessingPattern.java @@ -1,4 +1,4 @@ -package com.extendedae_plus.content; +package com.extendedae_plus.ae.api.crafting; import appeng.api.crafting.IPatternDetails; import appeng.api.stacks.AEItemKey; diff --git a/src/main/java/com/extendedae_plus/NewIcon.java b/src/main/java/com/extendedae_plus/ae/client/gui/NewIcon.java similarity index 91% rename from src/main/java/com/extendedae_plus/NewIcon.java rename to src/main/java/com/extendedae_plus/ae/client/gui/NewIcon.java index 8535a1f..db82062 100644 --- a/src/main/java/com/extendedae_plus/NewIcon.java +++ b/src/main/java/com/extendedae_plus/ae/client/gui/NewIcon.java @@ -1,6 +1,7 @@ -package com.extendedae_plus; +package com.extendedae_plus.ae.client.gui; import appeng.client.gui.style.Blitter; +import com.extendedae_plus.ExtendedAEPlus; import net.minecraft.resources.ResourceLocation; public class NewIcon { 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 753b97a..70f956b 100644 --- a/src/main/java/com/extendedae_plus/ae/menu/EntitySpeedTickerMenu.java +++ b/src/main/java/com/extendedae_plus/ae/menu/EntitySpeedTickerMenu.java @@ -9,8 +9,8 @@ import com.extendedae_plus.ae.screen.EntitySpeedTickerScreen; import com.extendedae_plus.config.ModConfig; 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 net.minecraft.client.Minecraft; import net.minecraft.world.entity.player.Inventory; import net.minecraft.world.level.block.entity.BlockEntity; 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 91fa27f..bfa7f86 100644 --- a/src/main/java/com/extendedae_plus/ae/parts/EntitySpeedTickerPart.java +++ b/src/main/java/com/extendedae_plus/ae/parts/EntitySpeedTickerPart.java @@ -22,8 +22,8 @@ import com.extendedae_plus.ae.menu.EntitySpeedTickerMenu; import com.extendedae_plus.config.ModConfig; 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 net.minecraft.network.chat.Component; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.InteractionHand; 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 0f6ae50..842ad03 100644 --- a/src/main/java/com/extendedae_plus/ae/screen/EntitySpeedTickerScreen.java +++ b/src/main/java/com/extendedae_plus/ae/screen/EntitySpeedTickerScreen.java @@ -11,7 +11,7 @@ import appeng.util.Platform; import com.extendedae_plus.ae.menu.EntitySpeedTickerMenu; import com.extendedae_plus.init.ModNetwork; import com.extendedae_plus.network.ToggleEntityTickerC2SPacket; -import com.extendedae_plus.util.PowerUtils; +import com.extendedae_plus.util.entitySpeed.PowerUtils; import net.minecraft.network.chat.Component; import net.minecraft.world.entity.player.Inventory; diff --git a/src/main/java/com/extendedae_plus/wireless/IWirelessEndpoint.java b/src/main/java/com/extendedae_plus/ae/wireless/IWirelessEndpoint.java similarity index 94% rename from src/main/java/com/extendedae_plus/wireless/IWirelessEndpoint.java rename to src/main/java/com/extendedae_plus/ae/wireless/IWirelessEndpoint.java index 15a991b..d21ebae 100644 --- a/src/main/java/com/extendedae_plus/wireless/IWirelessEndpoint.java +++ b/src/main/java/com/extendedae_plus/ae/wireless/IWirelessEndpoint.java @@ -1,4 +1,4 @@ -package com.extendedae_plus.wireless; +package com.extendedae_plus.ae.wireless; import appeng.api.networking.IGridNode; import net.minecraft.core.BlockPos; diff --git a/src/main/java/com/extendedae_plus/wireless/WirelessMasterLink.java b/src/main/java/com/extendedae_plus/ae/wireless/WirelessMasterLink.java similarity index 97% rename from src/main/java/com/extendedae_plus/wireless/WirelessMasterLink.java rename to src/main/java/com/extendedae_plus/ae/wireless/WirelessMasterLink.java index 4024b49..6d40cc2 100644 --- a/src/main/java/com/extendedae_plus/wireless/WirelessMasterLink.java +++ b/src/main/java/com/extendedae_plus/ae/wireless/WirelessMasterLink.java @@ -1,4 +1,4 @@ -package com.extendedae_plus.wireless; +package com.extendedae_plus.ae.wireless; import net.minecraft.server.level.ServerLevel; diff --git a/src/main/java/com/extendedae_plus/wireless/WirelessMasterRegistry.java b/src/main/java/com/extendedae_plus/ae/wireless/WirelessMasterRegistry.java similarity index 98% rename from src/main/java/com/extendedae_plus/wireless/WirelessMasterRegistry.java rename to src/main/java/com/extendedae_plus/ae/wireless/WirelessMasterRegistry.java index a5f35b8..6d411a8 100644 --- a/src/main/java/com/extendedae_plus/wireless/WirelessMasterRegistry.java +++ b/src/main/java/com/extendedae_plus/ae/wireless/WirelessMasterRegistry.java @@ -1,4 +1,4 @@ -package com.extendedae_plus.wireless; +package com.extendedae_plus.ae.wireless; import com.extendedae_plus.config.ModConfig; import net.minecraft.resources.ResourceKey; diff --git a/src/main/java/com/extendedae_plus/wireless/WirelessSlaveLink.java b/src/main/java/com/extendedae_plus/ae/wireless/WirelessSlaveLink.java similarity index 99% rename from src/main/java/com/extendedae_plus/wireless/WirelessSlaveLink.java rename to src/main/java/com/extendedae_plus/ae/wireless/WirelessSlaveLink.java index 84035fd..8196349 100644 --- a/src/main/java/com/extendedae_plus/wireless/WirelessSlaveLink.java +++ b/src/main/java/com/extendedae_plus/ae/wireless/WirelessSlaveLink.java @@ -1,4 +1,4 @@ -package com.extendedae_plus.wireless; +package com.extendedae_plus.ae.wireless; import appeng.api.networking.GridHelper; import appeng.api.networking.IGridNode; diff --git a/src/main/java/com/extendedae_plus/wireless/endpoint/GenericNodeEndpointImpl.java b/src/main/java/com/extendedae_plus/ae/wireless/endpoint/GenericNodeEndpointImpl.java similarity index 93% rename from src/main/java/com/extendedae_plus/wireless/endpoint/GenericNodeEndpointImpl.java rename to src/main/java/com/extendedae_plus/ae/wireless/endpoint/GenericNodeEndpointImpl.java index 84cd191..3653e12 100644 --- a/src/main/java/com/extendedae_plus/wireless/endpoint/GenericNodeEndpointImpl.java +++ b/src/main/java/com/extendedae_plus/ae/wireless/endpoint/GenericNodeEndpointImpl.java @@ -1,7 +1,7 @@ -package com.extendedae_plus.wireless.endpoint; +package com.extendedae_plus.ae.wireless.endpoint; import appeng.api.networking.IGridNode; -import com.extendedae_plus.wireless.IWirelessEndpoint; +import com.extendedae_plus.ae.wireless.IWirelessEndpoint; import net.minecraft.core.BlockPos; import net.minecraft.server.level.ServerLevel; import net.minecraft.world.level.Level; diff --git a/src/main/java/com/extendedae_plus/wireless/endpoint/InterfaceNodeEndpointImpl.java b/src/main/java/com/extendedae_plus/ae/wireless/endpoint/InterfaceNodeEndpointImpl.java similarity index 92% rename from src/main/java/com/extendedae_plus/wireless/endpoint/InterfaceNodeEndpointImpl.java rename to src/main/java/com/extendedae_plus/ae/wireless/endpoint/InterfaceNodeEndpointImpl.java index 6736f4c..f6dbfff 100644 --- a/src/main/java/com/extendedae_plus/wireless/endpoint/InterfaceNodeEndpointImpl.java +++ b/src/main/java/com/extendedae_plus/ae/wireless/endpoint/InterfaceNodeEndpointImpl.java @@ -1,8 +1,8 @@ -package com.extendedae_plus.wireless.endpoint; +package com.extendedae_plus.ae.wireless.endpoint; import appeng.api.networking.IGridNode; import appeng.helpers.InterfaceLogicHost; -import com.extendedae_plus.wireless.IWirelessEndpoint; +import com.extendedae_plus.ae.wireless.IWirelessEndpoint; import net.minecraft.core.BlockPos; import net.minecraft.server.level.ServerLevel; import net.minecraft.world.level.Level; 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 09b843d..6884308 100644 --- a/src/main/java/com/extendedae_plus/content/wireless/WirelessTransceiverBlockEntity.java +++ b/src/main/java/com/extendedae_plus/content/wireless/WirelessTransceiverBlockEntity.java @@ -3,11 +3,11 @@ package com.extendedae_plus.content.wireless; import appeng.api.networking.*; import appeng.api.util.AECableType; import appeng.blockentity.AEBaseBlockEntity; +import com.extendedae_plus.ae.wireless.IWirelessEndpoint; +import com.extendedae_plus.ae.wireless.WirelessMasterLink; +import com.extendedae_plus.ae.wireless.WirelessSlaveLink; import com.extendedae_plus.init.ModBlockEntities; import com.extendedae_plus.init.ModItems; -import com.extendedae_plus.wireless.IWirelessEndpoint; -import com.extendedae_plus.wireless.WirelessMasterLink; -import com.extendedae_plus.wireless.WirelessSlaveLink; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.nbt.CompoundTag; 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 7786e1c..d0529fc 100644 --- a/src/main/java/com/extendedae_plus/integration/jade/WirelessTransceiverProvider.java +++ b/src/main/java/com/extendedae_plus/integration/jade/WirelessTransceiverProvider.java @@ -2,9 +2,9 @@ package com.extendedae_plus.integration.jade; import appeng.api.networking.IGrid; import appeng.api.networking.IGridNode; +import com.extendedae_plus.ae.wireless.IWirelessEndpoint; +import com.extendedae_plus.ae.wireless.WirelessMasterRegistry; import com.extendedae_plus.content.wireless.WirelessTransceiverBlockEntity; -import com.extendedae_plus.wireless.IWirelessEndpoint; -import com.extendedae_plus.wireless.WirelessMasterRegistry; import net.minecraft.core.BlockPos; import net.minecraft.nbt.CompoundTag; import net.minecraft.resources.ResourceLocation; diff --git a/src/main/java/com/extendedae_plus/mixin/advancedae/AdvPatternProviderLogicContainsRedirectMixin.java b/src/main/java/com/extendedae_plus/mixin/advancedae/AdvPatternProviderLogicContainsRedirectMixin.java index 0773388..fb482b9 100644 --- a/src/main/java/com/extendedae_plus/mixin/advancedae/AdvPatternProviderLogicContainsRedirectMixin.java +++ b/src/main/java/com/extendedae_plus/mixin/advancedae/AdvPatternProviderLogicContainsRedirectMixin.java @@ -1,7 +1,7 @@ package com.extendedae_plus.mixin.advancedae; import appeng.api.crafting.IPatternDetails; -import com.extendedae_plus.content.ScaledProcessingPattern; +import com.extendedae_plus.ae.api.crafting.ScaledProcessingPattern; import net.pedroksl.advanced_ae.common.logic.AdvPatternProviderLogic; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/autopattern/CraftingServiceGetProvidersMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/autopattern/CraftingServiceGetProvidersMixin.java index ccc4812..1a508c2 100644 --- a/src/main/java/com/extendedae_plus/mixin/ae2/autopattern/CraftingServiceGetProvidersMixin.java +++ b/src/main/java/com/extendedae_plus/mixin/ae2/autopattern/CraftingServiceGetProvidersMixin.java @@ -2,7 +2,7 @@ package com.extendedae_plus.mixin.ae2.autopattern; import appeng.api.crafting.IPatternDetails; import appeng.me.service.CraftingService; -import com.extendedae_plus.content.ScaledProcessingPattern; +import com.extendedae_plus.ae.api.crafting.ScaledProcessingPattern; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.ModifyArg; 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 aa4f062..c37d9b0 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 @@ -3,7 +3,7 @@ package com.extendedae_plus.mixin.ae2.autopattern; import appeng.api.stacks.KeyCounter; import appeng.crafting.CraftingTreeNode; import appeng.crafting.inv.CraftingSimulationState; -import com.extendedae_plus.util.RequestedAmountHolder; +import com.extendedae_plus.util.smartDoubling.RequestedAmountHolder; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; 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 2bc2189..30a2c80 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 @@ -9,11 +9,11 @@ import appeng.crafting.CraftingTreeNode; import appeng.crafting.CraftingTreeProcess; import appeng.crafting.pattern.AEProcessingPattern; import appeng.me.service.CraftingService; +import com.extendedae_plus.ae.api.crafting.ScaledProcessingPattern; import com.extendedae_plus.api.smartDoubling.ISmartDoublingAwarePattern; import com.extendedae_plus.config.ModConfig; -import com.extendedae_plus.content.ScaledProcessingPattern; -import com.extendedae_plus.util.PatternScaler; -import com.extendedae_plus.util.RequestedAmountHolder; +import com.extendedae_plus.util.smartDoubling.PatternScaler; +import com.extendedae_plus.util.smartDoubling.RequestedAmountHolder; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.ModifyVariable; diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/autopattern/PatternProviderLogicContainsRedirectMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/autopattern/PatternProviderLogicContainsRedirectMixin.java index d6dc2a5..a1b6bf7 100644 --- a/src/main/java/com/extendedae_plus/mixin/ae2/autopattern/PatternProviderLogicContainsRedirectMixin.java +++ b/src/main/java/com/extendedae_plus/mixin/ae2/autopattern/PatternProviderLogicContainsRedirectMixin.java @@ -2,7 +2,7 @@ package com.extendedae_plus.mixin.ae2.autopattern; import appeng.api.crafting.IPatternDetails; import appeng.helpers.patternprovider.PatternProviderLogic; -import com.extendedae_plus.content.ScaledProcessingPattern; +import com.extendedae_plus.ae.api.crafting.ScaledProcessingPattern; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Redirect; 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 0c6f828..5bfd4f0 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 @@ -3,17 +3,15 @@ package com.extendedae_plus.mixin.ae2.client.gui; import appeng.client.gui.AEBaseScreen; import appeng.client.gui.implementations.InterfaceScreen; import appeng.menu.AEBaseMenu; -import com.extendedae_plus.NewIcon; -import com.extendedae_plus.init.ModNetwork; -import com.extendedae_plus.network.InterfaceAdjustConfigAmountC2SPacket; import appeng.menu.SlotSemantics; -import net.minecraft.world.inventory.Slot; +import com.extendedae_plus.ae.client.gui.NewIcon; +import com.extendedae_plus.init.ModNetwork; import com.extendedae_plus.mixin.accessor.AbstractContainerScreenAccessor; import com.extendedae_plus.mixin.accessor.ScreenAccessor; +import com.extendedae_plus.network.InterfaceAdjustConfigAmountC2SPacket; import com.glodblock.github.extendedae.client.button.ActionEPPButton; -import net.minecraft.client.gui.components.Tooltip; -import net.minecraft.network.chat.Component; import com.mojang.logging.LogUtils; +import net.minecraft.world.inventory.Slot; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Unique; import org.spongepowered.asm.mixin.injection.At; 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 bad667d..69e52af 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 @@ -8,11 +8,11 @@ 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.ae.wireless.WirelessSlaveLink; +import com.extendedae_plus.ae.wireless.endpoint.GenericNodeEndpointImpl; import com.extendedae_plus.api.bridge.IInterfaceWirelessLinkBridge; import com.extendedae_plus.compat.UpgradeSlotCompat; import com.extendedae_plus.init.ModItems; -import com.extendedae_plus.wireless.WirelessSlaveLink; -import com.extendedae_plus.wireless.endpoint.GenericNodeEndpointImpl; import net.minecraft.nbt.CompoundTag; import net.minecraft.world.item.ItemStack; import org.spongepowered.asm.mixin.Final; 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 4e9b52a..a5ea0e3 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 @@ -4,10 +4,10 @@ import appeng.api.upgrades.IUpgradeInventory; import appeng.helpers.InterfaceLogic; import appeng.helpers.InterfaceLogicHost; import com.extendedae_plus.ae.items.ChannelCardItem; +import com.extendedae_plus.ae.wireless.WirelessSlaveLink; +import com.extendedae_plus.ae.wireless.endpoint.InterfaceNodeEndpointImpl; import com.extendedae_plus.api.bridge.IInterfaceWirelessLinkBridge; import com.extendedae_plus.init.ModItems; -import com.extendedae_plus.wireless.WirelessSlaveLink; -import com.extendedae_plus.wireless.endpoint.InterfaceNodeEndpointImpl; import net.minecraft.world.item.ItemStack; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; 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 1ce2e5d..eee7501 100644 --- a/src/main/java/com/extendedae_plus/mixin/ae2/parts/automation/IOBusPartChannelCardMixin.java +++ b/src/main/java/com/extendedae_plus/mixin/ae2/parts/automation/IOBusPartChannelCardMixin.java @@ -5,11 +5,11 @@ import appeng.api.upgrades.IUpgradeInventory; import appeng.api.upgrades.IUpgradeableObject; import appeng.parts.automation.IOBusPart; import com.extendedae_plus.ae.items.ChannelCardItem; +import com.extendedae_plus.ae.wireless.WirelessSlaveLink; +import com.extendedae_plus.ae.wireless.endpoint.GenericNodeEndpointImpl; import com.extendedae_plus.api.bridge.IInterfaceWirelessLinkBridge; import com.extendedae_plus.init.ModItems; import com.extendedae_plus.util.ExtendedAELogger; -import com.extendedae_plus.wireless.WirelessSlaveLink; -import com.extendedae_plus.wireless.endpoint.GenericNodeEndpointImpl; import net.minecraft.nbt.CompoundTag; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Unique; 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 d1f4264..d54d757 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 @@ -6,11 +6,11 @@ import appeng.api.upgrades.IUpgradeInventory; import appeng.api.upgrades.IUpgradeableObject; import appeng.parts.storagebus.StorageBusPart; import com.extendedae_plus.ae.items.ChannelCardItem; +import com.extendedae_plus.ae.wireless.WirelessSlaveLink; +import com.extendedae_plus.ae.wireless.endpoint.GenericNodeEndpointImpl; import com.extendedae_plus.api.bridge.IInterfaceWirelessLinkBridge; import com.extendedae_plus.init.ModItems; import com.extendedae_plus.util.ExtendedAELogger; -import com.extendedae_plus.wireless.WirelessSlaveLink; -import com.extendedae_plus.wireless.endpoint.GenericNodeEndpointImpl; import net.minecraft.nbt.CompoundTag; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Unique; 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 72101ae..6cc4b9d 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 @@ -4,7 +4,7 @@ import appeng.client.gui.Icon; import appeng.client.gui.implementations.PatternProviderScreen; import appeng.client.gui.style.ScreenStyle; import appeng.menu.SlotSemantics; -import com.extendedae_plus.NewIcon; +import com.extendedae_plus.ae.client.gui.NewIcon; import com.extendedae_plus.api.IExPatternButtonsAccessor; import com.extendedae_plus.api.IExPatternPageAccessor; import com.extendedae_plus.config.ModConfig; diff --git a/src/main/java/com/extendedae_plus/util/PatternProviderUIHelper.java b/src/main/java/com/extendedae_plus/util/PatternProviderUIHelper.java deleted file mode 100644 index 435a5a9..0000000 --- a/src/main/java/com/extendedae_plus/util/PatternProviderUIHelper.java +++ /dev/null @@ -1,127 +0,0 @@ -package com.extendedae_plus.util; - -import appeng.client.gui.implementations.PatternProviderScreen; -import appeng.helpers.patternprovider.PatternProviderLogic; -import appeng.menu.implementations.PatternProviderMenu; -import net.minecraft.client.Minecraft; -import net.minecraft.client.gui.screens.Screen; - -/** - * 样板供应器UI辅助工具类 - * 用于在样板供应器界面中获取PatternProviderLogic对象 - */ -public class PatternProviderUIHelper { - - /** - * 获取当前打开的样板供应器的PatternProviderLogic - * - * @return PatternProviderLogic对象,如果当前没有打开样板供应器界面则返回null - */ - public static PatternProviderLogic getCurrentPatternProvider() { - Minecraft mc = Minecraft.getInstance(); - Screen currentScreen = mc.screen; - - if (currentScreen instanceof PatternProviderScreen patternScreen) { - PatternProviderMenu menu = (PatternProviderMenu) patternScreen.getMenu(); - - // 通过反射安全地访问protected字段 - try { - var logicField = PatternProviderMenu.class.getDeclaredField("logic"); - logicField.setAccessible(true); - return (PatternProviderLogic) logicField.get(menu); - } catch (Exception e) { - // 如果反射失败,返回null - return null; - } - } - - return null; - } - - /** - * 检查当前是否打开了样板供应器界面 - * - * @return 如果当前打开的是样板供应器界面则返回true - */ - public static boolean isPatternProviderScreenOpen() { - Minecraft mc = Minecraft.getInstance(); - return mc.screen instanceof PatternProviderScreen; - } - - /** - * 获取当前样板供应器界面的Screen对象 - * - * @return PatternProviderScreen对象,如果当前没有打开样板供应器界面则返回null - */ - public static PatternProviderScreen getCurrentPatternProviderScreen() { - Minecraft mc = Minecraft.getInstance(); - Screen currentScreen = mc.screen; - - if (currentScreen instanceof PatternProviderScreen patternScreen) { - return patternScreen; - } - - return null; - } - - /** - * 在当前样板供应器中执行样板数量倍增 - * - * @param multiplier 倍数(必须大于0) - * @return 缩放操作结果,如果当前没有打开样板供应器界面则返回null - */ - public static PatternProviderDataUtil.PatternScalingResult multiplyCurrentPatternAmounts(double multiplier) { - PatternProviderLogic patternProvider = getCurrentPatternProvider(); - if (patternProvider == null) { - return null; - } - - return PatternProviderDataUtil.multiplyPatternAmounts(patternProvider, multiplier); - } - - /** - * ExtendedAE风格的样板复制倍增 - * 提供更好的错误处理和恢复机制 - * - * @param multiplier 倍数(必须大于0) - * @return 缩放操作结果,如果当前没有打开样板供应器界面则返回null - */ - public static PatternProviderDataUtil.PatternScalingResult duplicateCurrentPatternAmountsExtendedAEStyle(double multiplier) { - PatternProviderLogic patternProvider = getCurrentPatternProvider(); - if (patternProvider == null) { - return null; - } - - return PatternProviderDataUtil.duplicatePatternAmountsExtendedAEStyle(patternProvider, multiplier); - } - - /** - * 在当前样板供应器中执行样板数量倍除 - * - * @param divisor 除数(必须大于0) - * @return 缩放操作结果,如果当前没有打开样板供应器界面则返回null - */ - public static PatternProviderDataUtil.PatternScalingResult divideCurrentPatternAmounts(double divisor) { - PatternProviderLogic patternProvider = getCurrentPatternProvider(); - if (patternProvider == null) { - return null; - } - - return PatternProviderDataUtil.dividePatternAmounts(patternProvider, divisor); - } - - /** - * 预览当前样板供应器的缩放效果 - * - * @param scaleFactor 缩放因子 - * @return 预览结果列表,如果当前没有打开样板供应器界面则返回空列表 - */ - public static java.util.List previewCurrentPatternScaling(double scaleFactor) { - PatternProviderLogic patternProvider = getCurrentPatternProvider(); - if (patternProvider == null) { - return new java.util.ArrayList<>(); - } - - return PatternProviderDataUtil.previewPatternScaling(patternProvider, scaleFactor); - } -} 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 99% rename from src/main/java/com/extendedae_plus/util/ConfigParsingUtils.java rename to src/main/java/com/extendedae_plus/util/entitySpeed/ConfigParsingUtils.java index f74da49..44c9713 100644 --- a/src/main/java/com/extendedae_plus/util/ConfigParsingUtils.java +++ b/src/main/java/com/extendedae_plus/util/entitySpeed/ConfigParsingUtils.java @@ -1,4 +1,4 @@ -package com.extendedae_plus.util; +package com.extendedae_plus.util.entitySpeed; import java.util.ArrayList; import java.util.Collections; diff --git a/src/main/java/com/extendedae_plus/util/PowerUtils.java b/src/main/java/com/extendedae_plus/util/entitySpeed/PowerUtils.java similarity index 98% rename from src/main/java/com/extendedae_plus/util/PowerUtils.java rename to src/main/java/com/extendedae_plus/util/entitySpeed/PowerUtils.java index e8c2776..529b40f 100644 --- a/src/main/java/com/extendedae_plus/util/PowerUtils.java +++ b/src/main/java/com/extendedae_plus/util/entitySpeed/PowerUtils.java @@ -1,4 +1,4 @@ -package com.extendedae_plus.util; +package com.extendedae_plus.util.entitySpeed; import appeng.api.upgrades.IUpgradeInventory; import com.extendedae_plus.ae.definitions.upgrades.EntitySpeedCardItem; diff --git a/src/main/java/com/extendedae_plus/util/PatternScaler.java b/src/main/java/com/extendedae_plus/util/smartDoubling/PatternScaler.java similarity index 96% rename from src/main/java/com/extendedae_plus/util/PatternScaler.java rename to src/main/java/com/extendedae_plus/util/smartDoubling/PatternScaler.java index e14fce0..3a2eccf 100644 --- a/src/main/java/com/extendedae_plus/util/PatternScaler.java +++ b/src/main/java/com/extendedae_plus/util/smartDoubling/PatternScaler.java @@ -1,11 +1,11 @@ -package com.extendedae_plus.util; +package com.extendedae_plus.util.smartDoubling; import appeng.api.stacks.AEKey; import appeng.api.stacks.GenericStack; import appeng.crafting.pattern.AEProcessingPattern; +import com.extendedae_plus.ae.api.crafting.ScaledProcessingPattern; import com.extendedae_plus.api.smartDoubling.ISmartDoublingAwarePattern; import com.extendedae_plus.config.ModConfig; -import com.extendedae_plus.content.ScaledProcessingPattern; public final class PatternScaler { private PatternScaler() { diff --git a/src/main/java/com/extendedae_plus/util/RequestedAmountHolder.java b/src/main/java/com/extendedae_plus/util/smartDoubling/RequestedAmountHolder.java similarity index 95% rename from src/main/java/com/extendedae_plus/util/RequestedAmountHolder.java rename to src/main/java/com/extendedae_plus/util/smartDoubling/RequestedAmountHolder.java index e70187a..005f919 100644 --- a/src/main/java/com/extendedae_plus/util/RequestedAmountHolder.java +++ b/src/main/java/com/extendedae_plus/util/smartDoubling/RequestedAmountHolder.java @@ -1,4 +1,4 @@ -package com.extendedae_plus.util; +package com.extendedae_plus.util.smartDoubling; import java.util.ArrayDeque; import java.util.Deque; From fa9d9910d65ce7fbaa452e9f04e8bb0ba9c31857 Mon Sep 17 00:00:00 2001 From: C-H716 <1536152356@qq.com> Date: Sat, 27 Sep 2025 01:47:29 +0800 Subject: [PATCH 05/51] =?UTF-8?q?=E8=B0=83=E6=95=B4network=E7=BB=93?= =?UTF-8?q?=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/extendedae_plus/client/InputEvents.java | 4 ++-- .../client/screen/GlobalProviderModesScreen.java | 2 +- .../java/com/extendedae_plus/init/ModNetwork.java | 5 +++++ .../client/gui/AdvPatternProviderScreenMixin.java | 4 ++-- .../gui/SmallAdvPatternProviderScreenMixin.java | 4 ++-- .../mixin/ae2/client/gui/AEBaseScreenMixin.java | 4 ++-- .../mixin/ae2/client/gui/InterfaceScreenMixin.java | 2 +- .../client/gui/PatternEncodingTermScreenMixin.java | 3 ++- .../ae2/client/gui/PatternProviderScreenMixin.java | 4 ++-- .../client/gui/GuiExPatternTerminalMixin.java | 2 +- .../CraftingMonitorJumpC2SPacket.java | 4 +--- .../CraftingMonitorOpenProviderC2SPacket.java | 4 +++- .../{ => crafting}/OpenCraftFromJeiC2SPacket.java | 2 +- .../InterfaceAdjustConfigAmountC2SPacket.java | 5 ++--- .../AdvancedBlockingSyncS2CPacket.java | 2 +- .../GlobalToggleProviderModesC2SPacket.java | 2 +- .../{ => provider}/OpenProviderUiC2SPacket.java | 14 +++++++------- .../{ => provider}/ProvidersListS2CPacket.java | 2 +- .../RequestProvidersListC2SPacket.java | 2 +- .../{ => provider}/SetProviderPageS2CPacket.java | 2 +- .../ToggleAdvancedBlockingC2SPacket.java | 2 +- .../ToggleSmartDoublingC2SPacket.java | 2 +- 22 files changed, 41 insertions(+), 36 deletions(-) rename src/main/java/com/extendedae_plus/network/{ => crafting}/CraftingMonitorJumpC2SPacket.java (98%) rename src/main/java/com/extendedae_plus/network/{ => crafting}/CraftingMonitorOpenProviderC2SPacket.java (97%) rename src/main/java/com/extendedae_plus/network/{ => crafting}/OpenCraftFromJeiC2SPacket.java (98%) rename src/main/java/com/extendedae_plus/network/{ => meInterface}/InterfaceAdjustConfigAmountC2SPacket.java (98%) rename src/main/java/com/extendedae_plus/network/{ => provider}/AdvancedBlockingSyncS2CPacket.java (96%) rename src/main/java/com/extendedae_plus/network/{ => provider}/GlobalToggleProviderModesC2SPacket.java (99%) rename src/main/java/com/extendedae_plus/network/{ => provider}/OpenProviderUiC2SPacket.java (99%) rename src/main/java/com/extendedae_plus/network/{ => provider}/ProvidersListS2CPacket.java (97%) rename src/main/java/com/extendedae_plus/network/{ => provider}/RequestProvidersListC2SPacket.java (98%) rename src/main/java/com/extendedae_plus/network/{ => provider}/SetProviderPageS2CPacket.java (97%) rename src/main/java/com/extendedae_plus/network/{ => provider}/ToggleAdvancedBlockingC2SPacket.java (98%) rename src/main/java/com/extendedae_plus/network/{ => provider}/ToggleSmartDoublingC2SPacket.java (98%) diff --git a/src/main/java/com/extendedae_plus/client/InputEvents.java b/src/main/java/com/extendedae_plus/client/InputEvents.java index 6a921a4..d5766a9 100644 --- a/src/main/java/com/extendedae_plus/client/InputEvents.java +++ b/src/main/java/com/extendedae_plus/client/InputEvents.java @@ -8,8 +8,8 @@ import com.extendedae_plus.init.ModNetwork; 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.extendedae_plus.network.crafting.OpenCraftFromJeiC2SPacket; import com.glodblock.github.extendedae.client.gui.GuiExPatternTerminal; import mezz.jei.api.ingredients.ITypedIngredient; import net.minecraft.client.Minecraft; @@ -17,8 +17,8 @@ import net.minecraft.client.gui.screens.Screen; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.client.event.ScreenEvent; import net.minecraftforge.eventbus.api.SubscribeEvent; -import net.minecraftforge.fml.common.Mod; import net.minecraftforge.fml.ModList; +import net.minecraftforge.fml.common.Mod; import org.lwjgl.glfw.GLFW; import java.util.Optional; diff --git a/src/main/java/com/extendedae_plus/client/screen/GlobalProviderModesScreen.java b/src/main/java/com/extendedae_plus/client/screen/GlobalProviderModesScreen.java index 49c36a8..87aff55 100644 --- a/src/main/java/com/extendedae_plus/client/screen/GlobalProviderModesScreen.java +++ b/src/main/java/com/extendedae_plus/client/screen/GlobalProviderModesScreen.java @@ -2,7 +2,7 @@ package com.extendedae_plus.client.screen; import com.extendedae_plus.init.ModNetwork; import com.extendedae_plus.menu.NetworkPatternControllerMenu; -import com.extendedae_plus.network.GlobalToggleProviderModesC2SPacket; +import com.extendedae_plus.network.provider.GlobalToggleProviderModesC2SPacket; import net.minecraft.client.gui.components.Button; import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen; import net.minecraft.network.chat.Component; diff --git a/src/main/java/com/extendedae_plus/init/ModNetwork.java b/src/main/java/com/extendedae_plus/init/ModNetwork.java index e281d81..666991a 100644 --- a/src/main/java/com/extendedae_plus/init/ModNetwork.java +++ b/src/main/java/com/extendedae_plus/init/ModNetwork.java @@ -2,6 +2,11 @@ package com.extendedae_plus.init; import com.extendedae_plus.ExtendedAEPlus; import com.extendedae_plus.network.*; +import com.extendedae_plus.network.crafting.CraftingMonitorJumpC2SPacket; +import com.extendedae_plus.network.crafting.CraftingMonitorOpenProviderC2SPacket; +import com.extendedae_plus.network.crafting.OpenCraftFromJeiC2SPacket; +import com.extendedae_plus.network.meInterface.InterfaceAdjustConfigAmountC2SPacket; +import com.extendedae_plus.network.provider.*; import net.minecraft.resources.ResourceLocation; import net.minecraftforge.network.NetworkDirection; import net.minecraftforge.network.NetworkRegistry; 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 8a64727..813c164 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 @@ -9,8 +9,8 @@ import com.extendedae_plus.api.IExPatternButtonsAccessor; import com.extendedae_plus.api.IPatternProviderMenuAdvancedSync; import com.extendedae_plus.api.smartDoubling.IPatternProviderMenuDoublingSync; import com.extendedae_plus.init.ModNetwork; -import com.extendedae_plus.network.ToggleAdvancedBlockingC2SPacket; -import com.extendedae_plus.network.ToggleSmartDoublingC2SPacket; +import com.extendedae_plus.network.provider.ToggleAdvancedBlockingC2SPacket; +import com.extendedae_plus.network.provider.ToggleSmartDoublingC2SPacket; import com.glodblock.github.extendedae.client.gui.GuiExPatternProvider; import net.minecraft.network.chat.Component; import net.minecraft.world.entity.player.Inventory; 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 index f48a5da..233a610 100644 --- 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 @@ -9,8 +9,8 @@ import com.extendedae_plus.api.IExPatternButtonsAccessor; import com.extendedae_plus.api.IPatternProviderMenuAdvancedSync; import com.extendedae_plus.api.smartDoubling.IPatternProviderMenuDoublingSync; import com.extendedae_plus.init.ModNetwork; -import com.extendedae_plus.network.ToggleAdvancedBlockingC2SPacket; -import com.extendedae_plus.network.ToggleSmartDoublingC2SPacket; +import com.extendedae_plus.network.provider.ToggleAdvancedBlockingC2SPacket; +import com.extendedae_plus.network.provider.ToggleSmartDoublingC2SPacket; import com.glodblock.github.extendedae.client.gui.GuiExPatternProvider; import net.minecraft.network.chat.Component; import net.minecraft.world.entity.player.Inventory; 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 f4feef1..749e00d 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 @@ -16,8 +16,8 @@ import appeng.menu.slot.AppEngSlot; import com.extendedae_plus.api.IExPatternPageAccessor; import com.extendedae_plus.content.ClientPatternHighlightStore; import com.extendedae_plus.init.ModNetwork; -import com.extendedae_plus.network.CraftingMonitorJumpC2SPacket; -import com.extendedae_plus.network.CraftingMonitorOpenProviderC2SPacket; +import com.extendedae_plus.network.crafting.CraftingMonitorJumpC2SPacket; +import com.extendedae_plus.network.crafting.CraftingMonitorOpenProviderC2SPacket; 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/mixin/ae2/client/gui/InterfaceScreenMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/client/gui/InterfaceScreenMixin.java index 5bfd4f0..78fbd03 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 @@ -8,7 +8,7 @@ import com.extendedae_plus.ae.client.gui.NewIcon; import com.extendedae_plus.init.ModNetwork; import com.extendedae_plus.mixin.accessor.AbstractContainerScreenAccessor; import com.extendedae_plus.mixin.accessor.ScreenAccessor; -import com.extendedae_plus.network.InterfaceAdjustConfigAmountC2SPacket; +import com.extendedae_plus.network.meInterface.InterfaceAdjustConfigAmountC2SPacket; import com.glodblock.github.extendedae.client.button.ActionEPPButton; import com.mojang.logging.LogUtils; import net.minecraft.world.inventory.Slot; diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/client/gui/PatternEncodingTermScreenMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/client/gui/PatternEncodingTermScreenMixin.java index 23c262f..eaa7a16 100644 --- a/src/main/java/com/extendedae_plus/mixin/ae2/client/gui/PatternEncodingTermScreenMixin.java +++ b/src/main/java/com/extendedae_plus/mixin/ae2/client/gui/PatternEncodingTermScreenMixin.java @@ -11,6 +11,7 @@ import com.extendedae_plus.init.ModNetwork; import com.extendedae_plus.mixin.accessor.AbstractContainerScreenAccessor; import com.extendedae_plus.mixin.accessor.ScreenAccessor; import com.extendedae_plus.mixin.ae2.accessor.AEBaseScreenAccessor; +import com.extendedae_plus.network.provider.RequestProvidersListC2SPacket; import net.minecraft.client.gui.GuiGraphics; import net.minecraft.client.gui.components.Tooltip; import net.minecraft.client.renderer.Rect2i; @@ -41,7 +42,7 @@ public abstract class PatternEncodingTermScreenMixin { // 复用已存在的按钮实例,避免重复创建 if (eap$uploadBtn == null) { eap$uploadBtn = new IconButton(btn -> ModNetwork.CHANNEL - .sendToServer(new com.extendedae_plus.network.RequestProvidersListC2SPacket())) { + .sendToServer(new RequestProvidersListC2SPacket())) { private final float eap$scale = 0.75f; // 约 12x12 @Override diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/client/gui/PatternProviderScreenMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/client/gui/PatternProviderScreenMixin.java index 0a5278a..a47eb31 100644 --- a/src/main/java/com/extendedae_plus/mixin/ae2/client/gui/PatternProviderScreenMixin.java +++ b/src/main/java/com/extendedae_plus/mixin/ae2/client/gui/PatternProviderScreenMixin.java @@ -11,8 +11,8 @@ import com.extendedae_plus.api.IExPatternButtonsAccessor; import com.extendedae_plus.api.IPatternProviderMenuAdvancedSync; import com.extendedae_plus.api.smartDoubling.IPatternProviderMenuDoublingSync; import com.extendedae_plus.init.ModNetwork; -import com.extendedae_plus.network.ToggleAdvancedBlockingC2SPacket; -import com.extendedae_plus.network.ToggleSmartDoublingC2SPacket; +import com.extendedae_plus.network.provider.ToggleAdvancedBlockingC2SPacket; +import com.extendedae_plus.network.provider.ToggleSmartDoublingC2SPacket; import com.glodblock.github.extendedae.client.gui.GuiExPatternProvider; import net.minecraft.network.chat.Component; import net.minecraft.world.entity.player.Inventory; diff --git a/src/main/java/com/extendedae_plus/mixin/extendedae/client/gui/GuiExPatternTerminalMixin.java b/src/main/java/com/extendedae_plus/mixin/extendedae/client/gui/GuiExPatternTerminalMixin.java index 143aef9..af13e76 100644 --- a/src/main/java/com/extendedae_plus/mixin/extendedae/client/gui/GuiExPatternTerminalMixin.java +++ b/src/main/java/com/extendedae_plus/mixin/extendedae/client/gui/GuiExPatternTerminalMixin.java @@ -11,7 +11,7 @@ import appeng.menu.AEBaseMenu; import com.extendedae_plus.config.ModConfig; import com.extendedae_plus.init.ModNetwork; import com.extendedae_plus.mixin.extendedae.accessor.GuiExPatternTerminalAccessor; -import com.extendedae_plus.network.OpenProviderUiC2SPacket; +import com.extendedae_plus.network.provider.OpenProviderUiC2SPacket; import com.extendedae_plus.util.GuiUtil; import com.glodblock.github.extendedae.client.gui.GuiExPatternTerminal; import net.minecraft.client.Minecraft; diff --git a/src/main/java/com/extendedae_plus/network/CraftingMonitorJumpC2SPacket.java b/src/main/java/com/extendedae_plus/network/crafting/CraftingMonitorJumpC2SPacket.java similarity index 98% rename from src/main/java/com/extendedae_plus/network/CraftingMonitorJumpC2SPacket.java rename to src/main/java/com/extendedae_plus/network/crafting/CraftingMonitorJumpC2SPacket.java index eacd863..bb709e3 100644 --- a/src/main/java/com/extendedae_plus/network/CraftingMonitorJumpC2SPacket.java +++ b/src/main/java/com/extendedae_plus/network/crafting/CraftingMonitorJumpC2SPacket.java @@ -1,8 +1,7 @@ -package com.extendedae_plus.network; +package com.extendedae_plus.network.crafting; import appeng.api.crafting.IPatternDetails; import appeng.api.networking.IGrid; -import appeng.api.networking.crafting.ICraftingProvider; import appeng.api.networking.security.IActionHost; import appeng.api.stacks.AEKey; import appeng.helpers.patternprovider.PatternProviderLogic; @@ -18,7 +17,6 @@ import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.InteractionHand; import net.minecraft.world.InteractionResult; import net.minecraft.world.MenuProvider; -import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.phys.BlockHitResult; import net.minecraft.world.phys.Vec3; import net.minecraftforge.network.NetworkEvent; diff --git a/src/main/java/com/extendedae_plus/network/CraftingMonitorOpenProviderC2SPacket.java b/src/main/java/com/extendedae_plus/network/crafting/CraftingMonitorOpenProviderC2SPacket.java similarity index 97% rename from src/main/java/com/extendedae_plus/network/CraftingMonitorOpenProviderC2SPacket.java rename to src/main/java/com/extendedae_plus/network/crafting/CraftingMonitorOpenProviderC2SPacket.java index 91ddfa0..c962b77 100644 --- a/src/main/java/com/extendedae_plus/network/CraftingMonitorOpenProviderC2SPacket.java +++ b/src/main/java/com/extendedae_plus/network/crafting/CraftingMonitorOpenProviderC2SPacket.java @@ -1,4 +1,4 @@ -package com.extendedae_plus.network; +package com.extendedae_plus.network.crafting; import appeng.api.crafting.IPatternDetails; import appeng.api.networking.IGrid; @@ -13,6 +13,8 @@ import appeng.menu.me.crafting.CraftingCPUMenu; import appeng.parts.AEBasePart; import com.extendedae_plus.init.ModNetwork; import com.extendedae_plus.mixin.ae2.accessor.PatternProviderLogicAccessor; +import com.extendedae_plus.network.SetPatternHighlightS2CPacket; +import com.extendedae_plus.network.provider.SetProviderPageS2CPacket; import com.extendedae_plus.util.PatternProviderDataUtil; import com.glodblock.github.extendedae.util.FCClientUtil; import com.glodblock.github.glodium.util.GlodUtil; diff --git a/src/main/java/com/extendedae_plus/network/OpenCraftFromJeiC2SPacket.java b/src/main/java/com/extendedae_plus/network/crafting/OpenCraftFromJeiC2SPacket.java similarity index 98% rename from src/main/java/com/extendedae_plus/network/OpenCraftFromJeiC2SPacket.java rename to src/main/java/com/extendedae_plus/network/crafting/OpenCraftFromJeiC2SPacket.java index 5a9a33d..ac4f48e 100644 --- a/src/main/java/com/extendedae_plus/network/OpenCraftFromJeiC2SPacket.java +++ b/src/main/java/com/extendedae_plus/network/crafting/OpenCraftFromJeiC2SPacket.java @@ -1,4 +1,4 @@ -package com.extendedae_plus.network; +package com.extendedae_plus.network.crafting; import appeng.api.networking.IGrid; import appeng.api.stacks.AEKey; diff --git a/src/main/java/com/extendedae_plus/network/InterfaceAdjustConfigAmountC2SPacket.java b/src/main/java/com/extendedae_plus/network/meInterface/InterfaceAdjustConfigAmountC2SPacket.java similarity index 98% rename from src/main/java/com/extendedae_plus/network/InterfaceAdjustConfigAmountC2SPacket.java rename to src/main/java/com/extendedae_plus/network/meInterface/InterfaceAdjustConfigAmountC2SPacket.java index 8d899ed..f563ebb 100644 --- a/src/main/java/com/extendedae_plus/network/InterfaceAdjustConfigAmountC2SPacket.java +++ b/src/main/java/com/extendedae_plus/network/meInterface/InterfaceAdjustConfigAmountC2SPacket.java @@ -1,8 +1,7 @@ -package com.extendedae_plus.network; +package com.extendedae_plus.network.meInterface; -import appeng.menu.implementations.InterfaceMenu; -import appeng.menu.SlotSemantics; import appeng.api.stacks.GenericStack; +import appeng.menu.implementations.InterfaceMenu; import net.minecraft.network.FriendlyByteBuf; import net.minecraft.server.level.ServerPlayer; import net.minecraftforge.network.NetworkEvent; diff --git a/src/main/java/com/extendedae_plus/network/AdvancedBlockingSyncS2CPacket.java b/src/main/java/com/extendedae_plus/network/provider/AdvancedBlockingSyncS2CPacket.java similarity index 96% rename from src/main/java/com/extendedae_plus/network/AdvancedBlockingSyncS2CPacket.java rename to src/main/java/com/extendedae_plus/network/provider/AdvancedBlockingSyncS2CPacket.java index 357d0f3..eccbd30 100644 --- a/src/main/java/com/extendedae_plus/network/AdvancedBlockingSyncS2CPacket.java +++ b/src/main/java/com/extendedae_plus/network/provider/AdvancedBlockingSyncS2CPacket.java @@ -1,4 +1,4 @@ -package com.extendedae_plus.network; +package com.extendedae_plus.network.provider; import com.extendedae_plus.client.ClientAdvancedBlockingState; import net.minecraft.network.FriendlyByteBuf; diff --git a/src/main/java/com/extendedae_plus/network/GlobalToggleProviderModesC2SPacket.java b/src/main/java/com/extendedae_plus/network/provider/GlobalToggleProviderModesC2SPacket.java similarity index 99% rename from src/main/java/com/extendedae_plus/network/GlobalToggleProviderModesC2SPacket.java rename to src/main/java/com/extendedae_plus/network/provider/GlobalToggleProviderModesC2SPacket.java index 6d8ac8c..a07dc5d 100644 --- a/src/main/java/com/extendedae_plus/network/GlobalToggleProviderModesC2SPacket.java +++ b/src/main/java/com/extendedae_plus/network/provider/GlobalToggleProviderModesC2SPacket.java @@ -1,4 +1,4 @@ -package com.extendedae_plus.network; +package com.extendedae_plus.network.provider; import appeng.api.config.Settings; import appeng.api.config.YesNo; diff --git a/src/main/java/com/extendedae_plus/network/OpenProviderUiC2SPacket.java b/src/main/java/com/extendedae_plus/network/provider/OpenProviderUiC2SPacket.java similarity index 99% rename from src/main/java/com/extendedae_plus/network/OpenProviderUiC2SPacket.java rename to src/main/java/com/extendedae_plus/network/provider/OpenProviderUiC2SPacket.java index 8a02170..098c095 100644 --- a/src/main/java/com/extendedae_plus/network/OpenProviderUiC2SPacket.java +++ b/src/main/java/com/extendedae_plus/network/provider/OpenProviderUiC2SPacket.java @@ -1,20 +1,20 @@ -package com.extendedae_plus.network; +package com.extendedae_plus.network.provider; import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; import net.minecraft.core.registries.Registries; import net.minecraft.network.FriendlyByteBuf; -import net.minecraft.core.Direction; -import net.minecraft.world.InteractionHand; -import net.minecraft.world.phys.BlockHitResult; -import net.minecraft.world.phys.Vec3; -import net.minecraft.world.InteractionResult; import net.minecraft.resources.ResourceKey; import net.minecraft.resources.ResourceLocation; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.InteractionResult; import net.minecraft.world.MenuProvider; -import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.phys.BlockHitResult; +import net.minecraft.world.phys.Vec3; import net.minecraftforge.network.NetworkEvent; import net.minecraftforge.network.NetworkHooks; diff --git a/src/main/java/com/extendedae_plus/network/ProvidersListS2CPacket.java b/src/main/java/com/extendedae_plus/network/provider/ProvidersListS2CPacket.java similarity index 97% rename from src/main/java/com/extendedae_plus/network/ProvidersListS2CPacket.java rename to src/main/java/com/extendedae_plus/network/provider/ProvidersListS2CPacket.java index 87d3227..02689dc 100644 --- a/src/main/java/com/extendedae_plus/network/ProvidersListS2CPacket.java +++ b/src/main/java/com/extendedae_plus/network/provider/ProvidersListS2CPacket.java @@ -1,4 +1,4 @@ -package com.extendedae_plus.network; +package com.extendedae_plus.network.provider; import com.extendedae_plus.client.ui.ProviderSelectScreen; import net.minecraft.client.Minecraft; diff --git a/src/main/java/com/extendedae_plus/network/RequestProvidersListC2SPacket.java b/src/main/java/com/extendedae_plus/network/provider/RequestProvidersListC2SPacket.java similarity index 98% rename from src/main/java/com/extendedae_plus/network/RequestProvidersListC2SPacket.java rename to src/main/java/com/extendedae_plus/network/provider/RequestProvidersListC2SPacket.java index d9a0cc3..919ba09 100644 --- a/src/main/java/com/extendedae_plus/network/RequestProvidersListC2SPacket.java +++ b/src/main/java/com/extendedae_plus/network/provider/RequestProvidersListC2SPacket.java @@ -1,4 +1,4 @@ -package com.extendedae_plus.network; +package com.extendedae_plus.network.provider; import appeng.helpers.patternprovider.PatternContainer; import appeng.menu.implementations.PatternAccessTermMenu; diff --git a/src/main/java/com/extendedae_plus/network/SetProviderPageS2CPacket.java b/src/main/java/com/extendedae_plus/network/provider/SetProviderPageS2CPacket.java similarity index 97% rename from src/main/java/com/extendedae_plus/network/SetProviderPageS2CPacket.java rename to src/main/java/com/extendedae_plus/network/provider/SetProviderPageS2CPacket.java index 1c57644..5bfd936 100644 --- a/src/main/java/com/extendedae_plus/network/SetProviderPageS2CPacket.java +++ b/src/main/java/com/extendedae_plus/network/provider/SetProviderPageS2CPacket.java @@ -1,4 +1,4 @@ -package com.extendedae_plus.network; +package com.extendedae_plus.network.provider; import appeng.menu.SlotSemantics; import com.glodblock.github.extendedae.client.gui.GuiExPatternProvider; diff --git a/src/main/java/com/extendedae_plus/network/ToggleAdvancedBlockingC2SPacket.java b/src/main/java/com/extendedae_plus/network/provider/ToggleAdvancedBlockingC2SPacket.java similarity index 98% rename from src/main/java/com/extendedae_plus/network/ToggleAdvancedBlockingC2SPacket.java rename to src/main/java/com/extendedae_plus/network/provider/ToggleAdvancedBlockingC2SPacket.java index 8a4f227..c971d73 100644 --- a/src/main/java/com/extendedae_plus/network/ToggleAdvancedBlockingC2SPacket.java +++ b/src/main/java/com/extendedae_plus/network/provider/ToggleAdvancedBlockingC2SPacket.java @@ -1,4 +1,4 @@ -package com.extendedae_plus.network; +package com.extendedae_plus.network.provider; import appeng.api.config.Settings; import appeng.api.config.YesNo; diff --git a/src/main/java/com/extendedae_plus/network/ToggleSmartDoublingC2SPacket.java b/src/main/java/com/extendedae_plus/network/provider/ToggleSmartDoublingC2SPacket.java similarity index 98% rename from src/main/java/com/extendedae_plus/network/ToggleSmartDoublingC2SPacket.java rename to src/main/java/com/extendedae_plus/network/provider/ToggleSmartDoublingC2SPacket.java index 4792a92..abba57b 100644 --- a/src/main/java/com/extendedae_plus/network/ToggleSmartDoublingC2SPacket.java +++ b/src/main/java/com/extendedae_plus/network/provider/ToggleSmartDoublingC2SPacket.java @@ -1,4 +1,4 @@ -package com.extendedae_plus.network; +package com.extendedae_plus.network.provider; import appeng.menu.implementations.PatternProviderMenu; import com.extendedae_plus.api.smartDoubling.ISmartDoublingHolder; From 6b502aaadaa29627794aac82106ee5b72ea15264 Mon Sep 17 00:00:00 2001 From: C-H716 <1536152356@qq.com> Date: Sat, 27 Sep 2025 01:53:52 +0800 Subject: [PATCH 06/51] =?UTF-8?q?=E8=B0=83=E6=95=B4=E7=BB=93=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/extendedae_plus/ExtendedAEPlus.java | 2 +- .../{ => util}/command/InfinityDiskGiveCommand.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) rename src/main/java/com/extendedae_plus/{ => util}/command/InfinityDiskGiveCommand.java (98%) diff --git a/src/main/java/com/extendedae_plus/ExtendedAEPlus.java b/src/main/java/com/extendedae_plus/ExtendedAEPlus.java index 037a65a..011f78a 100644 --- a/src/main/java/com/extendedae_plus/ExtendedAEPlus.java +++ b/src/main/java/com/extendedae_plus/ExtendedAEPlus.java @@ -4,10 +4,10 @@ import appeng.api.storage.StorageCells; import appeng.menu.locator.MenuLocators; import com.extendedae_plus.ae.api.storage.InfinityBigIntegerCellHandler; import com.extendedae_plus.client.ClientRegistrar; -import com.extendedae_plus.command.InfinityDiskGiveCommand; import com.extendedae_plus.config.ModConfig; import com.extendedae_plus.init.*; import com.extendedae_plus.menu.locator.CuriosItemLocator; +import com.extendedae_plus.util.command.InfinityDiskGiveCommand; import com.extendedae_plus.util.storage.InfinityStorageManager; import net.minecraft.resources.ResourceLocation; import net.minecraftforge.api.distmarker.Dist; diff --git a/src/main/java/com/extendedae_plus/command/InfinityDiskGiveCommand.java b/src/main/java/com/extendedae_plus/util/command/InfinityDiskGiveCommand.java similarity index 98% rename from src/main/java/com/extendedae_plus/command/InfinityDiskGiveCommand.java rename to src/main/java/com/extendedae_plus/util/command/InfinityDiskGiveCommand.java index 67305f3..399eca1 100644 --- a/src/main/java/com/extendedae_plus/command/InfinityDiskGiveCommand.java +++ b/src/main/java/com/extendedae_plus/util/command/InfinityDiskGiveCommand.java @@ -1,4 +1,4 @@ -package com.extendedae_plus.command; +package com.extendedae_plus.util.command; import com.extendedae_plus.ExtendedAEPlus; import com.extendedae_plus.ae.items.InfinityBigIntegerCellItem; From 52cc600fb80fca962a7c11e56ddfac913d46154f Mon Sep 17 00:00:00 2001 From: C-H716 <1536152356@qq.com> Date: Sat, 27 Sep 2025 14:16:24 +0800 Subject: [PATCH 07/51] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E5=AE=9E?= =?UTF-8?q?=E4=BD=93=E5=8A=A0=E9=80=9F=E5=99=A8=E5=AF=B9appflux=E5=AD=98?= =?UTF-8?q?=E5=82=A8=E7=94=B5=E9=87=8F=E7=9A=84=E6=89=A3=E9=99=A4=E6=94=AF?= =?UTF-8?q?=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 1 + .../ae/parts/EntitySpeedTickerPart.java | 55 +++++++++++++++---- .../util/FluxEnergyHelper.java | 41 ++++++++++++++ 3 files changed, 86 insertions(+), 11 deletions(-) create mode 100644 src/main/java/com/extendedae_plus/util/FluxEnergyHelper.java diff --git a/build.gradle b/build.gradle index 4b8c05f..2d61a88 100644 --- a/build.gradle +++ b/build.gradle @@ -71,6 +71,7 @@ dependencies { modImplementation "curse.maven:glodium-957920:${glodium_version}" //extendedAE modImplementation files('libs/ExtendedAE-1.20-1.4.2-forge.jar') + modRuntimeOnly fileTree(dir: 'libs', includes: ['*.jar']) //ae2 modImplementation "appeng:appliedenergistics2-forge:${ae2_version}" 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 bfa7f86..6604f80 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; @@ -35,10 +38,12 @@ 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.minecraftforge.fml.ModList; import net.minecraftforge.registries.ForgeRegistries; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.lang.reflect.Method; import java.util.List; /** @@ -254,21 +259,49 @@ public class EntitySpeedTickerPart extends UpgradeablePart implements IGridTicka } /** - * 提取网络能量并更新状态。 - * @param requiredPower 所需能量 - * @return 是否成功提取 + * 提取网络能量并更新状态,优先从 AE2 网络提取 AE 能量,不足时从磁盘提取 FE 能量。 + * @param requiredPower 所需能量(AE 单位) + * @return 是否成功提取足够能量 */ private boolean extractPower(double requiredPower) { - var energyService = getMainNode().getGrid().getEnergyService(); + IEnergyService energyService = getMainNode().getGrid().getEnergyService(); + MEStorage storage = getMainNode().getGrid().getStorageService().getInventory(); + IActionSource source = IActionSource.ofMachine(this); + + // 优先尝试提取 AE 能量 double simulated = energyService.extractAEPower(requiredPower, Actionable.SIMULATE, PowerMultiplier.CONFIG); - if (simulated < requiredPower) { - updateNetworkEnergySufficient(false); - return false; + if (simulated >= requiredPower) { + double extracted = energyService.extractAEPower(requiredPower, Actionable.MODULATE, PowerMultiplier.CONFIG); + boolean sufficient = extracted >= requiredPower; + updateNetworkEnergySufficient(sufficient); + return sufficient; } - double extracted = energyService.extractAEPower(requiredPower, Actionable.MODULATE, PowerMultiplier.CONFIG); - boolean sufficient = extracted >= requiredPower; - updateNetworkEnergySufficient(sufficient); - return sufficient; + updateNetworkEnergySufficient(false); + + // AE 能量不足,尝试从磁盘提取 FE 能量(如果 Applied Flux 存在) + if (ModList.get().isLoaded("appflux")) { + 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; } /** 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 From 2e30d4892529be63fb5071c183c0ba141b565b37 Mon Sep 17 00:00:00 2001 From: C-H716 <1536152356@qq.com> Date: Sat, 27 Sep 2025 16:46:42 +0800 Subject: [PATCH 08/51] =?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 | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/extendedae_plus/ae/api/storage/InfinityBigIntegerCellInventory.java b/src/main/java/com/extendedae_plus/ae/api/storage/InfinityBigIntegerCellInventory.java index efcce92..9c69845 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 @@ -326,8 +326,11 @@ public class InfinityBigIntegerCellInventory implements StorageCell { if (amount == 0){ return 0; } - // 不允许存储无限单元自身 - if (what instanceof AEItemKey itemKey && itemKey.getItem() instanceof InfinityBigIntegerCellItem) { + // 不允许存储有物品的无限单元 + if (what instanceof AEItemKey itemKey && + itemKey.getItem() instanceof InfinityBigIntegerCellItem && + itemKey.hasTag() + ) { return 0; } From be48565e75605d9220b765eb85595ed1afafe5fa Mon Sep 17 00:00:00 2001 From: C-H716 <1536152356@qq.com> Date: Sat, 27 Sep 2025 19:42:17 +0800 Subject: [PATCH 09/51] =?UTF-8?q?=E5=88=A0=E9=99=A4=E6=97=A5=E5=BF=97?= =?UTF-8?q?=EF=BC=8C=E8=B0=83=E6=95=B4=E5=B0=8F=E9=83=A8=E5=88=86mixin?= =?UTF-8?q?=E7=B1=BB=EF=BC=8C=E5=8E=BB=E6=8E=89=E6=B2=A1=E7=94=A8=E7=9A=84?= =?UTF-8?q?Accessor?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../compat/UpgradeSlotCompat.java | 17 +----- .../mixin/MixinConditions.java | 2 - .../ae2/accessor/AEBaseScreenInvoker.java | 10 ---- .../PatternProviderLogicAccessor.java | 6 +++ ...ernProviderLogicPatternInputsAccessor.java | 14 ----- .../PatternProviderLogicPatternsAccessor.java | 14 ----- ....java => PatternProviderMenuAccessor.java} | 2 +- .../ae2/client/gui/AEBaseScreenMixin.java | 33 ++---------- .../ae2/client/gui/InterfaceScreenMixin.java | 18 ++----- .../client/gui/PatternProviderCloseMixin.java | 3 +- .../PatternProviderScreenUpgradesMixin.java | 54 ------------------- .../PatternProviderLogicDoublingMixin.java | 6 +-- .../ToggleAdvancedBlockingC2SPacket.java | 4 +- .../ToggleSmartDoublingC2SPacket.java | 4 +- .../resources/extendedae_plus.mixins.json | 25 ++++----- 15 files changed, 35 insertions(+), 177 deletions(-) delete mode 100644 src/main/java/com/extendedae_plus/mixin/ae2/accessor/AEBaseScreenInvoker.java delete mode 100644 src/main/java/com/extendedae_plus/mixin/ae2/accessor/PatternProviderLogicPatternInputsAccessor.java delete mode 100644 src/main/java/com/extendedae_plus/mixin/ae2/accessor/PatternProviderLogicPatternsAccessor.java rename src/main/java/com/extendedae_plus/mixin/ae2/accessor/{PatternProviderMenuAdvancedAccessor.java => PatternProviderMenuAccessor.java} (87%) delete mode 100644 src/main/java/com/extendedae_plus/mixin/ae2/client/gui/PatternProviderScreenUpgradesMixin.java diff --git a/src/main/java/com/extendedae_plus/compat/UpgradeSlotCompat.java b/src/main/java/com/extendedae_plus/compat/UpgradeSlotCompat.java index 9768050..c78c7c7 100644 --- a/src/main/java/com/extendedae_plus/compat/UpgradeSlotCompat.java +++ b/src/main/java/com/extendedae_plus/compat/UpgradeSlotCompat.java @@ -2,7 +2,6 @@ package com.extendedae_plus.compat; import appeng.api.upgrades.IUpgradeInventory; import appeng.api.upgrades.IUpgradeableObject; -import appeng.api.upgrades.UpgradeInventories; import appeng.client.gui.style.ScreenStyle; import appeng.client.gui.widgets.ToolboxPanel; import appeng.client.gui.widgets.UpgradesPanel; @@ -41,15 +40,7 @@ public class UpgradeSlotCompat { */ public static boolean shouldEnableUpgradeSlots() { boolean appfluxExists = isAppfluxPresent(); - ExtendedAELogger.LOGGER.info("ExtendedAE-appflux模组检测: {}", appfluxExists ? "存在" : "不存在"); - - if (appfluxExists) { - ExtendedAELogger.LOGGER.info("检测到ExtendedAE-appflux模组,跳过我们的升级卡槽功能"); - return false; - } else { - ExtendedAELogger.LOGGER.info("未检测到ExtendedAE-appflux模组,启用我们的升级卡槽功能"); - return true; - } + return !appfluxExists; } /** @@ -100,8 +91,6 @@ public class UpgradeSlotCompat { if (menu instanceof IUpgradeableMenuCompat compatMenu) { compatMenu.setCompatToolbox(toolbox); } - - ExtendedAELogger.LOGGER.debug("成功为PatternProviderMenu初始化升级功能"); return true; } } catch (Exception e) { @@ -147,8 +136,6 @@ public class UpgradeSlotCompat { ToolboxPanel toolboxPanel = new ToolboxPanel(style, toolbox.getName()); addMethod.invoke(widgets, "toolbox", toolboxPanel); } - - ExtendedAELogger.LOGGER.debug("成功为PatternProviderScreen添加升级面板"); return true; } catch (NoSuchMethodException e) { // 尝试其他可能的方法签名 @@ -173,8 +160,6 @@ public class UpgradeSlotCompat { ToolboxPanel toolboxPanel = new ToolboxPanel(style, toolbox.getName()); putMethod.invoke(widgets, "toolbox", toolboxPanel); } - - ExtendedAELogger.LOGGER.debug("成功为PatternProviderScreen添加升级面板(使用put方法)"); return true; } catch (Exception e2) { ExtendedAELogger.LOGGER.error("反射调用widgets方法失败", e2); diff --git a/src/main/java/com/extendedae_plus/mixin/MixinConditions.java b/src/main/java/com/extendedae_plus/mixin/MixinConditions.java index 595bf1c..dbf7799 100644 --- a/src/main/java/com/extendedae_plus/mixin/MixinConditions.java +++ b/src/main/java/com/extendedae_plus/mixin/MixinConditions.java @@ -1,6 +1,5 @@ package com.extendedae_plus.mixin; -import net.minecraftforge.fml.ModList; import org.spongepowered.asm.mixin.extensibility.IMixinConfigPlugin; import org.spongepowered.asm.mixin.extensibility.IMixinInfo; @@ -27,7 +26,6 @@ public class MixinConditions implements IMixinConfigPlugin { public boolean shouldApplyMixin(String targetClassName, String mixinClassName) { // 对于升级相关的Mixin,检查appflux是否存在 if (mixinClassName.contains("PatternProviderMenuUpgradesMixin") || - mixinClassName.contains("PatternProviderScreenUpgradesMixin") || mixinClassName.contains("PatternProviderLogicUpgradesMixin") || mixinClassName.contains("PatternProviderLogicHostUpgradesMixin")) { diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/accessor/AEBaseScreenInvoker.java b/src/main/java/com/extendedae_plus/mixin/ae2/accessor/AEBaseScreenInvoker.java deleted file mode 100644 index 0ec30f9..0000000 --- a/src/main/java/com/extendedae_plus/mixin/ae2/accessor/AEBaseScreenInvoker.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.extendedae_plus.mixin.ae2.accessor; - -import appeng.client.gui.AEBaseScreen; -import appeng.menu.AEBaseMenu; -import org.spongepowered.asm.mixin.Mixin; - -@Mixin(AEBaseScreen.class) -public interface AEBaseScreenInvoker { - // 空接口:避免在 AEBaseScreen 上声明不存在方法的 Invoker 导致编译错误 -} diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/accessor/PatternProviderLogicAccessor.java b/src/main/java/com/extendedae_plus/mixin/ae2/accessor/PatternProviderLogicAccessor.java index 6155a13..eaf4672 100644 --- a/src/main/java/com/extendedae_plus/mixin/ae2/accessor/PatternProviderLogicAccessor.java +++ b/src/main/java/com/extendedae_plus/mixin/ae2/accessor/PatternProviderLogicAccessor.java @@ -1,11 +1,14 @@ package com.extendedae_plus.mixin.ae2.accessor; +import appeng.api.crafting.IPatternDetails; import appeng.api.networking.IManagedGridNode; import appeng.helpers.patternprovider.PatternProviderLogic; import appeng.helpers.patternprovider.PatternProviderLogicHost; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.gen.Accessor; +import java.util.List; + @Mixin(PatternProviderLogic.class) public interface PatternProviderLogicAccessor { @Accessor(value = "host", remap = false) @@ -13,4 +16,7 @@ public interface PatternProviderLogicAccessor { @Accessor(value = "mainNode", remap = false) IManagedGridNode eap$mainNode(); + + @Accessor(value = "patterns" , remap = false) + List eap$patterns(); } diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/accessor/PatternProviderLogicPatternInputsAccessor.java b/src/main/java/com/extendedae_plus/mixin/ae2/accessor/PatternProviderLogicPatternInputsAccessor.java deleted file mode 100644 index 3f62fd4..0000000 --- a/src/main/java/com/extendedae_plus/mixin/ae2/accessor/PatternProviderLogicPatternInputsAccessor.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.extendedae_plus.mixin.ae2.accessor; - -import appeng.api.stacks.AEKey; -import appeng.helpers.patternprovider.PatternProviderLogic; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.gen.Accessor; - -import java.util.Set; - -@Mixin(PatternProviderLogic.class) -public interface PatternProviderLogicPatternInputsAccessor { - @Accessor(value = "patternInputs",remap = false) - Set eap$patternInputs(); -} diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/accessor/PatternProviderLogicPatternsAccessor.java b/src/main/java/com/extendedae_plus/mixin/ae2/accessor/PatternProviderLogicPatternsAccessor.java deleted file mode 100644 index c1471e3..0000000 --- a/src/main/java/com/extendedae_plus/mixin/ae2/accessor/PatternProviderLogicPatternsAccessor.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.extendedae_plus.mixin.ae2.accessor; - -import appeng.api.crafting.IPatternDetails; -import appeng.helpers.patternprovider.PatternProviderLogic; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.gen.Accessor; - -import java.util.List; - -@Mixin(value = PatternProviderLogic.class, remap = false) -public interface PatternProviderLogicPatternsAccessor { - @Accessor("patterns") - List eap$patterns(); -} diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/accessor/PatternProviderMenuAdvancedAccessor.java b/src/main/java/com/extendedae_plus/mixin/ae2/accessor/PatternProviderMenuAccessor.java similarity index 87% rename from src/main/java/com/extendedae_plus/mixin/ae2/accessor/PatternProviderMenuAdvancedAccessor.java rename to src/main/java/com/extendedae_plus/mixin/ae2/accessor/PatternProviderMenuAccessor.java index 18ca7b7..a1d296f 100644 --- a/src/main/java/com/extendedae_plus/mixin/ae2/accessor/PatternProviderMenuAdvancedAccessor.java +++ b/src/main/java/com/extendedae_plus/mixin/ae2/accessor/PatternProviderMenuAccessor.java @@ -6,7 +6,7 @@ import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.gen.Accessor; @Mixin(PatternProviderMenu.class) -public interface PatternProviderMenuAdvancedAccessor { +public interface PatternProviderMenuAccessor { @Accessor(value = "logic", remap = false) PatternProviderLogic eap$logic(); } 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 749e00d..df7e08a 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 @@ -16,11 +16,11 @@ import appeng.menu.slot.AppEngSlot; import com.extendedae_plus.api.IExPatternPageAccessor; import com.extendedae_plus.content.ClientPatternHighlightStore; import com.extendedae_plus.init.ModNetwork; +import com.extendedae_plus.mixin.ae2.accessor.AEBaseScreenAccessor; import com.extendedae_plus.network.crafting.CraftingMonitorJumpC2SPacket; import com.extendedae_plus.network.crafting.CraftingMonitorOpenProviderC2SPacket; import com.extendedae_plus.util.GuiUtil; import com.glodblock.github.extendedae.client.gui.GuiExPatternProvider; -import com.mojang.logging.LogUtils; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.Font; import net.minecraft.client.gui.GuiGraphics; @@ -40,17 +40,6 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; @Mixin(AEBaseScreen.class) public abstract class AEBaseScreenMixin { - @Unique - private ScreenStyle eap$getStyle(Object self) { - try { - var f = self.getClass().getDeclaredField("style"); - f.setAccessible(true); - Object v = f.get(self); - if (v instanceof ScreenStyle s) return s; - } catch (Throwable ignored) {} - return null; - } - /** * 在 AEBaseScreen 的 mouseClicked 入口拦截 CraftingCPUScreen 的 Shift+左键, * 读取鼠标下的 AEKey 并发送 CraftingMonitorJumpC2SPacket。 @@ -75,14 +64,9 @@ public abstract class AEBaseScreenMixin { if (key == null) { return; } - // Debug: 标记一次发送 - try { - LogUtils.getLogger().info("EAP: Send CraftingMonitorJumpC2SPacket: {}", key); - } catch (Throwable ignored2) {} ModNetwork.CHANNEL.sendToServer(new CraftingMonitorJumpC2SPacket(key)); cir.setReturnValue(true); - } catch (Throwable ignored) { - } + } catch (Throwable ignored) {} } /** @@ -109,14 +93,9 @@ public abstract class AEBaseScreenMixin { if (key == null) { return; } - // Debug: 标记一次发送(打开供应器UI) - try { - LogUtils.getLogger().info("EAP: Send CraftingMonitorOpenProviderC2SPacket: {}", key); - } catch (Throwable ignored2) {} ModNetwork.CHANNEL.sendToServer(new CraftingMonitorOpenProviderC2SPacket(key)); cir.setReturnValue(true); - } catch (Throwable ignored) { - } + } catch (Throwable ignored) {} } @Unique @@ -289,7 +268,7 @@ public abstract class AEBaseScreenMixin { String pageText = "第" + cur + "页" + "/" + max + "页"; - ScreenStyle style = eap$getStyle(self); + ScreenStyle style = ((AEBaseScreenAccessor) this).eap$getStyle(); int color = 0xFFFFFFFF; if (style != null) { try { @@ -307,11 +286,9 @@ public abstract class AEBaseScreenMixin { guiGraphics.drawString(font, pageText, lineWidth + padding, 0, color, false); guiGraphics.pose().popPose(); } - } catch (Throwable ignored) { - } + } catch (Throwable ignored) {} } - @Shadow(remap = false) protected void setTextContent(String id, Component content) {}; 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 78fbd03..5bb6ec1 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 @@ -4,13 +4,13 @@ import appeng.client.gui.AEBaseScreen; import appeng.client.gui.implementations.InterfaceScreen; import appeng.menu.AEBaseMenu; import appeng.menu.SlotSemantics; +import appeng.menu.implementations.InterfaceMenu; import com.extendedae_plus.ae.client.gui.NewIcon; import com.extendedae_plus.init.ModNetwork; import com.extendedae_plus.mixin.accessor.AbstractContainerScreenAccessor; import com.extendedae_plus.mixin.accessor.ScreenAccessor; import com.extendedae_plus.network.meInterface.InterfaceAdjustConfigAmountC2SPacket; import com.glodblock.github.extendedae.client.button.ActionEPPButton; -import com.mojang.logging.LogUtils; import net.minecraft.world.inventory.Slot; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Unique; @@ -46,7 +46,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) -> { @@ -108,7 +107,6 @@ public abstract class InterfaceScreenMixin { // 初次定位 eap$relayoutButtons(); - try { LogUtils.getLogger().info("[EAP][InterfaceMixin] buttons added and laid out."); } catch (Throwable ignored) {} } @Inject(method = "containerTick", at = @At("TAIL")) @@ -121,32 +119,26 @@ public abstract class InterfaceScreenMixin { if (eap$divideBy2Button != null && !accessor.eap$getRenderables().contains(eap$divideBy2Button)) { accessor.eap$getRenderables().add(eap$divideBy2Button); accessor.eap$getChildren().add(eap$divideBy2Button); - try { LogUtils.getLogger().info("[EAP][InterfaceMixin] re-added divide2 button to renderables."); } catch (Throwable ignored) {} } if (eap$x2Button != null && !accessor.eap$getRenderables().contains(eap$x2Button)) { accessor.eap$getRenderables().add(eap$x2Button); accessor.eap$getChildren().add(eap$x2Button); - try { LogUtils.getLogger().info("[EAP][InterfaceMixin] re-added x2 button to renderables."); } catch (Throwable ignored) {} } if (eap$divideBy5Button != null && !accessor.eap$getRenderables().contains(eap$divideBy5Button)) { accessor.eap$getRenderables().add(eap$divideBy5Button); accessor.eap$getChildren().add(eap$divideBy5Button); - try { LogUtils.getLogger().info("[EAP][InterfaceMixin] re-added divide5 button to renderables."); } catch (Throwable ignored) {} } if (eap$x5Button != null && !accessor.eap$getRenderables().contains(eap$x5Button)) { accessor.eap$getRenderables().add(eap$x5Button); accessor.eap$getChildren().add(eap$x5Button); - try { LogUtils.getLogger().info("[EAP][InterfaceMixin] re-added x5 button to renderables."); } catch (Throwable ignored) {} } if (eap$divideBy10Button != null && !accessor.eap$getRenderables().contains(eap$divideBy10Button)) { accessor.eap$getRenderables().add(eap$divideBy10Button); accessor.eap$getChildren().add(eap$divideBy10Button); - try { LogUtils.getLogger().info("[EAP][InterfaceMixin] re-added divide10 button to renderables."); } catch (Throwable ignored) {} } if (eap$x10Button != null && !accessor.eap$getRenderables().contains(eap$x10Button)) { accessor.eap$getRenderables().add(eap$x10Button); accessor.eap$getChildren().add(eap$x10Button); - try { LogUtils.getLogger().info("[EAP][InterfaceMixin] re-added x10 button to renderables."); } catch (Throwable ignored) {} } // 尺寸变化时重新定位 int curLeft = ((AbstractContainerScreenAccessor) (Object) this).eap$getLeftPos(); @@ -159,7 +151,6 @@ public abstract class InterfaceScreenMixin { eap$lastImageWidth = curImgW; eap$lastImageHeight = curImgH; eap$relayoutButtons(); - try { LogUtils.getLogger().info("[EAP][InterfaceMixin] relayout due to bounds change: left={}, top={}, w={}, h={}", curLeft, curTop, curImgW, curImgH); } catch (Throwable ignored) {} } // 每帧根据 hoveredSlot 刷新最近一次的配置槽索引 eap$updateLastConfigFromHover(); @@ -177,7 +168,7 @@ public abstract class InterfaceScreenMixin { // 获取菜单与配置槽列表 var screen = (AEBaseScreen) (Object) this; var menu = screen.getMenu(); - if (!(menu instanceof appeng.menu.implementations.InterfaceMenu interfaceMenu)) { + if (!(menu instanceof InterfaceMenu interfaceMenu)) { return; } var configSlots = interfaceMenu.getSlots(SlotSemantics.CONFIG); @@ -212,10 +203,8 @@ public abstract class InterfaceScreenMixin { slotField = slotFieldObj; } else if (eap$lastConfigIndex >= 0 && eap$lastConfigIndex < configSlots.size()) { slotField = eap$lastConfigIndex; - try { LogUtils.getLogger().info("[EAP][InterfaceMixin] Using last hovered config index: {}", slotField); } catch (Throwable ignored) {} } if (slotField < 0) { - try { LogUtils.getLogger().info("[EAP][InterfaceMixin] No hovered slot and no last config index; ignoring adjust."); } catch (Throwable ignored) {} return; } @@ -282,7 +271,7 @@ public abstract class InterfaceScreenMixin { } var screen = (AEBaseScreen) (Object) this; var menu = screen.getMenu(); - if (!(menu instanceof appeng.menu.implementations.InterfaceMenu interfaceMenu)) { + if (!(menu instanceof InterfaceMenu interfaceMenu)) { return; } var configSlots = interfaceMenu.getSlots(SlotSemantics.CONFIG); @@ -310,7 +299,6 @@ public abstract class InterfaceScreenMixin { if (idx != null && idx >= 0) { if (eap$lastConfigIndex != idx) { eap$lastConfigIndex = idx; - try { LogUtils.getLogger().info("[EAP][InterfaceMixin] lastConfigIndex updated: {}", eap$lastConfigIndex); } catch (Throwable ignored) {} } } } catch (Throwable ignored) {} diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/client/gui/PatternProviderCloseMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/client/gui/PatternProviderCloseMixin.java index 4613b2c..4370433 100644 --- a/src/main/java/com/extendedae_plus/mixin/ae2/client/gui/PatternProviderCloseMixin.java +++ b/src/main/java/com/extendedae_plus/mixin/ae2/client/gui/PatternProviderCloseMixin.java @@ -16,7 +16,6 @@ public class PatternProviderCloseMixin { if (((Object) this) instanceof PatternProviderScreen) { ClientPatternHighlightStore.clearAll(); } - } catch (Throwable ignored) { - } + } catch (Throwable ignored) {} } } \ No newline at end of file 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 deleted file mode 100644 index 1a061ae..0000000 --- a/src/main/java/com/extendedae_plus/mixin/ae2/client/gui/PatternProviderScreenUpgradesMixin.java +++ /dev/null @@ -1,54 +0,0 @@ -package com.extendedae_plus.mixin.ae2.client.gui; - -import appeng.api.upgrades.Upgrades; -import appeng.client.gui.AEBaseScreen; -import appeng.client.gui.implementations.PatternProviderScreen; -import appeng.client.gui.style.ScreenStyle; -import appeng.client.gui.widgets.ToolboxPanel; -import appeng.client.gui.widgets.UpgradesPanel; -import appeng.core.localization.GuiText; -import appeng.menu.SlotSemantics; -import appeng.menu.implementations.PatternProviderMenu; -import com.extendedae_plus.api.bridge.IUpgradableMenu; -import com.extendedae_plus.compat.UpgradeSlotCompat; -import net.minecraft.network.chat.Component; -import net.minecraft.world.entity.player.Inventory; -import org.spongepowered.asm.mixin.Mixin; -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; - } - - this.widgets.add("upgrades", new UpgradesPanel( - menu.getSlots(SlotSemantics.UPGRADE), - this::eap$getCompatibleUpgrades)); - if (((IUpgradableMenu) menu).getToolbox() != null && ((IUpgradableMenu) menu).getToolbox().isPresent()) { - this.widgets.add("toolbox", new ToolboxPanel(style, ((IUpgradableMenu) menu).getToolbox().getName())); - } - } - - @Unique - private List eap$getCompatibleUpgrades() { - var list = new ArrayList(); - list.add(GuiText.CompatibleUpgrades.text()); - list.addAll(Upgrades.getTooltipLinesForMachine(((IUpgradableMenu) menu).getUpgrades().getUpgradableItem())); - return list; - } - - public PatternProviderScreenUpgradesMixin(C menu, Inventory playerInventory, Component title, ScreenStyle style) { - super(menu, playerInventory, title, style); - } -} diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/helpers/PatternProviderLogicDoublingMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/helpers/PatternProviderLogicDoublingMixin.java index df1e66a..d78a7eb 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 @@ -5,7 +5,7 @@ import appeng.crafting.pattern.AEProcessingPattern; import appeng.helpers.patternprovider.PatternProviderLogic; import com.extendedae_plus.api.smartDoubling.ISmartDoublingAwarePattern; import com.extendedae_plus.api.smartDoubling.ISmartDoublingHolder; -import com.extendedae_plus.mixin.ae2.accessor.PatternProviderLogicPatternsAccessor; +import com.extendedae_plus.mixin.ae2.accessor.PatternProviderLogicAccessor; import net.minecraft.nbt.CompoundTag; import net.minecraft.world.entity.player.Player; import org.spongepowered.asm.mixin.Mixin; @@ -33,7 +33,7 @@ public class PatternProviderLogicDoublingMixin implements ISmartDoublingHolder { this.eap$smartDoubling = value; // 立即将开关状态应用到当前 Provider 的样板上,避免等待下一次 updatePatterns try { - var list = ((PatternProviderLogicPatternsAccessor) this).eap$patterns(); + var list = ((PatternProviderLogicAccessor) this).eap$patterns(); for (IPatternDetails details : list) { if (details instanceof AEProcessingPattern proc && proc instanceof ISmartDoublingAwarePattern aware) { aware.eap$setAllowScaling(value); @@ -60,7 +60,7 @@ public class PatternProviderLogicDoublingMixin implements ISmartDoublingHolder { @Inject(method = "updatePatterns", at = @At("TAIL")) private void eap$applySmartDoublingToPatterns(CallbackInfo ci) { try { - var list = ((PatternProviderLogicPatternsAccessor) this).eap$patterns(); + var list = ((PatternProviderLogicAccessor) this).eap$patterns(); boolean allow = this.eap$smartDoubling; for (IPatternDetails details : list) { if (details instanceof AEProcessingPattern proc && proc instanceof ISmartDoublingAwarePattern aware) { diff --git a/src/main/java/com/extendedae_plus/network/provider/ToggleAdvancedBlockingC2SPacket.java b/src/main/java/com/extendedae_plus/network/provider/ToggleAdvancedBlockingC2SPacket.java index c971d73..9162194 100644 --- a/src/main/java/com/extendedae_plus/network/provider/ToggleAdvancedBlockingC2SPacket.java +++ b/src/main/java/com/extendedae_plus/network/provider/ToggleAdvancedBlockingC2SPacket.java @@ -5,7 +5,7 @@ import appeng.api.config.YesNo; import appeng.menu.implementations.PatternProviderMenu; import com.extendedae_plus.api.IAdvancedBlocking; import com.extendedae_plus.mixin.advancedae.accessor.AdvPatternProviderMenuAdvancedAccessor; -import com.extendedae_plus.mixin.ae2.accessor.PatternProviderMenuAdvancedAccessor; +import com.extendedae_plus.mixin.ae2.accessor.PatternProviderMenuAccessor; import net.minecraft.network.FriendlyByteBuf; import net.minecraft.server.level.ServerPlayer; import net.minecraftforge.network.NetworkEvent; @@ -34,7 +34,7 @@ public class ToggleAdvancedBlockingC2SPacket { var containerMenu = player.containerMenu; if (containerMenu instanceof PatternProviderMenu menu) { - var accessor = (PatternProviderMenuAdvancedAccessor) menu; + var accessor = (PatternProviderMenuAccessor) menu; var logic = accessor.eap$logic(); if (logic instanceof IAdvancedBlocking holder) { boolean current = holder.eap$getAdvancedBlocking(); diff --git a/src/main/java/com/extendedae_plus/network/provider/ToggleSmartDoublingC2SPacket.java b/src/main/java/com/extendedae_plus/network/provider/ToggleSmartDoublingC2SPacket.java index abba57b..2006b5c 100644 --- a/src/main/java/com/extendedae_plus/network/provider/ToggleSmartDoublingC2SPacket.java +++ b/src/main/java/com/extendedae_plus/network/provider/ToggleSmartDoublingC2SPacket.java @@ -3,7 +3,7 @@ package com.extendedae_plus.network.provider; import appeng.menu.implementations.PatternProviderMenu; import com.extendedae_plus.api.smartDoubling.ISmartDoublingHolder; import com.extendedae_plus.mixin.advancedae.accessor.AdvPatternProviderMenuAdvancedAccessor; -import com.extendedae_plus.mixin.ae2.accessor.PatternProviderMenuAdvancedAccessor; +import com.extendedae_plus.mixin.ae2.accessor.PatternProviderMenuAccessor; import net.minecraft.network.FriendlyByteBuf; import net.minecraft.server.level.ServerPlayer; import net.minecraftforge.network.NetworkEvent; @@ -31,7 +31,7 @@ public class ToggleSmartDoublingC2SPacket { if (player == null) return; var containerMenu = player.containerMenu; if (containerMenu instanceof PatternProviderMenu menu) { - var accessor = (PatternProviderMenuAdvancedAccessor) menu; + var accessor = (PatternProviderMenuAccessor) menu; var logic = accessor.eap$logic(); if (logic instanceof ISmartDoublingHolder holder) { boolean current = holder.eap$getSmartDoubling(); diff --git a/src/main/resources/extendedae_plus.mixins.json b/src/main/resources/extendedae_plus.mixins.json index 97c7ce5..c01b196 100644 --- a/src/main/resources/extendedae_plus.mixins.json +++ b/src/main/resources/extendedae_plus.mixins.json @@ -11,7 +11,6 @@ "advancedae.client.gui.SmallAdvPatternProviderScreenMixin", "ae2.QuartzCuttingKnifeItemMixin", "ae2.accessor.AEBaseScreenAccessor", - "ae2.accessor.AEBaseScreenInvoker", "ae2.accessor.MEStorageScreenAccessor", "ae2.accessor.PatternAccessTermScreenAccessor", "ae2.accessor.PatternAccessTermScreenSlotsRowAccessor", @@ -20,8 +19,8 @@ "ae2.client.gui.PatternEncodingTermScreenMixin", "ae2.client.gui.PatternProviderCloseMixin", "ae2.client.gui.PatternProviderScreenMixin", - "ae2.compat.PatternProviderScreenCompatMixin", "ae2.client.gui.SlotGridLayoutMixin", + "ae2.compat.PatternProviderScreenCompatMixin", "ae2.menu.CraftConfirmMenuGoBackMixin", "extendedae.accessor.GuiExPatternTerminalAccessor", "extendedae.accessor.GuiExPatternTerminalSlotsRowAccessor", @@ -45,43 +44,41 @@ "ae2.CraftingCalculationMixin", "ae2.CraftingCPUClusterMixin", "ae2.EncodedPatternItemMixin", + "ae2.InterfaceLogicUpgradesMixin", "ae2.accessor.MEStorageMenuAccessor", "ae2.accessor.PatternEncodingTermMenuAccessor", "ae2.accessor.PatternProviderLogicAccessor", - "ae2.accessor.PatternProviderLogicPatternInputsAccessor", - "ae2.accessor.PatternProviderLogicPatternsAccessor", - "ae2.accessor.PatternProviderMenuAdvancedAccessor", + "ae2.accessor.PatternProviderMenuAccessor", "ae2.autopattern.CraftingServiceGetProvidersMixin", "ae2.autopattern.CraftingTreeNodeAccessor", "ae2.autopattern.CraftingTreeNodeMixin", "ae2.autopattern.CraftingTreeProcessMixin", "ae2.autopattern.PatternProviderLogicContainsRedirectMixin", + "ae2.compat.PatternProviderCompatMixin", + "ae2.compat.PatternProviderLogicCompatMixin", + "ae2.compat.PatternProviderLogicHostCompatMixin", + "ae2.helpers.InterfaceLogicChannelCardMixin", + "ae2.helpers.InterfaceLogicTickerMixin", "ae2.helpers.PatternProviderLogicAdvancedMixin", "ae2.helpers.PatternProviderLogicDoublingMixin", + "ae2.helpers.patternprovider.PatternProviderLogicTickerMixin", "ae2.menu.ContainerPatternEncodingTermMenuMixin", "ae2.menu.MEStorageMenuMixin", "ae2.menu.PatternEncodingTermMenuMixin", "ae2.menu.PatternProviderMenuAdvancedMixin", "ae2.menu.PatternProviderMenuDoublingMixin", - "ae2.compat.PatternProviderLogicCompatMixin", - "ae2.compat.PatternProviderLogicHostCompatMixin", - "ae2.compat.PatternProviderCompatMixin", - "appflux.AppfluxPatternProviderLogicMixin", - "ae2.helpers.patternprovider.PatternProviderLogicTickerMixin", "ae2.parts.AEBasePartClientSyncMixin", "ae2.parts.automation.IOBusPartChannelCardMixin", "ae2.parts.automation.IOBusPartTickerChannelCardMixin", "ae2.parts.storagebus.StorageBusPartChannelCardMixin", "ae2.parts.storagebus.StorageBusPartTickerChannelCardMixin", "ae2WTlib.ContainerUWirelessExPatternTerminalMixin", + "appflux.AppfluxPatternProviderLogicMixin", "extendedae.common.PartExPatternProviderMixin", "extendedae.common.TileExPatternProviderMixin", "extendedae.container.ContainerExPatternProviderMixin", "extendedae.container.ContainerExPatternTerminalMixin", - "extendedae.container.ContainerWirelessExPatternTerminalMixin", - "ae2.helpers.InterfaceLogicChannelCardMixin", - "ae2.helpers.InterfaceLogicTickerMixin", - "ae2.InterfaceLogicUpgradesMixin" + "extendedae.container.ContainerWirelessExPatternTerminalMixin" ], "injectors": { "defaultRequire": 1 From 13a7a1f5ce77e29b179b00f6393fc55b39c201d6 Mon Sep 17 00:00:00 2001 From: C-H716 <1536152356@qq.com> Date: Sat, 27 Sep 2025 20:15:33 +0800 Subject: [PATCH 10/51] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E5=AE=9E?= =?UTF-8?q?=E4=BD=93=E5=8A=A0=E9=80=9F=E5=99=A8=E6=98=AF=E5=90=A6=E4=BC=98?= =?UTF-8?q?=E5=85=88=E4=BD=BF=E7=94=A8=E7=A3=81=E7=9B=98=E5=AD=98=E5=82=A8?= =?UTF-8?q?=E8=83=BD=E6=BA=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ae/parts/EntitySpeedTickerPart.java | 57 ++++++++++++------- .../com/extendedae_plus/config/ModConfig.java | 7 +++ .../assets/extendedae_plus/lang/en_us.json | 10 +++- .../assets/extendedae_plus/lang/zh_cn.json | 13 +++-- 4 files changed, 58 insertions(+), 29 deletions(-) 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 6604f80..1d71ddf 100644 --- a/src/main/java/com/extendedae_plus/ae/parts/EntitySpeedTickerPart.java +++ b/src/main/java/com/extendedae_plus/ae/parts/EntitySpeedTickerPart.java @@ -267,8 +267,17 @@ public class EntitySpeedTickerPart extends UpgradeablePart implements IGridTicka 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 && ModConfig.INSTANCE.prioritizeDiskEnergy; - // 优先尝试提取 AE 能量 + // 如果 appflux 存在且优先磁盘能量,尝试提取 FE 能量 + if (appFluxLoaded && preferDiskEnergy) { + if (tryExtractFE(energyService, storage, requiredPower, source)) { + return true; + } + } + + // 尝试提取 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); @@ -278,29 +287,33 @@ public class EntitySpeedTickerPart extends UpgradeablePart implements IGridTicka } updateNetworkEnergySufficient(false); - // AE 能量不足,尝试从磁盘提取 FE 能量(如果 Applied Flux 存在) - if (ModList.get().isLoaded("appflux")) { - 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); + // 如果 appflux 存在且优先 AE 能量,尝试提取 FE 能量作为备用 + if (appFluxLoaded && !preferDiskEnergy) { + return tryExtractFE(energyService, storage, requiredPower, source); } + return false; + } + 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; } diff --git a/src/main/java/com/extendedae_plus/config/ModConfig.java b/src/main/java/com/extendedae_plus/config/ModConfig.java index 413c54b..173ae83 100644 --- a/src/main/java/com/extendedae_plus/config/ModConfig.java +++ b/src/main/java/com/extendedae_plus/config/ModConfig.java @@ -125,4 +125,11 @@ public final class ModConfig { @Configurable.Range(min = 100, max = Integer.MAX_VALUE) public int craftingPauseThreshold = 100000; + @Configurable + @Configurable.Comment(value = { + "是否优先从磁盘提取FE能量(仅当Applied Flux模组存在时生效)", + "开启后,将优先尝试从磁盘提取FE能量;反之优先消耗AE网络中的能量" + }) + @Configurable.Synchronized + public boolean prioritizeDiskEnergy = true; } \ 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 9d88bc6..3e8a67d 100644 --- a/src/main/resources/assets/extendedae_plus/lang/en_us.json +++ b/src/main/resources/assets/extendedae_plus/lang/en_us.json @@ -27,6 +27,12 @@ "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": "Max effective: %s x", + "block.extendedae_plus.assembler_matrix_upload_core.tooltip": "Assembler Matrix Upload Core", + "block.extendedae_plus.assembler_matrix_upload_core.tooltip.upload": "Upload to Assembler Matrix", + "block.extendedae_plus.assembler_matrix_upload_core.tooltip.upload_success": "Pattern uploaded to Assembler Matrix", + "block.extendedae_plus.assembler_matrix_upload_core.tooltip.upload_fail_not_crafting": "Only crafting patterns supported, processing patterns ignored", + "block.extendedae_plus.assembler_matrix_upload_core.tooltip.upload_fail_no_matrix": "No formed Assembler Matrix found in network", + "block.extendedae_plus.assembler_matrix_upload_core.tooltip.upload_fail_full": "Assembler Matrix pattern storage full or cannot insert", "block.extendedae_plus.wireless_transceiver": "Wireless Transceiver", "block.extendedae_plus.4x_crafting_accelerator": "4x Crafting Accelerator", @@ -88,11 +94,13 @@ "config.extendedae_plus.option.entityTickerBlackList": "Entity Ticker Blacklist", "config.extendedae_plus.option.entityTickerMultipliers": "Entity Ticker Extra Consumption Multipliers", "config.extendedae_plus.option.craftingPauseThreshold": "AE synthesis calculation pause check threshold", - "block.extendedae_plus.assembler_matrix_upload_core": "Assembler Matrix Upload Core", + "config.extendedae_plus.option.prioritizeDiskEnergy": "Prioritize FE energy from disk (requires Applied Flux)", "item.extendedae_plus.channel_card": "Channel Card", "item.extendedae_plus.channel_card.channel": "Frequency: %s", "item.extendedae_plus.channel_card.channel.unset": "Frequency: Unset", "item.extendedae_plus.channel_card.set": "Frequency set to: %s", + "group.pattern_provider.name": "Pattern Provider", + "group.entity_ticker.name": "Entity Accelerator", "group.storage.name": "StorageBus" } \ No newline at end of file diff --git a/src/main/resources/assets/extendedae_plus/lang/zh_cn.json b/src/main/resources/assets/extendedae_plus/lang/zh_cn.json index eb60ec2..0a2343c 100644 --- a/src/main/resources/assets/extendedae_plus/lang/zh_cn.json +++ b/src/main/resources/assets/extendedae_plus/lang/zh_cn.json @@ -27,6 +27,12 @@ "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 倍", + "block.extendedae_plus.assembler_matrix_upload_core.tooltip": "装配矩阵上传核心", + "block.extendedae_plus.assembler_matrix_upload_core.tooltip.upload": "上传到装配矩阵", + "block.extendedae_plus.assembler_matrix_upload_core.tooltip.upload_success": "样板已上传到装配矩阵", + "block.extendedae_plus.assembler_matrix_upload_core.tooltip.upload_fail_not_crafting": "仅支持上传合成样板,处理样板将被忽略", + "block.extendedae_plus.assembler_matrix_upload_core.tooltip.upload_fail_no_matrix": "未在当前网络中找到已成型的装配矩阵", + "block.extendedae_plus.assembler_matrix_upload_core.tooltip.upload_fail_full": "装配矩阵的样板仓已满或无法插入", "block.extendedae_plus.wireless_transceiver": "无线收发器", "block.extendedae_plus.4x_crafting_accelerator": "4x并行处理单元", @@ -87,13 +93,8 @@ "config.extendedae_plus.option.entityTickerCost": "实体加速器能量消耗基础值", "config.extendedae_plus.option.entityTickerBlackList": "实体加速器黑名单", "config.extendedae_plus.option.entityTickerMultipliers": "实体加速器额外消耗倍率", + "config.extendedae_plus.option.prioritizeDiskEnergy": "优先从磁盘提取FE能量(仅当Applied Flux模组存在时生效)", "config.extendedae_plus.option.craftingPauseThreshold": "AE合成计算暂停检查阈值", - "block.extendedae_plus.assembler_matrix_upload_core.tooltip": "装配矩阵上传核心", - "block.extendedae_plus.assembler_matrix_upload_core.tooltip.upload": "上传到装配矩阵", - "block.extendedae_plus.assembler_matrix_upload_core.tooltip.upload_success": "样板已上传到装配矩阵", - "block.extendedae_plus.assembler_matrix_upload_core.tooltip.upload_fail_not_crafting": "仅支持上传合成样板,处理样板将被忽略", - "block.extendedae_plus.assembler_matrix_upload_core.tooltip.upload_fail_no_matrix": "未在当前网络中找到已成型的装配矩阵", - "block.extendedae_plus.assembler_matrix_upload_core.tooltip.upload_fail_full": "装配矩阵的样板仓已满或无法插入", "item.extendedae_plus.channel_card": "频道卡", "item.extendedae_plus.channel_card.channel": "频率:%s", From 8aeeb1e226f3ffa4fcdf3ad8e08b15eba4cfa23e Mon Sep 17 00:00:00 2001 From: GaLicn <133291877+GaLicn@users.noreply.github.com> Date: Sun, 28 Sep 2025 12:35:25 +0800 Subject: [PATCH 11/51] =?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 --- .../integration/jei/JeiBookmarkBridge.java | 181 ++++++++++++++++++ 1 file changed, 181 insertions(+) create mode 100644 src/main/java/com/extendedae_plus/integration/jei/JeiBookmarkBridge.java diff --git a/src/main/java/com/extendedae_plus/integration/jei/JeiBookmarkBridge.java b/src/main/java/com/extendedae_plus/integration/jei/JeiBookmarkBridge.java new file mode 100644 index 0000000..9e1572d --- /dev/null +++ b/src/main/java/com/extendedae_plus/integration/jei/JeiBookmarkBridge.java @@ -0,0 +1,181 @@ +package com.extendedae_plus.integration.jei; + +import com.extendedae_plus.mixin.jei.accessor.BookmarkOverlayAccessor; +import mezz.jei.api.constants.VanillaTypes; +import mezz.jei.api.forge.ForgeTypes; +import mezz.jei.api.ingredients.IIngredientType; +import mezz.jei.api.ingredients.ITypedIngredient; +import mezz.jei.api.runtime.IBookmarkOverlay; +import mezz.jei.api.runtime.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.minecraftforge.fluids.FluidStack; +import net.minecraftforge.fml.ModList; +import org.spongepowered.asm.mixin.Pseudo; + +import javax.annotation.Nullable; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.Collections; +import java.util.List; +import java.util.Optional; + +/** + * 将所有会引用 JEI GUI 内部类(如 BookmarkList、IngredientBookmark、IElement 等)的逻辑 + * 隔离在此桥接类中,避免在未安装 JEI 或 JEI 组件不完整时过早类加载导致的 NoClassDefFoundError。 + * + * 该类仅会被 {@link JeiRuntimeProxy} 通过反射调用。 + */ +@Pseudo +public final class JeiBookmarkBridge { + private JeiBookmarkBridge() {} + + // 通过 JeiRuntimeProxy 的包内可见方法安全地获取 Runtime + private static @Nullable IJeiRuntime getRuntime() { + try { + Class proxy = Class.forName("com.extendedae_plus.integration.jei.JeiRuntimeProxy"); + var m = proxy.getDeclaredMethod("get"); + Object rt = m.invoke(null); + return (IJeiRuntime) rt; + } catch (Throwable ignored) { + return null; + } + } + + public static List> getBookmarkList() { + IJeiRuntime rt = getRuntime(); + 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(); + } + + public static void addBookmark(ItemStack stack) { + IJeiRuntime rt = getRuntime(); + if (rt == null) return; + + IBookmarkOverlay overlay = rt.getBookmarkOverlay(); + if (overlay instanceof BookmarkOverlayAccessor accessor) { + BookmarkList list = accessor.eap$getBookmarkList(); + Optional> typedOpt = rt.getIngredientManager() + .createTypedIngredient(VanillaTypes.ITEM_STACK, stack); + typedOpt.ifPresent(typed -> { + IngredientBookmark bookmark = IngredientBookmark.create(typed, rt.getIngredientManager()); + list.add(bookmark); // add 内部会自动保存到配置 + }); + } + } + + public static void addBookmark(FluidStack fluidStack) { + IJeiRuntime rt = getRuntime(); + if (rt == null) return; + + IBookmarkOverlay overlay = rt.getBookmarkOverlay(); + if (overlay instanceof BookmarkOverlayAccessor accessor) { + BookmarkList list = accessor.eap$getBookmarkList(); + Optional> typedOpt = rt.getIngredientManager() + .createTypedIngredient(ForgeTypes.FLUID_STACK, fluidStack); + typedOpt.ifPresent(typed -> { + IngredientBookmark bookmark = IngredientBookmark.create(typed, rt.getIngredientManager()); + list.add(bookmark); // add 内部会自动保存到配置 + }); + } + } + + /** + * 如果存在 Mekanism/appmek,则将 Mekanism 化学堆栈添加到 JEI 书签。 + */ + public static void addBookmark(Object chemicalStack) { + if (!ModList.get().isLoaded("mekanism") && !ModList.get().isLoaded("appmek")) return; + + IJeiRuntime rt = getRuntime(); + if (rt == null) return; + + IBookmarkOverlay overlay = rt.getBookmarkOverlay(); + if (overlay instanceof BookmarkOverlayAccessor accessor) { + BookmarkList list = accessor.eap$getBookmarkList(); + try { + if (chemicalStack == null) return; + + // Determine Mekanism JEI ingredient type constant by runtime class name + String clsName = chemicalStack.getClass().getName(); + String mekanismJeiClass = "mekanism.client.jei.MekanismJEI"; + Class jeiCls = Class.forName(mekanismJeiClass); + Field typeField = getField(clsName, jeiCls); + + if (typeField == null) return; + Object typeConst = typeField.get(null); + + // Use ingredient manager reflectively to create a typed ingredient + Object ingredientManager = rt.getIngredientManager(); + Method createTypedIngredient = ingredientManager.getClass().getMethod("createTypedIngredient", IIngredientType.class, Object.class); + Object opt = createTypedIngredient.invoke(ingredientManager, typeConst, chemicalStack); + if (!(opt instanceof Optional typedOpt)) return; + if (typedOpt.isPresent()) { + Object typed = typedOpt.get(); + // Find a compatible static create(...) method on IngredientBookmark where + // the second parameter is assignable from the actual ingredientManager instance. + Method createMethod = null; + for (Method m : IngredientBookmark.class.getMethods()) { + if (!m.getName().equals("create")) continue; + Class[] params = m.getParameterTypes(); + if (params.length != 2) continue; + // first param should accept the typed ingredient + boolean firstOk = params[0].isAssignableFrom(typed.getClass()) || params[0].isAssignableFrom(ITypedIngredient.class); + boolean secondOk = params[1].isAssignableFrom(ingredientManager.getClass()); + if (firstOk && secondOk) { + createMethod = m; + break; + } + } + if (createMethod != null) { + Object bookmark = createMethod.invoke(null, typed, ingredientManager); + if (bookmark != null) { + list.add((IngredientBookmark) bookmark); + } + } + } + } catch (Throwable e) { + e.printStackTrace(); + } + } + } + + private static @Nullable Field getField(String clsName, Class jeiCls) throws NoSuchFieldException { + Field typeField = null; + if ("mekanism.api.chemical.gas.GasStack".equals(clsName)) { + typeField = jeiCls.getField("TYPE_GAS"); + } else if ("mekanism.api.chemical.infuse.InfusionStack".equals(clsName)) { + typeField = jeiCls.getField("TYPE_INFUSION"); + } else if ("mekanism.api.chemical.pigment.PigmentStack".equals(clsName)) { + typeField = jeiCls.getField("TYPE_PIGMENT"); + } else if ("mekanism.api.chemical.slurry.SlurryStack".equals(clsName)) { + typeField = jeiCls.getField("TYPE_SLURRY"); + } + return typeField; + } + + /** + * 从 JEI 书签移除物品 + */ + public static void removeBookmark(ItemStack stack) { + IJeiRuntime rt = getRuntime(); + if (rt == null) return; + + IBookmarkOverlay overlay = rt.getBookmarkOverlay(); + if (overlay instanceof BookmarkOverlayAccessor accessor) { + BookmarkList list = accessor.eap$getBookmarkList(); + Optional> typedOpt = rt.getIngredientManager() + .createTypedIngredient(VanillaTypes.ITEM_STACK, stack); + typedOpt.ifPresent(typed -> { + IngredientBookmark bookmark = IngredientBookmark.create(typed, rt.getIngredientManager()); + list.remove(bookmark); + }); + } + } +} From d61a41873125a61cb43cd6bcee1a4b33cae4c26c Mon Sep 17 00:00:00 2001 From: C-H716 <1536152356@qq.com> Date: Sun, 28 Sep 2025 12:50:42 +0800 Subject: [PATCH 12/51] =?UTF-8?q?feat:=20=E5=AE=9E=E4=BD=93=E5=8A=A0?= =?UTF-8?q?=E9=80=9F=E5=8D=A1=E5=B0=8F=E5=BD=A9=E8=9B=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/assets/extendedae_plus/lang/zh_cn.json | 1 + 1 file changed, 1 insertion(+) 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 0a2343c..92d01cc 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,7 @@ "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.x1": "§k§l?#@§r§8不§k§l1§r限§k§l0§r存于§k§l%§r现实的§c实§k&0%§r体加§k§l*§r速卡§k§l...§r", "item.extendedae_plus.entity_speed_card.x2": "实体加速卡 (x2)", "item.extendedae_plus.entity_speed_card.x4": "实体加速卡 (x4)", "item.extendedae_plus.entity_speed_card.x8": "实体加速卡 (x8)", From 869ab98af4bdb0508eca14844b264ac0a4dc44f1 Mon Sep 17 00:00:00 2001 From: C-H716 <1536152356@qq.com> Date: Sun, 28 Sep 2025 20:18:30 +0800 Subject: [PATCH 13/51] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E5=86=85?= =?UTF-8?q?=E5=AD=98=E5=8D=A1=E5=AF=B9=E5=AE=9E=E4=BD=93=E5=8A=A0=E9=80=9F?= =?UTF-8?q?=E5=99=A8=E3=80=81=E5=AE=9E=E4=BD=93=E5=8A=A0=E9=80=9F=E5=8D=A1?= =?UTF-8?q?=E7=9A=84=E5=A4=8D=E5=88=B6=E7=B2=98=E8=B4=B4=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ae/api/config/Settings.java | 41 ++++ .../ae/menu/EntitySpeedTickerMenu.java | 3 +- .../ae/parts/EntitySpeedTickerPart.java | 21 +- .../ae/screen/EntitySpeedTickerScreen.java | 4 +- .../extendedae_plus/init/UpgradeCards.java | 10 +- .../mixin/ae2/items/MemoryCardItemMixin.java | 223 ++++++++++++++++++ .../QuartzCuttingKnifeItemMixin.java | 2 +- .../resources/extendedae_plus.mixins.json | 3 +- 8 files changed, 287 insertions(+), 20 deletions(-) create mode 100644 src/main/java/com/extendedae_plus/ae/api/config/Settings.java create mode 100644 src/main/java/com/extendedae_plus/mixin/ae2/items/MemoryCardItemMixin.java rename src/main/java/com/extendedae_plus/mixin/ae2/{ => items}/QuartzCuttingKnifeItemMixin.java (99%) diff --git a/src/main/java/com/extendedae_plus/ae/api/config/Settings.java b/src/main/java/com/extendedae_plus/ae/api/config/Settings.java new file mode 100644 index 0000000..6ed3fc5 --- /dev/null +++ b/src/main/java/com/extendedae_plus/ae/api/config/Settings.java @@ -0,0 +1,41 @@ +package com.extendedae_plus.ae.api.config; + +import appeng.api.config.Setting; +import appeng.api.config.YesNo; +import com.google.common.base.Preconditions; + +import java.util.EnumSet; +import java.util.HashMap; +import java.util.Map; + +public final class Settings { + private static final Map> SETTINGS = new HashMap<>(); + public static final Setting ACCELERATE = register("accelerate", YesNo.NO, YesNo.YES); + + + private Settings() { + } + + private synchronized static > Setting register(String name, Class enumClass) { + Preconditions.checkState(!SETTINGS.containsKey(name)); + var setting = new Setting<>(name, enumClass); + SETTINGS.put(name, setting); + return setting; + } + + @SafeVarargs + private synchronized static > Setting register(String name, T firstOption, T... moreOptions) { + Preconditions.checkState(!SETTINGS.containsKey(name)); + var setting = new Setting(name, firstOption.getDeclaringClass(), EnumSet.of(firstOption, moreOptions)); + SETTINGS.put(name, setting); + return setting; + } + + public static Setting getOrThrow(String name) { + var setting = SETTINGS.get(name); + if (setting == null) { + throw new IllegalArgumentException("Unknown setting '" + name + "'"); + } + return setting; + } +} 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 70f956b..008a9ff 100644 --- a/src/main/java/com/extendedae_plus/ae/menu/EntitySpeedTickerMenu.java +++ b/src/main/java/com/extendedae_plus/ae/menu/EntitySpeedTickerMenu.java @@ -13,6 +13,7 @@ import com.extendedae_plus.util.entitySpeed.ConfigParsingUtils; import com.extendedae_plus.util.entitySpeed.PowerUtils; import net.minecraft.client.Minecraft; import net.minecraft.world.entity.player.Inventory; +import net.minecraft.world.inventory.Slot; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraftforge.registries.ForgeRegistries; @@ -93,7 +94,7 @@ public class EntitySpeedTickerMenu extends UpgradeableMenu extends UpgradeableScreen { - private boolean eap$entitySpeedTickerEnabled = false; // 本地缓存的加速开关状态 - private final SettingToggleButton eap$entitySpeedTickerToggle; // 加速开关按钮 + private boolean eap$entitySpeedTickerEnabled = false; // 本地缓存的加速开关状态 + private final SettingToggleButton eap$entitySpeedTickerToggle; // 加速开关按钮 /** * 构造函数,初始化界面和控件。 diff --git a/src/main/java/com/extendedae_plus/init/UpgradeCards.java b/src/main/java/com/extendedae_plus/init/UpgradeCards.java index c7b9c33..747cecb 100644 --- a/src/main/java/com/extendedae_plus/init/UpgradeCards.java +++ b/src/main/java/com/extendedae_plus/init/UpgradeCards.java @@ -6,14 +6,8 @@ import appeng.core.definitions.AEItems; import appeng.core.definitions.AEParts; import appeng.core.localization.GuiText; import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent; -import static com.glodblock.github.extendedae.common.EPPItemAndBlock.EX_PATTERN_PROVIDER; -import static com.glodblock.github.extendedae.common.EPPItemAndBlock.EX_PATTERN_PROVIDER_PART; -import static com.glodblock.github.extendedae.common.EPPItemAndBlock.EX_INTERFACE; -import static com.glodblock.github.extendedae.common.EPPItemAndBlock.EX_INTERFACE_PART; -import static com.glodblock.github.extendedae.common.EPPItemAndBlock.OVERSIZE_INTERFACE; -import static com.glodblock.github.extendedae.common.EPPItemAndBlock.OVERSIZE_INTERFACE_PART; -import static com.glodblock.github.extendedae.common.EPPItemAndBlock.EX_IMPORT_BUS; -import static com.glodblock.github.extendedae.common.EPPItemAndBlock.EX_EXPORT_BUS; + +import static com.glodblock.github.extendedae.common.EPPItemAndBlock.*; /** * diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/items/MemoryCardItemMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/items/MemoryCardItemMixin.java new file mode 100644 index 0000000..7add032 --- /dev/null +++ b/src/main/java/com/extendedae_plus/mixin/ae2/items/MemoryCardItemMixin.java @@ -0,0 +1,223 @@ +package com.extendedae_plus.mixin.ae2.items; + +import appeng.api.inventories.InternalInventory; +import appeng.api.upgrades.IUpgradeableObject; +import appeng.core.localization.PlayerMessages; +import appeng.items.tools.MemoryCardItem; +import appeng.items.tools.NetworkToolItem; +import appeng.util.inv.PlayerInternalInventory; +import com.extendedae_plus.ae.parts.EntitySpeedTickerPart; +import com.extendedae_plus.init.ModItems; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.ListTag; +import net.minecraft.nbt.Tag; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.block.entity.BlockEntity; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +import java.util.ArrayList; + +@Mixin(value = MemoryCardItem.class, remap = false) +public class MemoryCardItemMixin { + + /** + * 写入 Memory Card 时保留实体加速卡的完整 NBT 数据 + */ + @Inject(method = "storeUpgrades", at = @At("HEAD"), cancellable = true) + private static void storeUpgradesCustom(IUpgradeableObject upgradeableObject, CompoundTag output, CallbackInfo ci) { + try { + CompoundTag desiredUpgradesTag = new CompoundTag(); + ListTag entitySpeedCards = new ListTag(); + InternalInventory upgrades = upgradeableObject.getUpgrades(); + + for (int i = 0; i < upgrades.size(); i++) { + ItemStack upgradeStack = upgrades.getStackInSlot(i); + if (upgradeStack.isEmpty()) continue; + + ResourceLocation itemId = BuiltInRegistries.ITEM.getKey(upgradeStack.getItem()); + String key = itemId.toString(); + + if (upgradeStack.getItem().equals(ModItems.ENTITY_SPEED_CARD.get())) { + CompoundTag stackTag = new CompoundTag(); + stackTag.putInt("Slot", i); + upgradeStack.save(stackTag); + entitySpeedCards.add(stackTag); + } else { + desiredUpgradesTag.putInt(key, desiredUpgradesTag.getInt(key) + upgradeStack.getCount()); + } + } + + if (!entitySpeedCards.isEmpty()) { + desiredUpgradesTag.put("entity_speed_cards", entitySpeedCards); + } + + output.put("upgrades", desiredUpgradesTag); + ci.cancel(); + } catch (Exception e) { + e.printStackTrace(); + } + } + + /** + * 从 Memory Card 恢复升级时,从玩家背包或网络工具提取实体加速卡 + */ + @Inject(method = "restoreUpgrades", at = @At("HEAD"), cancellable = true) + private static void restoreUpgradesCustom(Player player, CompoundTag input, IUpgradeableObject upgradeableObject, CallbackInfoReturnable cir) { + try { + if (!input.contains("upgrades")) { + cir.setReturnValue(false); + return; + } + + CompoundTag desiredUpgradesTag = input.getCompound("upgrades"); + InternalInventory upgrades = upgradeableObject.getUpgrades(); + + // 收集背包和网络工具作为升级卡来源 + var upgradeSources = new ArrayList(); + upgradeSources.add(new PlayerInternalInventory(player.getInventory())); + var networkTool = NetworkToolItem.findNetworkToolInv(player); + if (networkTool != null) { + upgradeSources.add(networkTool.getInventory()); + } + + // 清空所有槽位中的 EntitySpeedCardItem + for (int i = 0; i < upgrades.size(); i++) { + ItemStack stack = upgrades.getStackInSlot(i); + if (!stack.isEmpty() && stack.getItem().equals(ModItems.ENTITY_SPEED_CARD.get())) { + ItemStack removed = upgrades.extractItem(i, stack.getCount(), false); + for (var source : upgradeSources) { + if (!removed.isEmpty()) { + removed = source.addItems(removed); + } + } + if (!removed.isEmpty()) { + player.drop(removed, false); + } + } + } + + // 恢复 EntitySpeedCardItem + if (desiredUpgradesTag.contains("entity_speed_cards", Tag.TAG_LIST)) { + ListTag entitySpeedCards = desiredUpgradesTag.getList("entity_speed_cards", Tag.TAG_COMPOUND); + for (int i = 0; i < entitySpeedCards.size(); i++) { + CompoundTag stackTag = entitySpeedCards.getCompound(i); + ItemStack desiredStack = ItemStack.of(stackTag); + int slot = stackTag.contains("Slot") ? stackTag.getInt("Slot") : i; + + if (player.getAbilities().instabuild) { + // 创造模式:直接生成 + if (slot >= 0 && slot < upgrades.size()) { + upgrades.setItemDirect(slot, desiredStack); + } else { + upgrades.addItems(desiredStack); + } + } else { + // 非创造模式:从背包或网络工具提取 + int missingAmount = desiredStack.getCount(); + ItemStack extracted = ItemStack.EMPTY; + + for (var source : upgradeSources) { + ItemStack potential = new ItemStack(desiredStack.getItem(), missingAmount); + if (desiredStack.hasTag()) { + potential.setTag(desiredStack.getTag().copy()); + } + ItemStack cards = source.removeItems(missingAmount, potential, null); + if (!cards.isEmpty()) { + ItemStack overflow = upgrades.addItems(cards); + if (!overflow.isEmpty()) { + player.getInventory().placeItemBackInInventory(overflow); + } + missingAmount -= cards.getCount(); + extracted = cards; + } + if (missingAmount <= 0) break; + } + + if (missingAmount > 0 && !player.level().isClientSide()) { + player.displayClientMessage( + PlayerMessages.MissingUpgrades.text(desiredStack.getItem().getDescription(), missingAmount), + true + ); + } else if (!extracted.isEmpty()) { + if (slot >= 0 && slot < upgrades.size()) { + upgrades.setItemDirect(slot, extracted); + } else { + upgrades.addItems(extracted); + } + } + } + } + } + + // 恢复其他升级卡(按 AE2 原逻辑) + for (String key : desiredUpgradesTag.getAllKeys()) { + ResourceLocation id; + try { + id = new ResourceLocation(key); + } catch (Exception ex) { + continue; + } + + var item = BuiltInRegistries.ITEM.getOptional(id).orElse(null); + if (item == null || item.equals(ModItems.ENTITY_SPEED_CARD.get())) continue; + + int desiredCount = desiredUpgradesTag.getInt(key); + if (desiredCount > 0) { + if (player.getAbilities().instabuild) { + // 创造模式:直接生成 + ItemStack stack = new ItemStack(item, desiredCount); + upgrades.addItems(stack); + } else { + // 非创造模式:从背包或网络工具提取 + int missingAmount = desiredCount; + ItemStack potential = new ItemStack(item, missingAmount); + ItemStack overflow = upgrades.addItems(potential, true); + if (!overflow.isEmpty()) { + missingAmount -= overflow.getCount(); + } + + for (var source : upgradeSources) { + ItemStack cards = source.removeItems(missingAmount, potential, null); + if (!cards.isEmpty()) { + overflow = upgrades.addItems(cards); + if (!overflow.isEmpty()) { + player.getInventory().placeItemBackInInventory(overflow); + } + missingAmount -= cards.getCount(); + } + if (missingAmount <= 0) break; + } + + if (missingAmount > 0 && !player.level().isClientSide()) { + player.displayClientMessage( + PlayerMessages.MissingUpgrades.text(item.getDescription(), missingAmount), + true + ); + } + } + } + } + + // 标记保存并通知升级变化 + if (upgradeableObject instanceof EntitySpeedTickerPart speedTickerPart) { + BlockEntity be = speedTickerPart.getBlockEntity(); + if (be != null) { + be.setChanged(); + } + speedTickerPart.upgradesChanged(); + } + + cir.setReturnValue(true); + } catch (Exception e) { + e.printStackTrace(); + cir.setReturnValue(false); + } + } +} \ No newline at end of file diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/QuartzCuttingKnifeItemMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/items/QuartzCuttingKnifeItemMixin.java similarity index 99% rename from src/main/java/com/extendedae_plus/mixin/ae2/QuartzCuttingKnifeItemMixin.java rename to src/main/java/com/extendedae_plus/mixin/ae2/items/QuartzCuttingKnifeItemMixin.java index e8ddd97..bd61dca 100644 --- a/src/main/java/com/extendedae_plus/mixin/ae2/QuartzCuttingKnifeItemMixin.java +++ b/src/main/java/com/extendedae_plus/mixin/ae2/items/QuartzCuttingKnifeItemMixin.java @@ -1,4 +1,4 @@ -package com.extendedae_plus.mixin.ae2; +package com.extendedae_plus.mixin.ae2.items; import appeng.api.parts.IPartHost; import appeng.api.parts.SelectedPart; diff --git a/src/main/resources/extendedae_plus.mixins.json b/src/main/resources/extendedae_plus.mixins.json index c01b196..2a6915a 100644 --- a/src/main/resources/extendedae_plus.mixins.json +++ b/src/main/resources/extendedae_plus.mixins.json @@ -9,7 +9,6 @@ "accessor.ScreenAccessor", "advancedae.client.gui.AdvPatternProviderScreenMixin", "advancedae.client.gui.SmallAdvPatternProviderScreenMixin", - "ae2.QuartzCuttingKnifeItemMixin", "ae2.accessor.AEBaseScreenAccessor", "ae2.accessor.MEStorageScreenAccessor", "ae2.accessor.PatternAccessTermScreenAccessor", @@ -21,6 +20,7 @@ "ae2.client.gui.PatternProviderScreenMixin", "ae2.client.gui.SlotGridLayoutMixin", "ae2.compat.PatternProviderScreenCompatMixin", + "ae2.items.QuartzCuttingKnifeItemMixin", "ae2.menu.CraftConfirmMenuGoBackMixin", "extendedae.accessor.GuiExPatternTerminalAccessor", "extendedae.accessor.GuiExPatternTerminalSlotsRowAccessor", @@ -62,6 +62,7 @@ "ae2.helpers.PatternProviderLogicAdvancedMixin", "ae2.helpers.PatternProviderLogicDoublingMixin", "ae2.helpers.patternprovider.PatternProviderLogicTickerMixin", + "ae2.items.MemoryCardItemMixin", "ae2.menu.ContainerPatternEncodingTermMenuMixin", "ae2.menu.MEStorageMenuMixin", "ae2.menu.PatternEncodingTermMenuMixin", From ea8168a8c18fc76f6d629f344bfc7a8a71f123ba Mon Sep 17 00:00:00 2001 From: C-H716 <1536152356@qq.com> Date: Mon, 29 Sep 2025 08:56:36 +0800 Subject: [PATCH 14/51] =?UTF-8?q?feat:=20=E4=BD=BF=E7=94=A8=E9=A2=84?= =?UTF-8?q?=E7=BC=93=E5=AD=98=E5=AE=9E=E4=BD=93=E5=8A=A0=E9=80=9F=E5=99=A8?= =?UTF-8?q?=E8=83=BD=E8=80=97Map=E5=87=8F=E5=B0=91=E4=BD=BF=E7=94=A8?= =?UTF-8?q?=E8=BF=87=E7=A8=8B=E4=B8=AD=E7=9A=84=E8=AE=A1=E7=AE=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ae/parts/EntitySpeedTickerPart.java | 2 +- .../ae/screen/EntitySpeedTickerScreen.java | 8 +- .../compat/CompatibilityTest.java | 16 +- .../compat/UpgradeSlotCompat.java | 12 +- .../gui/AdvPatternProviderScreenMixin.java | 8 +- .../SmallAdvPatternProviderScreenMixin.java | 8 +- .../autopattern/CraftingTreeProcessMixin.java | 4 +- .../gui/PatternProviderScreenMixin.java | 8 +- .../compat/PatternProviderCompatMixin.java | 3 +- .../PatternProviderLogicCompatMixin.java | 45 +++--- .../PatternProviderScreenCompatMixin.java | 7 +- .../items/QuartzCuttingKnifeItemMixin.java | 8 +- .../PatternProviderMenuAdvancedMixin.java | 4 +- .../PatternProviderMenuDoublingMixin.java | 6 +- .../automation/IOBusPartChannelCardMixin.java | 12 +- .../StorageBusPartChannelCardMixin.java | 12 +- .../AppfluxPatternProviderLogicMixin.java | 16 +- .../client/gui/GuiExPatternProviderMixin.java | 8 +- .../{ExtendedAELogger.java => Logger.java} | 9 +- .../util/entitySpeed/ConfigParsingUtils.java | 14 +- .../util/entitySpeed/PowerUtils.java | 150 ++++++++++++++---- 21 files changed, 228 insertions(+), 132 deletions(-) rename src/main/java/com/extendedae_plus/util/{ExtendedAELogger.java => Logger.java} (55%) 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 53f634f..4d06f38 100644 --- a/src/main/java/com/extendedae_plus/ae/parts/EntitySpeedTickerPart.java +++ b/src/main/java/com/extendedae_plus/ae/parts/EntitySpeedTickerPart.java @@ -51,7 +51,7 @@ import java.util.List; * 实体加速器部件,用于加速目标方块实体的 tick 速率,消耗 AE 网络能量,支持加速卡和能量卡升级。 * 灵感来源于 Crazy AE2 Addons。 */ -public class EntitySpeedTickerPart extends UpgradeablePart implements IGridTickable, MenuProvider, IUpgradeableObject { +public class EntitySpeedTickerPart extends UpgradeablePart implements IGridTickable, MenuProvider, IUpgradeableObject { public static final ResourceLocation MODEL_BASE = new ResourceLocation( ExtendedAEPlus.MODID, "part/entity_speed_ticker_part"); 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 751d318..7a4a169 100644 --- a/src/main/java/com/extendedae_plus/ae/screen/EntitySpeedTickerScreen.java +++ b/src/main/java/com/extendedae_plus/ae/screen/EntitySpeedTickerScreen.java @@ -98,8 +98,8 @@ public class EntitySpeedTickerScreen extends Up */ private void textData() { Map textContents = new HashMap<>(); - if (getMenu().targetBlacklisted) { - // 黑名单禁用时的默认显示 + if (getMenu().targetBlacklisted || !getMenu().getAccelerateEnabled()) { + // 黑名单禁用或加速关闭时的默认显示 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))); @@ -110,8 +110,8 @@ public class EntitySpeedTickerScreen extends Up int energyCardCount = getMenu().energyCardCount; double multiplier = getMenu().multiplier; int effectiveSpeed = getMenu().effectiveSpeed; - double finalPower = PowerUtils.computeFinalPowerForProduct(effectiveSpeed, energyCardCount); - double remainingRatio = PowerUtils.getRemainingRatio(energyCardCount); + double finalPower = PowerUtils.getCachedPower(effectiveSpeed, energyCardCount); + double remainingRatio = PowerUtils.getCachedRatio(energyCardCount); textContents.put("enable", getMenu().networkEnergySufficient ? null : Component.translatable("screen.extendedae_plus.entity_speed_ticker.warning_network_energy_insufficient")); diff --git a/src/main/java/com/extendedae_plus/compat/CompatibilityTest.java b/src/main/java/com/extendedae_plus/compat/CompatibilityTest.java index 50b7687..ffb5556 100644 --- a/src/main/java/com/extendedae_plus/compat/CompatibilityTest.java +++ b/src/main/java/com/extendedae_plus/compat/CompatibilityTest.java @@ -1,6 +1,6 @@ package com.extendedae_plus.compat; -import com.extendedae_plus.util.ExtendedAELogger; +import com.extendedae_plus.util.Logger; import net.minecraftforge.fml.ModList; /** @@ -13,28 +13,28 @@ public class CompatibilityTest { * 测试模组兼容性检测 */ public static void testCompatibility() { - ExtendedAELogger.LOGGER.info("=== ExtendedAE_Plus 兼容性测试开始 ==="); + Logger.EAP$LOGGER.info("=== ExtendedAE_Plus 兼容性测试开始 ==="); // 测试appflux模组检测 boolean appfluxExists = ModList.get().isLoaded("appflux"); - ExtendedAELogger.LOGGER.info("ExtendedAE-appflux模组检测结果: {}", appfluxExists ? "存在" : "不存在"); + Logger.EAP$LOGGER.info("ExtendedAE-appflux模组检测结果: {}", appfluxExists ? "存在" : "不存在"); // 测试升级卡槽功能启用状态 boolean shouldEnableUpgrades = UpgradeSlotCompat.shouldEnableUpgradeSlots(); - ExtendedAELogger.LOGGER.info("升级卡槽功能启用状态: {}", shouldEnableUpgrades ? "启用" : "禁用"); + Logger.EAP$LOGGER.info("升级卡槽功能启用状态: {}", shouldEnableUpgrades ? "启用" : "禁用"); // 测试Screen升级面板添加状态 boolean shouldAddPanel = UpgradeSlotCompat.shouldAddUpgradePanelToScreen(); - ExtendedAELogger.LOGGER.info("Screen升级面板添加状态: {}", shouldAddPanel ? "启用" : "禁用"); + Logger.EAP$LOGGER.info("Screen升级面板添加状态: {}", shouldAddPanel ? "启用" : "禁用"); // 输出兼容性策略 if (appfluxExists) { - ExtendedAELogger.LOGGER.info("兼容性策略: 检测到ExtendedAE-appflux模组,将使用其升级卡槽功能"); + Logger.EAP$LOGGER.info("兼容性策略: 检测到ExtendedAE-appflux模组,将使用其升级卡槽功能"); } else { - ExtendedAELogger.LOGGER.info("兼容性策略: 未检测到ExtendedAE-appflux模组,将使用我们自己的升级卡槽功能"); + Logger.EAP$LOGGER.info("兼容性策略: 未检测到ExtendedAE-appflux模组,将使用我们自己的升级卡槽功能"); } - ExtendedAELogger.LOGGER.info("=== ExtendedAE_Plus 兼容性测试完成 ==="); + Logger.EAP$LOGGER.info("=== ExtendedAE_Plus 兼容性测试完成 ==="); } /** diff --git a/src/main/java/com/extendedae_plus/compat/UpgradeSlotCompat.java b/src/main/java/com/extendedae_plus/compat/UpgradeSlotCompat.java index c78c7c7..c6eab7a 100644 --- a/src/main/java/com/extendedae_plus/compat/UpgradeSlotCompat.java +++ b/src/main/java/com/extendedae_plus/compat/UpgradeSlotCompat.java @@ -10,7 +10,7 @@ import appeng.helpers.patternprovider.PatternProviderLogicHost; import appeng.menu.AEBaseMenu; import appeng.menu.SlotSemantics; import appeng.menu.ToolboxMenu; -import com.extendedae_plus.util.ExtendedAELogger; +import com.extendedae_plus.util.Logger; import net.minecraft.network.chat.Component; import net.minecraft.world.inventory.Slot; import net.minecraftforge.fml.ModList; @@ -83,7 +83,7 @@ public class UpgradeSlotCompat { setupUpgradesMethod.setAccessible(true); setupUpgradesMethod.invoke(menu, upgradeableHost.getUpgrades()); } catch (Exception e) { - ExtendedAELogger.LOGGER.error("反射调用setupUpgrades失败", e); + Logger.EAP$LOGGER.error("反射调用setupUpgrades失败", e); return false; } @@ -94,7 +94,7 @@ public class UpgradeSlotCompat { return true; } } catch (Exception e) { - ExtendedAELogger.LOGGER.error("初始化PatternProviderMenu升级功能时出错", e); + Logger.EAP$LOGGER.error("初始化PatternProviderMenu升级功能时出错", e); } return false; @@ -162,13 +162,13 @@ public class UpgradeSlotCompat { } return true; } catch (Exception e2) { - ExtendedAELogger.LOGGER.error("反射调用widgets方法失败", e2); + Logger.EAP$LOGGER.error("反射调用widgets方法失败", e2); return false; } } } } catch (Exception e) { - ExtendedAELogger.LOGGER.error("为PatternProviderScreen添加升级面板时出错", e); + Logger.EAP$LOGGER.error("为PatternProviderScreen添加升级面板时出错", e); } return false; @@ -187,7 +187,7 @@ public class UpgradeSlotCompat { list.addAll(appeng.api.upgrades.Upgrades.getTooltipLinesForMachine(upgrades.getUpgradableItem())); } } catch (Exception e) { - ExtendedAELogger.LOGGER.error("获取兼容升级列表时出错", e); + Logger.EAP$LOGGER.error("获取兼容升级列表时出错", e); } return list; 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 813c164..cbefdde 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 @@ -22,7 +22,7 @@ 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; +import static com.extendedae_plus.util.Logger.EAP$LOGGER; /** * 为高级ae样板供应器界面添加“高级阻挡模式”按钮。 @@ -56,7 +56,7 @@ public abstract class AdvPatternProviderScreenMixin extends AEBaseScreen 的外观(原版图标),但自定义悬停描述为“智能阻挡” @@ -89,7 +89,7 @@ public abstract class AdvPatternProviderScreenMixin extends AEBaseScreen( @@ -139,7 +139,7 @@ public abstract class AdvPatternProviderScreenMixin extends AEBaseScreen 的外观(原版图标),但自定义悬停描述为“智能阻挡” @@ -89,7 +89,7 @@ public abstract class SmallAdvPatternProviderScreenMixin extends AEBaseScreen( @@ -139,7 +139,7 @@ public abstract class SmallAdvPatternProviderScreenMixin extends AEBaseScreen this.eap$AdvancedBlockingEnabled = sync.eap$getAdvancedBlockingSynced(); } } catch (Throwable t) { - LOGGER.error("Error initializing advanced sync", t); + EAP$LOGGER.error("Error initializing advanced sync", t); } // 使用 SettingToggleButton 的外观(原版图标),但自定义悬停描述为“智能阻挡” @@ -89,7 +89,7 @@ public abstract class PatternProviderScreenMixin this.eap$SmartDoublingEnabled = sync2.eap$getSmartDoublingSynced(); } } catch (Throwable t) { - LOGGER.error("Error initializing smart doubling sync", t); + EAP$LOGGER.error("Error initializing smart doubling sync", t); } this.eap$SmartDoublingToggle = new SettingToggleButton<>( @@ -139,7 +139,7 @@ public abstract class PatternProviderScreenMixin try { ((IExPatternButtonsAccessor) this).eap$updateButtonsLayout(); } catch (Throwable t) { - LOGGER.debug("[EAP] updateButtonsLayout skipped: {}", t.toString()); + EAP$LOGGER.debug("[EAP] updateButtonsLayout skipped: {}", t.toString()); } } } diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/compat/PatternProviderCompatMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/compat/PatternProviderCompatMixin.java index edba48a..db7ef04 100644 --- a/src/main/java/com/extendedae_plus/mixin/ae2/compat/PatternProviderCompatMixin.java +++ b/src/main/java/com/extendedae_plus/mixin/ae2/compat/PatternProviderCompatMixin.java @@ -6,6 +6,7 @@ import appeng.menu.AEBaseMenu; import appeng.menu.ToolboxMenu; import appeng.menu.implementations.PatternProviderMenu; import com.extendedae_plus.compat.UpgradeSlotCompat; +import com.extendedae_plus.util.Logger; import net.minecraft.world.entity.player.Inventory; import net.minecraft.world.inventory.MenuType; import org.spongepowered.asm.mixin.Mixin; @@ -43,7 +44,7 @@ public abstract class PatternProviderCompatMixin extends AEBaseMenu implements U } } catch (Exception e) { // 静默处理异常,确保不会因为升级功能导致崩溃 - com.extendedae_plus.util.ExtendedAELogger.LOGGER.error("PatternProviderMenu兼容性升级初始化失败", e); + Logger.EAP$LOGGER.error("PatternProviderMenu兼容性升级初始化失败", e); } } 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 69e52af..edebb1c 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 @@ -13,6 +13,7 @@ import com.extendedae_plus.ae.wireless.endpoint.GenericNodeEndpointImpl; import com.extendedae_plus.api.bridge.IInterfaceWirelessLinkBridge; import com.extendedae_plus.compat.UpgradeSlotCompat; import com.extendedae_plus.init.ModItems; +import com.extendedae_plus.util.Logger; import net.minecraft.nbt.CompoundTag; import net.minecraft.world.item.ItemStack; import org.spongepowered.asm.mixin.Final; @@ -75,7 +76,7 @@ public abstract class PatternProviderLogicCompatMixin implements IUpgradeableObj eap$compatInitializeChannelLink(); } } catch (Exception e) { - com.extendedae_plus.util.ExtendedAELogger.LOGGER.error("兼容性升级变更处理失败", e); + Logger.EAP$LOGGER.error("兼容性升级变更处理失败", e); } } @@ -84,14 +85,14 @@ public abstract class PatternProviderLogicCompatMixin implements IUpgradeableObj private void eap$onAppfluxUpgradesChanged(CallbackInfo ci) { try { if (UpgradeSlotCompat.shouldEnableChannelCard()) { - com.extendedae_plus.util.ExtendedAELogger.LOGGER.info("监听到appflux升级变化,处理频道卡"); + Logger.EAP$LOGGER.info("监听到appflux升级变化,处理频道卡"); // 升级变更,重置并尝试初始化频道卡 eap$compatLastChannel = -1; eap$compatHasInitialized = false; eap$compatInitializeChannelLink(); } } catch (Exception e) { - com.extendedae_plus.util.ExtendedAELogger.LOGGER.error("监听appflux升级变化失败", e); + Logger.EAP$LOGGER.error("监听appflux升级变化失败", e); } } @@ -99,12 +100,12 @@ public abstract class PatternProviderLogicCompatMixin implements IUpgradeableObj at = @At("TAIL")) private void eap$compatInitUpgrades(IManagedGridNode mainNode, PatternProviderLogicHost host, int patternInventorySize, CallbackInfo ci) { try { - com.extendedae_plus.util.ExtendedAELogger.LOGGER.info("兼容性PatternProviderLogic初始化被调用"); + Logger.EAP$LOGGER.info("兼容性PatternProviderLogic初始化被调用"); boolean upgradeSlots = UpgradeSlotCompat.shouldEnableUpgradeSlots(); boolean channelCard = UpgradeSlotCompat.shouldEnableChannelCard(); - com.extendedae_plus.util.ExtendedAELogger.LOGGER.info("升级槽功能: {}, 频道卡功能: {}", upgradeSlots, channelCard); + Logger.EAP$LOGGER.info("升级槽功能: {}, 频道卡功能: {}", upgradeSlots, channelCard); if (upgradeSlots) { // 只有在升级槽功能启用时才创建升级槽 @@ -113,15 +114,15 @@ public abstract class PatternProviderLogicCompatMixin implements IUpgradeableObj 1, this::eap$compatOnUpgradesChanged ); - com.extendedae_plus.util.ExtendedAELogger.LOGGER.info("创建了完整的升级槽"); + Logger.EAP$LOGGER.info("创建了完整的升级槽"); } else if (channelCard) { // 如果装了appflux,我们不创建自己的升级槽,而是监听appflux的升级槽 - com.extendedae_plus.util.ExtendedAELogger.LOGGER.info("装了appflux,将监听其升级槽来处理频道卡"); + Logger.EAP$LOGGER.info("装了appflux,将监听其升级槽来处理频道卡"); } else { - com.extendedae_plus.util.ExtendedAELogger.LOGGER.info("跳过升级槽创建"); + Logger.EAP$LOGGER.info("跳过升级槽创建"); } } catch (Exception e) { - com.extendedae_plus.util.ExtendedAELogger.LOGGER.error("兼容性升级初始化失败", e); + Logger.EAP$LOGGER.error("兼容性升级初始化失败", e); } } @@ -132,7 +133,7 @@ public abstract class PatternProviderLogicCompatMixin implements IUpgradeableObj this.eap$compatUpgrades.writeToNBT(tag, "compat_upgrades"); } } catch (Exception e) { - com.extendedae_plus.util.ExtendedAELogger.LOGGER.error("兼容性升级保存失败", e); + Logger.EAP$LOGGER.error("兼容性升级保存失败", e); } } @@ -149,7 +150,7 @@ public abstract class PatternProviderLogicCompatMixin implements IUpgradeableObj } } } catch (Exception e) { - com.extendedae_plus.util.ExtendedAELogger.LOGGER.error("兼容性升级加载失败", e); + Logger.EAP$LOGGER.error("兼容性升级加载失败", e); } } @@ -164,7 +165,7 @@ public abstract class PatternProviderLogicCompatMixin implements IUpgradeableObj } } } catch (Exception e) { - com.extendedae_plus.util.ExtendedAELogger.LOGGER.error("兼容性升级掉落失败", e); + Logger.EAP$LOGGER.error("兼容性升级掉落失败", e); } } @@ -175,7 +176,7 @@ public abstract class PatternProviderLogicCompatMixin implements IUpgradeableObj this.eap$compatUpgrades.clear(); } } catch (Exception e) { - com.extendedae_plus.util.ExtendedAELogger.LOGGER.error("兼容性升级清理失败", e); + Logger.EAP$LOGGER.error("兼容性升级清理失败", e); } } @@ -187,7 +188,7 @@ public abstract class PatternProviderLogicCompatMixin implements IUpgradeableObj } else { // 装了appflux时,这个方法不应该被调用,因为appflux的Mixin会覆盖它 // 但是为了安全起见,返回空的升级槽 - com.extendedae_plus.util.ExtendedAELogger.LOGGER.debug("装了appflux时getUpgrades被调用,这不应该发生"); + Logger.EAP$LOGGER.debug("装了appflux时getUpgrades被调用,这不应该发生"); return UpgradeInventories.empty(); } } @@ -203,7 +204,7 @@ public abstract class PatternProviderLogicCompatMixin implements IUpgradeableObj eap$compatLink.updateStatus(); } } catch (Exception e) { - com.extendedae_plus.util.ExtendedAELogger.LOGGER.error("兼容性无线链接更新失败", e); + Logger.EAP$LOGGER.error("兼容性无线链接更新失败", e); } } @@ -249,10 +250,10 @@ public abstract class PatternProviderLogicCompatMixin implements IUpgradeableObj if (this instanceof IUpgradeableObject) { IUpgradeableObject upgradeableThis = (IUpgradeableObject) this; upgrades = upgradeableThis.getUpgrades(); - com.extendedae_plus.util.ExtendedAELogger.LOGGER.debug("从appflux获取到升级槽: {}", upgrades != null); + Logger.EAP$LOGGER.debug("从appflux获取到升级槽: {}", upgrades != null); } } catch (Exception e) { - com.extendedae_plus.util.ExtendedAELogger.LOGGER.error("获取appflux升级槽失败", e); + Logger.EAP$LOGGER.error("获取appflux升级槽失败", e); } } @@ -261,7 +262,7 @@ public abstract class PatternProviderLogicCompatMixin implements IUpgradeableObj if (!stack.isEmpty() && stack.getItem() == ModItems.CHANNEL_CARD.get()) { channel = ChannelCardItem.getChannel(stack); found = true; - com.extendedae_plus.util.ExtendedAELogger.LOGGER.info("找到频道卡,频道: {}", channel); + Logger.EAP$LOGGER.info("找到频道卡,频道: {}", channel); break; } } @@ -297,7 +298,7 @@ public abstract class PatternProviderLogicCompatMixin implements IUpgradeableObj } catch (Throwable ignored) {} } } catch (Exception e) { - com.extendedae_plus.util.ExtendedAELogger.LOGGER.error("兼容性频道链接初始化失败", e); + Logger.EAP$LOGGER.error("兼容性频道链接初始化失败", e); } } @@ -321,7 +322,7 @@ public abstract class PatternProviderLogicCompatMixin implements IUpgradeableObj return eap$compatLink != null && eap$compatLink.isConnected(); } } catch (Exception e) { - com.extendedae_plus.util.ExtendedAELogger.LOGGER.error("检查兼容性无线连接状态失败", e); + Logger.EAP$LOGGER.error("检查兼容性无线连接状态失败", e); return false; } } @@ -370,7 +371,7 @@ public abstract class PatternProviderLogicCompatMixin implements IUpgradeableObj } } } catch (Exception e) { - com.extendedae_plus.util.ExtendedAELogger.LOGGER.error("兼容性延迟初始化失败", e); + Logger.EAP$LOGGER.error("兼容性延迟初始化失败", e); } } @@ -390,7 +391,7 @@ public abstract class PatternProviderLogicCompatMixin implements IUpgradeableObj }); } catch (Throwable ignored) {} } catch (Exception e) { - com.extendedae_plus.util.ExtendedAELogger.LOGGER.error("兼容性主节点状态变更处理失败", e); + Logger.EAP$LOGGER.error("兼容性主节点状态变更处理失败", e); } } } diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/compat/PatternProviderScreenCompatMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/compat/PatternProviderScreenCompatMixin.java index eaee82c..32b450f 100644 --- a/src/main/java/com/extendedae_plus/mixin/ae2/compat/PatternProviderScreenCompatMixin.java +++ b/src/main/java/com/extendedae_plus/mixin/ae2/compat/PatternProviderScreenCompatMixin.java @@ -10,6 +10,7 @@ import appeng.core.localization.GuiText; import appeng.menu.SlotSemantics; import appeng.menu.implementations.PatternProviderMenu; import com.extendedae_plus.compat.UpgradeSlotCompat; +import com.extendedae_plus.util.Logger; import net.minecraft.network.chat.Component; import net.minecraft.world.entity.player.Inventory; import org.spongepowered.asm.mixin.Mixin; @@ -38,7 +39,7 @@ public abstract class PatternProviderScreenCompatMixin ((IActionHost)(Object)this).getActionableNode() ); eap$link = new WirelessSlaveLink(endpoint); - ExtendedAELogger.LOGGER.debug("[服务端] IOBus 创建新的无线链接"); + Logger.EAP$LOGGER.debug("[服务端] IOBus 创建新的无线链接"); } eap$link.setFrequency(channel); eap$link.updateStatus(); - ExtendedAELogger.LOGGER.debug("[服务端] IOBus 设置频道: {}, 连接状态: {}", channel, eap$link.isConnected()); + Logger.EAP$LOGGER.debug("[服务端] IOBus 设置频道: {}, 连接状态: {}", channel, eap$link.isConnected()); // 通知客户端状态变化 ((appeng.parts.AEBasePart)(Object)this).getHost().markForUpdate(); } catch (Exception e) { - ExtendedAELogger.LOGGER.error("[服务端] IOBus 初始化频道链接失败", e); + Logger.EAP$LOGGER.error("[服务端] IOBus 初始化频道链接失败", e); } } 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 d54d757..be8ee96 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 @@ -10,7 +10,7 @@ import com.extendedae_plus.ae.wireless.WirelessSlaveLink; import com.extendedae_plus.ae.wireless.endpoint.GenericNodeEndpointImpl; import com.extendedae_plus.api.bridge.IInterfaceWirelessLinkBridge; import com.extendedae_plus.init.ModItems; -import com.extendedae_plus.util.ExtendedAELogger; +import com.extendedae_plus.util.Logger; import net.minecraft.nbt.CompoundTag; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Unique; @@ -84,13 +84,13 @@ public abstract class StorageBusPartChannelCardMixin implements IInterfaceWirele } eap$lastChannel = channel; - ExtendedAELogger.LOGGER.debug("[服务端] StorageBus 初始化频道链接: found={}, channel={}", found, channel); + Logger.EAP$LOGGER.debug("[服务端] StorageBus 初始化频道链接: found={}, channel={}", found, channel); if (!found) { if (eap$link != null) { eap$link.setFrequency(0L); eap$link.updateStatus(); - ExtendedAELogger.LOGGER.debug("[服务端] StorageBus 断开频道链接"); + Logger.EAP$LOGGER.debug("[服务端] StorageBus 断开频道链接"); // 通知客户端状态变化 ((appeng.parts.AEBasePart)(Object)this).getHost().markForUpdate(); } @@ -103,17 +103,17 @@ public abstract class StorageBusPartChannelCardMixin implements IInterfaceWirele () -> ((IActionHost)(Object)this).getActionableNode() ); eap$link = new WirelessSlaveLink(endpoint); - ExtendedAELogger.LOGGER.debug("[服务端] StorageBus 创建新的无线链接"); + Logger.EAP$LOGGER.debug("[服务端] StorageBus 创建新的无线链接"); } eap$link.setFrequency(channel); eap$link.updateStatus(); - ExtendedAELogger.LOGGER.debug("[服务端] StorageBus 设置频道: {}, 连接状态: {}", channel, eap$link.isConnected()); + Logger.EAP$LOGGER.debug("[服务端] StorageBus 设置频道: {}, 连接状态: {}", channel, eap$link.isConnected()); // 通知客户端状态变化 ((appeng.parts.AEBasePart)(Object)this).getHost().markForUpdate(); } catch (Exception e) { - ExtendedAELogger.LOGGER.error("[服务端] StorageBus 初始化频道链接失败", e); + Logger.EAP$LOGGER.error("[服务端] StorageBus 初始化频道链接失败", e); } } diff --git a/src/main/java/com/extendedae_plus/mixin/appflux/AppfluxPatternProviderLogicMixin.java b/src/main/java/com/extendedae_plus/mixin/appflux/AppfluxPatternProviderLogicMixin.java index 201121e..4a22199 100644 --- a/src/main/java/com/extendedae_plus/mixin/appflux/AppfluxPatternProviderLogicMixin.java +++ b/src/main/java/com/extendedae_plus/mixin/appflux/AppfluxPatternProviderLogicMixin.java @@ -6,8 +6,8 @@ import appeng.api.upgrades.UpgradeInventories; import appeng.helpers.patternprovider.PatternProviderLogic; import appeng.helpers.patternprovider.PatternProviderLogicHost; import com.extendedae_plus.compat.UpgradeSlotCompat; +import com.extendedae_plus.util.Logger; import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; @@ -28,11 +28,11 @@ public class AppfluxPatternProviderLogicMixin { at = @At("TAIL")) private void eap$modifyAppfluxUpgradeSlots(IManagedGridNode mainNode, PatternProviderLogicHost host, int patternInventorySize, CallbackInfo ci) { try { - com.extendedae_plus.util.ExtendedAELogger.LOGGER.info("AppfluxPatternProviderLogicMixin被调用!"); + Logger.EAP$LOGGER.info("AppfluxPatternProviderLogicMixin被调用!"); // 只有当appflux存在且不启用我们的升级槽时才修改数量 if (!UpgradeSlotCompat.shouldEnableUpgradeSlots() && UpgradeSlotCompat.shouldEnableChannelCard()) { - com.extendedae_plus.util.ExtendedAELogger.LOGGER.info("尝试修改appflux升级槽数量为2个"); + Logger.EAP$LOGGER.info("尝试修改appflux升级槽数量为2个"); // 使用反射找到appflux的升级槽字段并替换 try { @@ -41,7 +41,7 @@ public class AppfluxPatternProviderLogicMixin { IUpgradeInventory currentUpgrades = (IUpgradeInventory) upgradesField.get(this); if (currentUpgrades != null) { - com.extendedae_plus.util.ExtendedAELogger.LOGGER.info("找到appflux升级槽,当前大小: {}", currentUpgrades.size()); + Logger.EAP$LOGGER.info("找到appflux升级槽,当前大小: {}", currentUpgrades.size()); // 创建新的2槽升级槽 IUpgradeInventory newUpgrades = UpgradeInventories.forMachine( @@ -52,7 +52,7 @@ public class AppfluxPatternProviderLogicMixin { // 调用appflux的升级变更方法 this.getClass().getDeclaredMethod("af_$onUpgradesChanged").invoke(this); } catch (Exception e) { - com.extendedae_plus.util.ExtendedAELogger.LOGGER.error("调用appflux升级变更方法失败", e); + Logger.EAP$LOGGER.error("调用appflux升级变更方法失败", e); } } ); @@ -66,14 +66,14 @@ public class AppfluxPatternProviderLogicMixin { // 替换升级槽 upgradesField.set(this, newUpgrades); - com.extendedae_plus.util.ExtendedAELogger.LOGGER.info("成功将appflux升级槽替换为2个槽"); + Logger.EAP$LOGGER.info("成功将appflux升级槽替换为2个槽"); } } catch (Exception e) { - com.extendedae_plus.util.ExtendedAELogger.LOGGER.error("反射修改appflux升级槽失败", e); + Logger.EAP$LOGGER.error("反射修改appflux升级槽失败", e); } } } catch (Exception e) { - com.extendedae_plus.util.ExtendedAELogger.LOGGER.error("AppfluxPatternProviderLogicMixin执行失败", e); + Logger.EAP$LOGGER.error("AppfluxPatternProviderLogicMixin执行失败", e); } } } 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 6cc4b9d..4b0df62 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 @@ -24,7 +24,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import java.lang.reflect.Field; import java.lang.reflect.Method; -import static com.extendedae_plus.util.ExtendedAELogger.LOGGER; +import static com.extendedae_plus.util.Logger.EAP$LOGGER; @Mixin(GuiExPatternProvider.class) public abstract class GuiExPatternProviderMixin extends PatternProviderScreen implements IExPatternButtonsAccessor, IExPatternPageAccessor { @@ -127,7 +127,7 @@ public abstract class GuiExPatternProviderMixin extends PatternProviderScreen {} (max={})", currentPage, newPage, maxPage); + EAP$LOGGER.info("[EAP] PrevPage clicked: {} -> {} (max={})", currentPage, newPage, maxPage); this.repositionSlots(SlotSemantics.ENCODED_PATTERN); this.repositionSlots(SlotSemantics.STORAGE); this.hoveredSlot = null; @@ -186,7 +186,7 @@ public abstract class GuiExPatternProviderMixin extends PatternProviderScreen {} (max={})", currentPage, newPage, maxPage); + EAP$LOGGER.info("[EAP] NextPage clicked: {} -> {} (max={})", currentPage, newPage, maxPage); this.repositionSlots(SlotSemantics.ENCODED_PATTERN); this.repositionSlots(SlotSemantics.STORAGE); this.hoveredSlot = null; diff --git a/src/main/java/com/extendedae_plus/util/ExtendedAELogger.java b/src/main/java/com/extendedae_plus/util/Logger.java similarity index 55% rename from src/main/java/com/extendedae_plus/util/ExtendedAELogger.java rename to src/main/java/com/extendedae_plus/util/Logger.java index 728f5ab..b7ec4e8 100644 --- a/src/main/java/com/extendedae_plus/util/ExtendedAELogger.java +++ b/src/main/java/com/extendedae_plus/util/Logger.java @@ -1,17 +1,16 @@ package com.extendedae_plus.util; import com.extendedae_plus.ExtendedAEPlus; -import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * 统一日志工具类。 * 在需要的类中可使用: - * import static com.extendedae_plus.util.ExtendedAELogger.LOGGER; + * import static com.extendedae_plus.util.Logger.LOGGER; */ -public final class ExtendedAELogger { - public static final Logger LOGGER = LoggerFactory.getLogger(ExtendedAEPlus.MODID); - private ExtendedAELogger() { +public final class Logger { + public static final org.slf4j.Logger EAP$LOGGER = LoggerFactory.getLogger(ExtendedAEPlus.MODID); + private Logger() { // no instance throw new UnsupportedOperationException("This is a utility class and cannot be instantiated"); } diff --git a/src/main/java/com/extendedae_plus/util/entitySpeed/ConfigParsingUtils.java b/src/main/java/com/extendedae_plus/util/entitySpeed/ConfigParsingUtils.java index 44c9713..1aa0b1f 100644 --- a/src/main/java/com/extendedae_plus/util/entitySpeed/ConfigParsingUtils.java +++ b/src/main/java/com/extendedae_plus/util/entitySpeed/ConfigParsingUtils.java @@ -6,7 +6,7 @@ import java.util.List; import java.util.regex.Pattern; import java.util.regex.PatternSyntaxException; -import static com.extendedae_plus.util.ExtendedAELogger.LOGGER; +import static com.extendedae_plus.util.Logger.EAP$LOGGER; /** * 配置解析工具类:用于解析黑名单与倍率配置的字符串 @@ -29,7 +29,7 @@ public final class ConfigParsingUtils { */ public static Pattern compilePattern(String raw) { if (raw == null || raw.trim().isEmpty()) { - LOGGER.warn("Invalid pattern: {}", raw); + EAP$LOGGER.warn("Invalid pattern: {}", raw); throw new IllegalArgumentException("Pattern is null or empty"); } raw = raw.trim(); @@ -39,7 +39,7 @@ public final class ConfigParsingUtils { try { return Pattern.compile("^" + raw + "$"); } catch (PatternSyntaxException e) { - LOGGER.warn("Failed to compile regex pattern '{}': {}", raw, e.getMessage()); + EAP$LOGGER.warn("Failed to compile regex pattern '{}': {}", raw, e.getMessage()); // Fallback to glob } } @@ -73,7 +73,7 @@ public final class ConfigParsingUtils { if (entry == null || entry.trim().isEmpty()) return null; String[] parts = entry.trim().split("\\s+"); if (parts.length < 2) { - LOGGER.warn("Invalid multiplier entry: {}", entry); + EAP$LOGGER.warn("Invalid multiplier entry: {}", entry); return null; } String key = parts[0]; @@ -83,14 +83,14 @@ public final class ConfigParsingUtils { try { multiplier = Double.parseDouble(val); } catch (NumberFormatException e) { - LOGGER.warn("Invalid multiplier value in '{}': {}", entry, val); + EAP$LOGGER.warn("Invalid multiplier value in '{}': {}", entry, val); return null; } try { Pattern pattern = compilePattern(key); return new MultiplierEntry(pattern, multiplier); } catch (IllegalArgumentException e) { - LOGGER.warn("Failed to compile pattern in '{}': {}", entry, e.getMessage()); + EAP$LOGGER.warn("Failed to compile pattern in '{}': {}", entry, e.getMessage()); return null; } } @@ -125,7 +125,7 @@ public final class ConfigParsingUtils { try { out.add(compilePattern(s)); } catch (IllegalArgumentException e) { - LOGGER.warn("Failed to compile pattern '{}': {}", s, e.getMessage()); + EAP$LOGGER.warn("Failed to compile pattern '{}': {}", s, e.getMessage()); } } return out; diff --git a/src/main/java/com/extendedae_plus/util/entitySpeed/PowerUtils.java b/src/main/java/com/extendedae_plus/util/entitySpeed/PowerUtils.java index 529b40f..feba836 100644 --- a/src/main/java/com/extendedae_plus/util/entitySpeed/PowerUtils.java +++ b/src/main/java/com/extendedae_plus/util/entitySpeed/PowerUtils.java @@ -6,21 +6,89 @@ import com.extendedae_plus.config.ModConfig; import net.minecraft.world.item.ItemStack; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; /** * 用于计算实体加速器的能耗与加速倍率的工具类 */ public final class PowerUtils { - private PowerUtils() {} + private static final int[] VALID_MULTIPLIERS = {2, 4, 8, 16, 32, 64, 128, 256, 512, 1024}; + private static final int[] VALID_ENERGY_CARD_COUNTS = {0, 1, 2, 3, 4, 5, 6, 7, 8}; + private static final AtomicInteger checkCounter = new AtomicInteger(0); // 原子计数器 + private static final int CHECK_COUNTER_THRESHOLD = 100; // 配置检查阈值 + private static final int CHECK_COUNTER_RESET_THRESHOLD = 10000; // 计数器重置阈值 + private static volatile Map> powerCache = new HashMap<>(); + private static volatile Map ratioCache = new HashMap<>(); + private static volatile int lastEntityTickerCost = -1; // 使用 int,与 entityTickerCost 类型一致 + + // 静态初始化块,预计算缓存 + static { + initializeCaches(); + } + + private PowerUtils() { + } + + /** + * 初始化所有可能的缓存条目 + */ + private static void initializeCaches() { + synchronized (PowerUtils.class) { + powerCache.clear(); + ratioCache.clear(); + for (int energyCardCount : VALID_ENERGY_CARD_COUNTS) { + double ratio = + energyCardCount == 0 ? 1.0 : + energyCardCount == 1 ? 0.9 : + energyCardCount == 8 ? 0.5 : + 1.0 - 0.5 * (1.0 - Math.pow(0.7, energyCardCount)); + ratioCache.put(energyCardCount, ratio); + } + for (int product : VALID_MULTIPLIERS) { + Map energyCardMap = new HashMap<>(); + for (int energyCardCount : VALID_ENERGY_CARD_COUNTS) { + double power = computePowerForProduct(product, energyCardCount); + energyCardMap.put(energyCardCount, power); + } + powerCache.put(product, energyCardMap); + } + lastEntityTickerCost = ModConfig.INSTANCE.entityTickerCost; + } + } + + /** + * 计算指定倍率和能源卡数量的能耗 + */ + private static double computePowerForProduct(int product, int energyCardCount) { + double base = ModConfig.INSTANCE.entityTickerCost; + int log2 = Integer.numberOfTrailingZeros(product); + double raw; + if (product == 2) { + raw = base * 4; + } else if (product <= 256) { + raw = base * (1L << (int)(1.5 * log2)) * 2; + } else { + raw = base * (1L << (int)(2.5 * log2)); + } + double ratio = + energyCardCount <= 0 ? 1.0 : + energyCardCount == 1 ? 0.9 : + energyCardCount >= 8 ? 0.5 : + 1.0 - 0.5 * (1.0 - Math.pow(0.7, energyCardCount)); + return raw * ratio / 8.0; + } /** * 计算加速卡的乘积并应用上限。 + * * @param upgrades 升级槽位 * @param maxCards 最大计入的卡数 * @return 被上限约束后的乘积 */ - public static long computeProductWithCap(IUpgradeInventory upgrades, int maxCards) { + public static int computeProductWithCap(IUpgradeInventory upgrades, int maxCards) { List multipliers = new ArrayList<>(); int considered = 0; for (ItemStack stack : upgrades) { @@ -34,7 +102,7 @@ public final class PowerUtils { } } } - long product = 1L; + int product = 1; int highest = 1; for (Integer m : multipliers) { if (m == null || m <= 0) continue; @@ -47,43 +115,69 @@ public final class PowerUtils { /** * 根据最高单卡倍率返回上限值。 */ - 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; + public static int capForHighestMultiplier(int highestMultiplier) { + if (highestMultiplier >= 16) return 1024; + if (highestMultiplier >= 8) return 256; + if (highestMultiplier >= 4) return 64; + if (highestMultiplier >= 2) return 8; + return 1; } /** * 计算最终能耗。 - * @param product 加速卡乘积 + * + * @param product 加速卡乘积 * @param energyCardCount 能量卡数量 * @return 最终能耗值 */ - public static double computeFinalPowerForProduct(long product, int energyCardCount) { - if (product <= 1L) return 0.0; - double base = ModConfig.INSTANCE.entityTickerCost; - 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); + public static double computeFinalPowerForProduct(int product, int energyCardCount) { + if (product <= 1) return 0.0; + // 每 100 次调用检查一次配置 + if (checkCounter.getAndIncrement() % CHECK_COUNTER_THRESHOLD == 0) { + checkCounter.set(checkCounter.get() % CHECK_COUNTER_RESET_THRESHOLD); + if (lastEntityTickerCost != ModConfig.INSTANCE.entityTickerCost) { + synchronized (PowerUtils.class) { + if (lastEntityTickerCost != ModConfig.INSTANCE.entityTickerCost) { + initializeCaches(); + } + } + } } - return raw * getRemainingRatio(energyCardCount) / 8.0; + // 查找缓存 + return getCachedPower(product, energyCardCount); } /** - * 计算能量卡减免后的剩余功耗比率。 + * 直接从缓存获取能耗值(无配置检查)。 + * + * @param product 加速卡乘积 + * @param energyCardCount 能量卡数量 + * @return 缓存的能耗值 */ - 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 double getCachedPower(int product, int energyCardCount) { + Map energyCardMap = powerCache.get(product); + if (energyCardMap == null) { + return 0.0; + } + Double cachedPower = energyCardMap.get(energyCardCount); + if (cachedPower == null) { + return 0.0; + } + return cachedPower; + } + + /** + * 直接从缓存获取比率(无配置检查)。 + * + * @param energyCardCount 能量卡数量 + * @return 缓存的比率值 + */ + public static double getCachedRatio(int energyCardCount) { + Double cachedRatio = ratioCache.get(energyCardCount); + if (cachedRatio == null) { + return 1.0; + } + return cachedRatio; } /** From 2ac589066b8fd38ca2bed2f7454eb340e41d0cef Mon Sep 17 00:00:00 2001 From: C-H716 <1536152356@qq.com> Date: Mon, 29 Sep 2025 14:47:52 +0800 Subject: [PATCH 15/51] =?UTF-8?q?feat:=20=E5=88=9D=E5=A7=8B=E5=8C=96?= =?UTF-8?q?=E7=BC=93=E5=AD=98FE=E5=AD=98=E5=82=A8=E5=8F=8D=E5=B0=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ae/parts/EntitySpeedTickerPart.java | 38 +++++++++++++------ 1 file changed, 26 insertions(+), 12 deletions(-) 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 4d06f38..5ff74f6 100644 --- a/src/main/java/com/extendedae_plus/ae/parts/EntitySpeedTickerPart.java +++ b/src/main/java/com/extendedae_plus/ae/parts/EntitySpeedTickerPart.java @@ -65,6 +65,27 @@ public class EntitySpeedTickerPart extends UpgradeablePart implements IGridTick public EntitySpeedTickerMenu menu; // 当前打开的菜单实例 private boolean networkEnergySufficient = true; // 网络能量是否充足 + private static volatile Method cachedFEExtractMethod; + private static volatile boolean FE_UNAVAILABLE; + + // ===== 静态块:反射初始化 FE 方法 ===== + static { + if (ModList.get().isLoaded("appflux")) { + try { + Class helperClass = Class.forName("com.extendedae_plus.util.FluxEnergyHelper"); + cachedFEExtractMethod = helperClass.getMethod( + "extractFE", + IEnergyService.class, + MEStorage.class, + long.class, + IActionSource.class + ); + FE_UNAVAILABLE = false; + } catch (Exception e) { + FE_UNAVAILABLE = true; + } + } + } /** * 构造函数,初始化部件并设置网络节点属性。 * @param partItem 部件物品 @@ -250,7 +271,7 @@ public class EntitySpeedTickerPart extends UpgradeablePart implements IGridTick private int calculateSpeed() { int entitySpeedCardCount = getUpgrades().getInstalledUpgrades(ModItems.ENTITY_SPEED_CARD.get()); if (entitySpeedCardCount <= 0) return 0; - return (int) PowerUtils.computeProductWithCap(getUpgrades(), 8); + return PowerUtils.computeProductWithCap(getUpgrades(), 8); } /** @@ -302,23 +323,16 @@ public class EntitySpeedTickerPart extends UpgradeablePart implements IGridTick } private boolean tryExtractFE(IEnergyService energyService, MEStorage storage, double requiredPower, IActionSource source) { + if (FE_UNAVAILABLE || cachedFEExtractMethod == null) return false; 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); + long feRequired = (long) requiredPower << 1; + long feExtracted = (long) cachedFEExtractMethod.invoke(null, energyService, storage, feRequired, source); if (feExtracted >= feRequired) { updateNetworkEnergySufficient(true); return true; } } catch (Exception e) { - // 如果反射失败,视为 FE 不可用 + FE_UNAVAILABLE = true; } updateNetworkEnergySufficient(false); return false; From 704f8ebd93b27a8b1960b48843f49e52cd100170 Mon Sep 17 00:00:00 2001 From: C-H716 <1536152356@qq.com> Date: Mon, 29 Sep 2025 15:31:01 +0800 Subject: [PATCH 16/51] =?UTF-8?q?feat:=20=E7=BC=93=E5=AD=98=E5=8A=A0?= =?UTF-8?q?=E9=80=9F=E5=80=8D=E7=8E=87=E3=80=81=E8=83=BD=E6=BA=90=E5=8D=A1?= =?UTF-8?q?=E6=95=B0=E9=87=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ae/parts/EntitySpeedTickerPart.java | 34 +++++++++-------- .../util/entitySpeed/PowerUtils.java | 37 +++++-------------- 2 files changed, 27 insertions(+), 44 deletions(-) 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 5ff74f6..7750640 100644 --- a/src/main/java/com/extendedae_plus/ae/parts/EntitySpeedTickerPart.java +++ b/src/main/java/com/extendedae_plus/ae/parts/EntitySpeedTickerPart.java @@ -64,6 +64,9 @@ public class EntitySpeedTickerPart extends UpgradeablePart implements IGridTick public EntitySpeedTickerMenu menu; // 当前打开的菜单实例 private boolean networkEnergySufficient = true; // 网络能量是否充足 + private int cachedSpeed = -1; // 缓存的加速倍率 + private int cachedEnergyCardCount = -1; // 缓存的能量卡数量 + private static volatile Method cachedFEExtractMethod; private static volatile boolean FE_UNAVAILABLE; @@ -190,6 +193,10 @@ public class EntitySpeedTickerPart extends UpgradeablePart implements IGridTick */ @Override public void upgradesChanged() { + // 更新缓存的升级卡数量和加速倍率 + this.cachedEnergyCardCount = getUpgrades().getInstalledUpgrades(AEItems.ENERGY_CARD); + this.cachedSpeed = calculateSpeed(); + if (menu != null) { menu.broadcastChanges(); } @@ -233,17 +240,24 @@ public class EntitySpeedTickerPart extends UpgradeablePart implements IGridTick return; } - int speed = calculateSpeed(); - if (speed <= 0) { + // 如果缓存未初始化(第一次 tick),计算并更新缓存 + if (cachedEnergyCardCount == -1 || cachedSpeed == -1) { + this.cachedEnergyCardCount = getUpgrades().getInstalledUpgrades(AEItems.ENERGY_CARD); + this.cachedSpeed = calculateSpeed(); + } + + if (cachedSpeed <= 0) { return; } - double requiredPower = calculateRequiredPower(speed, blockId); + // 使用 PowerUtils 的缓存获取能耗,并应用方块特定的倍率 + double requiredPower = PowerUtils.getCachedPower(cachedSpeed, cachedEnergyCardCount) + * ConfigParsingUtils.getMultiplierForBlock(blockId, List.of(ModConfig.INSTANCE.entityTickerMultipliers)); if (!extractPower(requiredPower)) { return; } - performTicks(blockEntity, ticker, speed); + performTicks(blockEntity, ticker, cachedSpeed); } /** @@ -274,18 +288,6 @@ public class EntitySpeedTickerPart extends UpgradeablePart implements IGridTick return PowerUtils.computeProductWithCap(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, List.of(ModConfig.INSTANCE.entityTickerMultipliers)); - return PowerUtils.computeFinalPowerForProduct(speed, energyCardCount) * multiplier; - } - /** * 提取网络能量并更新状态,优先从 AE2 网络提取 AE 能量,不足时从磁盘提取 FE 能量。 * @param requiredPower 所需能量(AE 单位) diff --git a/src/main/java/com/extendedae_plus/util/entitySpeed/PowerUtils.java b/src/main/java/com/extendedae_plus/util/entitySpeed/PowerUtils.java index feba836..ecf44ee 100644 --- a/src/main/java/com/extendedae_plus/util/entitySpeed/PowerUtils.java +++ b/src/main/java/com/extendedae_plus/util/entitySpeed/PowerUtils.java @@ -18,19 +18,18 @@ public final class PowerUtils { private static final int[] VALID_MULTIPLIERS = {2, 4, 8, 16, 32, 64, 128, 256, 512, 1024}; private static final int[] VALID_ENERGY_CARD_COUNTS = {0, 1, 2, 3, 4, 5, 6, 7, 8}; private static final AtomicInteger checkCounter = new AtomicInteger(0); // 原子计数器 - private static final int CHECK_COUNTER_THRESHOLD = 100; // 配置检查阈值 + private static final int CHECK_COUNTER_THRESHOLD = 100; // 配置检查阈值 private static final int CHECK_COUNTER_RESET_THRESHOLD = 10000; // 计数器重置阈值 private static volatile Map> powerCache = new HashMap<>(); private static volatile Map ratioCache = new HashMap<>(); - private static volatile int lastEntityTickerCost = -1; // 使用 int,与 entityTickerCost 类型一致 + private static volatile int lastEntityTickerCost = -1; // 静态初始化块,预计算缓存 static { initializeCaches(); } - private PowerUtils() { - } + private PowerUtils() {} /** * 初始化所有可能的缓存条目 @@ -124,13 +123,13 @@ public final class PowerUtils { } /** - * 计算最终能耗。 + * 直接从缓存获取能耗值 * * @param product 加速卡乘积 * @param energyCardCount 能量卡数量 - * @return 最终能耗值 + * @return 缓存的能耗值 */ - public static double computeFinalPowerForProduct(int product, int energyCardCount) { + public static double getCachedPower(int product, int energyCardCount) { if (product <= 1) return 0.0; // 每 100 次调用检查一次配置 if (checkCounter.getAndIncrement() % CHECK_COUNTER_THRESHOLD == 0) { @@ -143,26 +142,10 @@ public final class PowerUtils { } } } - // 查找缓存 - return getCachedPower(product, energyCardCount); - } - - /** - * 直接从缓存获取能耗值(无配置检查)。 - * - * @param product 加速卡乘积 - * @param energyCardCount 能量卡数量 - * @return 缓存的能耗值 - */ - public static double getCachedPower(int product, int energyCardCount) { Map energyCardMap = powerCache.get(product); - if (energyCardMap == null) { - return 0.0; - } + if (energyCardMap == null) return 0.0; Double cachedPower = energyCardMap.get(energyCardCount); - if (cachedPower == null) { - return 0.0; - } + if (cachedPower == null) return 0.0; return cachedPower; } @@ -174,9 +157,7 @@ public final class PowerUtils { */ public static double getCachedRatio(int energyCardCount) { Double cachedRatio = ratioCache.get(energyCardCount); - if (cachedRatio == null) { - return 1.0; - } + if (cachedRatio == null) return 1.0; return cachedRatio; } From ef4337d2c342c1d8e05f5be70e4ce16c5f01409f Mon Sep 17 00:00:00 2001 From: C-H716 <1536152356@qq.com> Date: Mon, 29 Sep 2025 19:53:33 +0800 Subject: [PATCH 17/51] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E7=BC=93?= =?UTF-8?q?=E5=AD=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ae/menu/EntitySpeedTickerMenu.java | 7 +- .../ae/parts/EntitySpeedTickerPart.java | 5 +- .../util/entitySpeed/ConfigParsingUtils.java | 178 ++++++++++-------- 3 files changed, 106 insertions(+), 84 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 008a9ff..e8f7d2a 100644 --- a/src/main/java/com/extendedae_plus/ae/menu/EntitySpeedTickerMenu.java +++ b/src/main/java/com/extendedae_plus/ae/menu/EntitySpeedTickerMenu.java @@ -6,7 +6,6 @@ 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.ModConfig; import com.extendedae_plus.init.ModItems; import com.extendedae_plus.init.ModMenuTypes; import com.extendedae_plus.util.entitySpeed.ConfigParsingUtils; @@ -17,8 +16,6 @@ import net.minecraft.world.inventory.Slot; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraftforge.registries.ForgeRegistries; -import java.util.List; - /** * 实体加速器菜单,负责管理客户端与服务端的数据同步,处理加速卡、能量卡和目标方块的状态。 */ @@ -135,8 +132,8 @@ public class EntitySpeedTickerMenu extends UpgradeableMenu BLACKLIST_SUPPLIER = () -> ModConfig.INSTANCE.entityTickerBlackList; + private static final Supplier MULTIPLIERS_SUPPLIER = () -> ModConfig.INSTANCE.entityTickerMultipliers; + + // 缓存字段 + private static volatile Map cachedBlacklist = null; + private static volatile Map cachedMultiplierEntries = null; + private static volatile String[] cachedBlacklistSourceSnapshot = null; + private static volatile String[] cachedMultiplierSourceSnapshot = null; + private static volatile Map blacklistResultCache = new HashMap<>(); + private static volatile Map multiplierResultCache = new HashMap<>(); + private static final Object CACHE_LOCK = new Object(); + private static final AtomicLong callCounter = new AtomicLong(0); + private static final long CHECK_INTERVAL = 100; + + /* + 初始化缓存,在模组加载时调用 + */ + static { + reload(); } /** @@ -98,116 +117,120 @@ public final class ConfigParsingUtils { /** * 检查方块是否在黑名单中。 */ - 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 boolean isBlockBlacklisted(String blockId) { + if (blockId == null || BLACKLIST_SUPPLIER.get() == null || BLACKLIST_SUPPLIER.get().length == 0) { + return false; + } + checkConfigChange(); + return blacklistResultCache.computeIfAbsent(blockId, id -> + getCachedBlacklist().values().stream().anyMatch(p -> p.matcher(id).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); - } + public static double getMultiplierForBlock(String blockId) { + if (blockId == null || MULTIPLIERS_SUPPLIER.get() == null || MULTIPLIERS_SUPPLIER.get().length == 0) { + return 1.0; } - return maxMultiplier; + checkConfigChange(); + return multiplierResultCache.computeIfAbsent(blockId, id -> { + double maxMultiplier = 1.0; + for (MultiplierEntry me : getCachedMultiplierEntries().values()) { + if (me.pattern.matcher(id).matches()) { + maxMultiplier = Math.max(maxMultiplier, me.multiplier); + } + } + return maxMultiplier; + }); } - public static List compilePatterns(List raw) { - List out = new ArrayList<>(); + /** + * 编译黑名单配置为 Map。 + */ + public static Map compilePatterns(String[] raw) { + Map out = new HashMap<>(); if (raw == null) return out; for (String s : raw) { if (s == null || s.trim().isEmpty()) continue; + String trimmed = s.trim(); try { - out.add(compilePattern(s)); + out.put(trimmed, compilePattern(trimmed)); } catch (IllegalArgumentException e) { - EAP$LOGGER.warn("Failed to compile pattern '{}': {}", s, e.getMessage()); + EAP$LOGGER.warn("Failed to compile pattern '{}': {}", trimmed, e.getMessage()); } } return out; } - public static List parseMultiplierList(List raw) { - List out = new ArrayList<>(); + /** + * 解析倍率配置为 Map。 + */ + public static Map parseMultiplierList(String[] raw) { + Map out = new HashMap<>(); if (raw == null) return out; for (String s : raw) { - MultiplierEntry me = parseMultiplierEntry(s); - if (me != null) out.add(me); + if (s == null || s.trim().isEmpty()) continue; + String trimmed = s.trim(); + MultiplierEntry me = parseMultiplierEntry(trimmed); + if (me != null) out.put(trimmed, me); } return out; } - // ------------------ 全局缓存与接口 ------------------ - 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) { - List normalized = normalizeSource(source); - if (cachedBlacklist != null && listEquals(cachedBlacklistSourceSnapshot, normalized)) { - return Collections.unmodifiableList(cachedBlacklist); + private static Map getCachedBlacklist() { + String[] source = BLACKLIST_SUPPLIER.get(); + if (cachedBlacklist != null && Arrays.equals(cachedBlacklistSourceSnapshot, source)) { + return Collections.unmodifiableMap(cachedBlacklist); } synchronized (CACHE_LOCK) { - if (cachedBlacklist == null || !listEquals(cachedBlacklistSourceSnapshot, normalized)) { - cachedBlacklist = compilePatterns(normalized); - cachedBlacklistSourceSnapshot = normalized.isEmpty() ? Collections.emptyList() : new ArrayList<>(normalized); + if (cachedBlacklist == null || !Arrays.equals(cachedBlacklistSourceSnapshot, source)) { + cachedBlacklist = compilePatterns(source); + cachedBlacklistSourceSnapshot = source == null ? new String[0] : source.clone(); } - return Collections.unmodifiableList(cachedBlacklist); + return Collections.unmodifiableMap(cachedBlacklist); } } /** * 获取已解析并缓存的倍率列表(线程安全、懒加载)。 */ - public static List getCachedMultiplierEntries(List source) { - List normalized = normalizeSource(source); - if (cachedMultiplierEntries != null && listEquals(cachedMultiplierSourceSnapshot, normalized)) { - return Collections.unmodifiableList(cachedMultiplierEntries); + private static Map getCachedMultiplierEntries() { + String[] source = MULTIPLIERS_SUPPLIER.get(); + if (cachedMultiplierEntries != null && Arrays.equals(cachedMultiplierSourceSnapshot, source)) { + return Collections.unmodifiableMap(cachedMultiplierEntries); } synchronized (CACHE_LOCK) { - if (cachedMultiplierEntries == null || !listEquals(cachedMultiplierSourceSnapshot, normalized)) { - cachedMultiplierEntries = parseMultiplierList(normalized); - cachedMultiplierSourceSnapshot = normalized.isEmpty() ? Collections.emptyList() : new ArrayList<>(normalized); + if (cachedMultiplierEntries == null || !Arrays.equals(cachedMultiplierSourceSnapshot, source)) { + cachedMultiplierEntries = parseMultiplierList(source); + cachedMultiplierSourceSnapshot = source == null ? new String[0] : source.clone(); } - return Collections.unmodifiableList(cachedMultiplierEntries); + return Collections.unmodifiableMap(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; - } - /** - * 清空缓存,下一次获取时将重新从提供的源解析(或调用方可以重新调用 getter)。 + * 每 100 次调用检查配置是否变化。 + */ + private static void checkConfigChange() { + if (callCounter.incrementAndGet() % CHECK_INTERVAL == 0) { + String[] currentBlacklist = BLACKLIST_SUPPLIER.get(); + String[] currentMultipliers = MULTIPLIERS_SUPPLIER.get(); + synchronized (CACHE_LOCK) { + if (cachedBlacklistSourceSnapshot == null || !Arrays.equals(cachedBlacklistSourceSnapshot, currentBlacklist) || + cachedMultiplierSourceSnapshot == null || !Arrays.equals(cachedMultiplierSourceSnapshot, currentMultipliers)) { + reload(); + } + } + } + } + + /** + * 清空缓存,下一次获取时将重新解析。 */ public static void reload() { synchronized (CACHE_LOCK) { @@ -215,6 +238,9 @@ public final class ConfigParsingUtils { cachedMultiplierEntries = null; cachedBlacklistSourceSnapshot = null; cachedMultiplierSourceSnapshot = null; + blacklistResultCache.clear(); + multiplierResultCache.clear(); + callCounter.set(0); } } } \ No newline at end of file From 72d0bbeaff13df58e193535be1cd00476c7a2744 Mon Sep 17 00:00:00 2001 From: C-H716 <1536152356@qq.com> Date: Mon, 29 Sep 2025 21:00:08 +0800 Subject: [PATCH 18/51] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E7=BC=93?= =?UTF-8?q?=E5=AD=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ae/parts/EntitySpeedTickerPart.java | 60 ++++++++----------- 1 file changed, 24 insertions(+), 36 deletions(-) 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 b012eda..f2df8a6 100644 --- a/src/main/java/com/extendedae_plus/ae/parts/EntitySpeedTickerPart.java +++ b/src/main/java/com/extendedae_plus/ae/parts/EntitySpeedTickerPart.java @@ -296,45 +296,33 @@ public class EntitySpeedTickerPart extends UpgradeablePart implements IGridTick 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 && ModConfig.INSTANCE.prioritizeDiskEnergy; - // 如果 appflux 存在且优先磁盘能量,尝试提取 FE 能量 - if (appFluxLoaded && preferDiskEnergy) { - if (tryExtractFE(energyService, storage, requiredPower, source)) { - return true; + // 优先级:FE(如果优先磁盘) -> AE -> FE(如果未优先磁盘) + boolean[] attempts = ModConfig.INSTANCE.prioritizeDiskEnergy ? new boolean[]{true, false, true} : new boolean[]{false, true, false}; + + for (int i = 0; i < attempts.length; i++) { + if (!attempts[i]) continue; + if (i == 0 || i == 2) { // FE 提取 + if (!FE_UNAVAILABLE && cachedFEExtractMethod != null) { + try { + long feRequired = (long) requiredPower << 1; + long feExtracted = (long) cachedFEExtractMethod.invoke(null, energyService, storage, feRequired, source); + if (feExtracted >= feRequired) { + updateNetworkEnergySufficient(true); + return true; + } + } catch (Exception e) { + FE_UNAVAILABLE = true; + } + } + } else { // AE 提取 + double extracted = energyService.extractAEPower(requiredPower, Actionable.MODULATE, PowerMultiplier.CONFIG); + if (extracted >= requiredPower) { + updateNetworkEnergySufficient(true); + return true; + } } } - - // 尝试提取 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); - - // 如果 appflux 存在且优先 AE 能量,尝试提取 FE 能量作为备用 - if (appFluxLoaded && !preferDiskEnergy) { - return tryExtractFE(energyService, storage, requiredPower, source); - } - return false; - } - - private boolean tryExtractFE(IEnergyService energyService, MEStorage storage, double requiredPower, IActionSource source) { - if (FE_UNAVAILABLE || cachedFEExtractMethod == null) return false; - try { - long feRequired = (long) requiredPower << 1; - long feExtracted = (long) cachedFEExtractMethod.invoke(null, energyService, storage, feRequired, source); - if (feExtracted >= feRequired) { - updateNetworkEnergySufficient(true); - return true; - } - } catch (Exception e) { - FE_UNAVAILABLE = true; - } updateNetworkEnergySufficient(false); return false; } From 636050039db6c79f8a4f5cde3769b46ede41c23e Mon Sep 17 00:00:00 2001 From: C-H716 <1536152356@qq.com> Date: Mon, 29 Sep 2025 21:57:24 +0800 Subject: [PATCH 19/51] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E7=BC=93?= =?UTF-8?q?=E5=AD=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ae/parts/EntitySpeedTickerPart.java | 30 +++++++++++++++---- 1 file changed, 25 insertions(+), 5 deletions(-) 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 f2df8a6..5a16d5b 100644 --- a/src/main/java/com/extendedae_plus/ae/parts/EntitySpeedTickerPart.java +++ b/src/main/java/com/extendedae_plus/ae/parts/EntitySpeedTickerPart.java @@ -28,6 +28,7 @@ import com.extendedae_plus.init.ModItems; import com.extendedae_plus.init.ModMenuTypes; import com.extendedae_plus.util.entitySpeed.ConfigParsingUtils; import com.extendedae_plus.util.entitySpeed.PowerUtils; +import net.minecraft.core.BlockPos; import net.minecraft.network.chat.Component; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.InteractionHand; @@ -65,7 +66,8 @@ public class EntitySpeedTickerPart extends UpgradeablePart implements IGridTick private boolean networkEnergySufficient = true; // 网络能量是否充足 private int cachedSpeed = -1; // 缓存的加速倍率 private int cachedEnergyCardCount = -1; // 缓存的能量卡数量 - + private BlockEntity cachedTarget = null; + private BlockPos cachedTargetPos = null; private static volatile Method cachedFEExtractMethod; private static volatile boolean FE_UNAVAILABLE; @@ -209,12 +211,12 @@ public class EntitySpeedTickerPart extends UpgradeablePart implements IGridTick */ @Override public TickRateModulation tickingRequest(IGridNode iGridNode, int ticksSinceLastCall) { - if (!this.getAccelerateEnabled()) { + if (!getAccelerateEnabled()) { return TickRateModulation.IDLE; } - BlockEntity target = getLevel().getBlockEntity(getBlockEntity().getBlockPos().relative(getSide())); - if (target != null && isActive()) { - ticker(target); + updateCachedTarget(); + if (cachedTarget != null && isActive()) { + ticker(cachedTarget); } return TickRateModulation.IDLE; } @@ -267,6 +269,17 @@ public class EntitySpeedTickerPart extends UpgradeablePart implements IGridTick return getGridNode() != null && getMainNode() != null && getMainNode().getGrid() != null; } + /** + * 更新缓存的目标方块实体引用。 + */ + private void updateCachedTarget() { + BlockPos targetPos = getBlockEntity().getBlockPos().relative(getSide()); + if (!targetPos.equals(cachedTargetPos) || cachedTarget == null || cachedTarget.isRemoved() || + cachedTarget.getType() != getLevel().getBlockEntity(targetPos).getType()) { + cachedTargetPos = targetPos; + cachedTarget = getLevel().getBlockEntity(targetPos); + } + } /** * 获取目标方块实体的 ticker。 * @param blockEntity 目标方块实体 @@ -379,4 +392,11 @@ public class EntitySpeedTickerPart extends UpgradeablePart implements IGridTick protected int getUpgradeSlots() { return 8; } + + @Override + public void removeFromWorld() { + super.removeFromWorld(); + cachedTarget = null; + cachedTargetPos = null; + } } \ No newline at end of file From e43522d3c89f437280580e53fbdac4bd5bede990 Mon Sep 17 00:00:00 2001 From: C-H716 <1536152356@qq.com> Date: Tue, 30 Sep 2025 09:17:33 +0800 Subject: [PATCH 20/51] =?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=99=A8=E7=9B=B8=E5=85=B3=E9=85=8D?= =?UTF-8?q?=E7=BD=AE=E7=BC=93=E5=AD=98=E5=88=B7=E6=96=B0=E6=9C=BA=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ae/parts/EntitySpeedTickerPart.java | 85 ++++++++++++------- .../com/extendedae_plus/config/ModConfig.java | 66 ++++++++++++-- .../PatternProviderLogicCompatMixin.java | 11 --- .../AppfluxPatternProviderLogicMixin.java | 7 -- .../util/entitySpeed/ConfigParsingUtils.java | 22 ----- .../util/entitySpeed/PowerUtils.java | 19 +---- 6 files changed, 116 insertions(+), 94 deletions(-) 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 5a16d5b..d541e22 100644 --- a/src/main/java/com/extendedae_plus/ae/parts/EntitySpeedTickerPart.java +++ b/src/main/java/com/extendedae_plus/ae/parts/EntitySpeedTickerPart.java @@ -51,7 +51,7 @@ import java.lang.reflect.Method; * 实体加速器部件,用于加速目标方块实体的 tick 速率,消耗 AE 网络能量,支持加速卡和能量卡升级。 * 灵感来源于 Crazy AE2 Addons。 */ -public class EntitySpeedTickerPart extends UpgradeablePart implements IGridTickable, MenuProvider, IUpgradeableObject { +public class EntitySpeedTickerPart extends UpgradeablePart implements IGridTickable, MenuProvider, IUpgradeableObject { public static final ResourceLocation MODEL_BASE = new ResourceLocation( ExtendedAEPlus.MODID, "part/entity_speed_ticker_part"); @@ -62,18 +62,14 @@ public class EntitySpeedTickerPart extends UpgradeablePart implements IGridTick @PartModels public static final PartModel MODELS_HAS_CHANNEL = new PartModel(MODEL_BASE, new ResourceLocation(ExtendedAEPlus.MODID, "part/entity_speed_ticker_has_channel")); - public EntitySpeedTickerMenu menu; // 当前打开的菜单实例 - private boolean networkEnergySufficient = true; // 网络能量是否充足 - private int cachedSpeed = -1; // 缓存的加速倍率 - private int cachedEnergyCardCount = -1; // 缓存的能量卡数量 - private BlockEntity cachedTarget = null; - private BlockPos cachedTargetPos = null; + private static volatile boolean[] cachedAttempts; private static volatile Method cachedFEExtractMethod; private static volatile boolean FE_UNAVAILABLE; - // ===== 静态块:反射初始化 FE 方法 ===== + // 静态块:初始化缓存 static { + cachedAttempts = ModConfig.INSTANCE.prioritizeDiskEnergy ? new boolean[]{true, false, true} : new boolean[]{false, true, true}; if (ModList.get().isLoaded("appflux")) { try { Class helperClass = Class.forName("com.extendedae_plus.util.FluxEnergyHelper"); @@ -90,8 +86,17 @@ public class EntitySpeedTickerPart extends UpgradeablePart implements IGridTick } } } + + public EntitySpeedTickerMenu menu; // 当前打开的菜单实例 + private boolean networkEnergySufficient = true; // 网络能量是否充足 + private int cachedSpeed = -1; // 缓存的加速倍率 + private int cachedEnergyCardCount = -1; // 缓存的能量卡数量 + private BlockEntity cachedTarget = null; + private BlockPos cachedTargetPos = null; + /** * 构造函数,初始化部件并设置网络节点属性。 + * * @param partItem 部件物品 */ public EntitySpeedTickerPart(IPartItem partItem) { @@ -108,17 +113,26 @@ public class EntitySpeedTickerPart extends UpgradeablePart implements IGridTick ); } + /** + * 更新缓存的 attempts 数组,由 ModConfig 调用。 + */ + public static void updateCachedAttempts(boolean prioritizeDiskEnergy) { + synchronized (EntitySpeedTickerPart.class) { + cachedAttempts = prioritizeDiskEnergy ? new boolean[]{true, false, true} : new boolean[]{false, true, true}; + } + } + public boolean getAccelerateEnabled() { return this.getConfigManager().getSetting(com.extendedae_plus.ae.api.config.Settings.ACCELERATE) == YesNo.YES; } /** * 设置加速开关状态并通知菜单。 + * * @param enabled 是否启用加速 */ public void setAccelerateEnabled(boolean enabled) { this.getConfigManager().putSetting(com.extendedae_plus.ae.api.config.Settings.ACCELERATE, enabled ? YesNo.YES : YesNo.NO); - // 是否启用加速 if (menu != null) { menu.setAccelerateEnabled(enabled); } @@ -130,6 +144,7 @@ public class EntitySpeedTickerPart extends UpgradeablePart implements IGridTick /** * 更新网络能量充足状态并通知菜单。 + * * @param sufficient 是否能量充足 */ private void updateNetworkEnergySufficient(boolean sufficient) { @@ -141,6 +156,7 @@ public class EntitySpeedTickerPart extends UpgradeablePart implements IGridTick /** * 获取当前状态的渲染模型。 + * * @return 当前状态的模型 */ @Override @@ -156,9 +172,10 @@ public class EntitySpeedTickerPart extends UpgradeablePart implements IGridTick /** * 处理玩家右键激活部件,打开菜单。 + * * @param player 玩家 - * @param hand 手 - * @param pos 点击位置 + * @param hand 手 + * @param pos 点击位置 * @return 总是返回 true */ @Override @@ -171,6 +188,7 @@ public class EntitySpeedTickerPart extends UpgradeablePart implements IGridTick /** * 定义部件的碰撞箱。 + * * @param bch 碰撞辅助器 */ @Override @@ -181,6 +199,7 @@ public class EntitySpeedTickerPart extends UpgradeablePart implements IGridTick /** * 获取定时请求,指定 tick 频率。 + * * @param iGridNode 网络节点 * @return TickingRequest 对象 */ @@ -205,7 +224,8 @@ public class EntitySpeedTickerPart extends UpgradeablePart implements IGridTick /** * 网络定时回调,处理目标方块实体的加速。 - * @param iGridNode 网络节点 + * + * @param iGridNode 网络节点 * @param ticksSinceLastCall 距离上次调用的 tick 数 * @return TickRateModulation.IDLE */ @@ -223,8 +243,9 @@ public class EntitySpeedTickerPart extends UpgradeablePart implements IGridTick /** * 对目标方块实体执行加速 tick 操作。 + * * @param blockEntity 目标方块实体 - * @param 方块实体类型 + * @param 方块实体类型 */ private void ticker(@NotNull T blockEntity) { if (!isValidForTicking()) { @@ -241,7 +262,6 @@ public class EntitySpeedTickerPart extends UpgradeablePart implements IGridTick return; } - // 如果缓存未初始化(第一次 tick),计算并更新缓存 if (cachedEnergyCardCount == -1 || cachedSpeed == -1) { this.cachedEnergyCardCount = getUpgrades().getInstalledUpgrades(AEItems.ENERGY_CARD); this.cachedSpeed = calculateSpeed(); @@ -251,7 +271,6 @@ public class EntitySpeedTickerPart extends UpgradeablePart implements IGridTick return; } - // 使用 PowerUtils 的缓存获取能耗,并应用方块特定的倍率 double requiredPower = PowerUtils.getCachedPower(cachedSpeed, cachedEnergyCardCount) * ConfigParsingUtils.getMultiplierForBlock(blockId); if (!extractPower(requiredPower)) { @@ -263,6 +282,7 @@ public class EntitySpeedTickerPart extends UpgradeablePart implements IGridTick /** * 检查网络节点是否有效。 + * * @return 是否可以执行 tick */ private boolean isValidForTicking() { @@ -280,8 +300,10 @@ public class EntitySpeedTickerPart extends UpgradeablePart implements IGridTick cachedTarget = getLevel().getBlockEntity(targetPos); } } + /** * 获取目标方块实体的 ticker。 + * * @param blockEntity 目标方块实体 * @return ticker 或 null */ @@ -292,6 +314,7 @@ public class EntitySpeedTickerPart extends UpgradeablePart implements IGridTick /** * 计算加速倍率。 + * * @return 生效的加速倍率 */ private int calculateSpeed() { @@ -302,6 +325,7 @@ public class EntitySpeedTickerPart extends UpgradeablePart implements IGridTick /** * 提取网络能量并更新状态,优先从 AE2 网络提取 AE 能量,不足时从磁盘提取 FE 能量。 + * * @param requiredPower 所需能量(AE 单位) * @return 是否成功提取足够能量 */ @@ -310,11 +334,8 @@ public class EntitySpeedTickerPart extends UpgradeablePart implements IGridTick MEStorage storage = getMainNode().getGrid().getStorageService().getInventory(); IActionSource source = IActionSource.ofMachine(this); - // 优先级:FE(如果优先磁盘) -> AE -> FE(如果未优先磁盘) - boolean[] attempts = ModConfig.INSTANCE.prioritizeDiskEnergy ? new boolean[]{true, false, true} : new boolean[]{false, true, false}; - - for (int i = 0; i < attempts.length; i++) { - if (!attempts[i]) continue; + for (int i = 0; i < cachedAttempts.length; i++) { + if (!cachedAttempts[i]) continue; if (i == 0 || i == 2) { // FE 提取 if (!FE_UNAVAILABLE && cachedFEExtractMethod != null) { try { @@ -329,10 +350,14 @@ public class EntitySpeedTickerPart extends UpgradeablePart implements IGridTick } } } else { // AE 提取 - double extracted = energyService.extractAEPower(requiredPower, Actionable.MODULATE, PowerMultiplier.CONFIG); - if (extracted >= requiredPower) { - updateNetworkEnergySufficient(true); - return true; + 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); + if (sufficient) { + return true; + } } } } @@ -342,14 +367,14 @@ public class EntitySpeedTickerPart extends UpgradeablePart implements IGridTick /** * 执行加速 tick 操作。 + * * @param blockEntity 目标方块实体 - * @param ticker 方块实体 ticker - * @param speed 加速倍率 + * @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++) { ticker.tick( blockEntity.getLevel(), @@ -372,9 +397,10 @@ public class EntitySpeedTickerPart extends UpgradeablePart implements IGridTick /** * 创建菜单实例。 - * @param containerId 容器ID + * + * @param containerId 容器ID * @param playerInventory 玩家背包 - * @param player 玩家 + * @param player 玩家 * @return 菜单实例 */ @Override @@ -386,6 +412,7 @@ public class EntitySpeedTickerPart extends UpgradeablePart implements IGridTick /** * 获取升级卡槽数量。 + * * @return 升级卡槽数量 */ @Override diff --git a/src/main/java/com/extendedae_plus/config/ModConfig.java b/src/main/java/com/extendedae_plus/config/ModConfig.java index 173ae83..5d33380 100644 --- a/src/main/java/com/extendedae_plus/config/ModConfig.java +++ b/src/main/java/com/extendedae_plus/config/ModConfig.java @@ -1,18 +1,30 @@ package com.extendedae_plus.config; import com.extendedae_plus.ExtendedAEPlus; +import com.extendedae_plus.ae.parts.EntitySpeedTickerPart; +import com.extendedae_plus.util.entitySpeed.ConfigParsingUtils; +import com.extendedae_plus.util.entitySpeed.PowerUtils; import dev.toma.configuration.Configuration; +import dev.toma.configuration.client.IValidationHandler; import dev.toma.configuration.config.Config; import dev.toma.configuration.config.Configurable; import dev.toma.configuration.config.format.ConfigFormats; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicLong; + +import static com.extendedae_plus.util.Logger.EAP$LOGGER; @Config(id = ExtendedAEPlus.MODID) public final class ModConfig { public static ModConfig INSTANCE; private static final Object lock = new Object(); - + private static final long DEBOUNCE_INTERVAL = 1000; // 防抖间隔,单位:毫秒 + private static final AtomicLong lastUpdateTime = new AtomicLong(0); public static void init() { synchronized (lock) { @@ -21,6 +33,7 @@ public final class ModConfig { } } } + @Configurable @Configurable.Comment(value = { "扩展样板供应器总槽位容量的倍率。", @@ -95,6 +108,7 @@ public final class ModConfig { }) @Configurable.Range(min = 0, max = Integer.MAX_VALUE) @Configurable.Synchronized + @Configurable.ValueUpdateCallback(method = "onEntityTickerCostUpdate") public int entityTickerCost = 512; @Configurable @@ -103,9 +117,8 @@ public final class ModConfig { "格式:全名或通配符/正则字符串,例如 'minecraft:chest'、'minecraft:*'、'modid:.*_fluid'" }) @Configurable.Synchronized - public String[] entityTickerBlackList = { - - }; + @Configurable.ValueUpdateCallback(method = "onEntityTickerBlackListUpdate") + public String[] entityTickerBlackList = {}; @Configurable @Configurable.Comment(value = { @@ -113,9 +126,8 @@ public final class ModConfig { "支持通配符/正则匹配(例如 'minecraft:* 2x' 会对整个命名空间生效)。" }) @Configurable.Synchronized - public String[] entityTickerMultipliers = { - - }; + @Configurable.ValueUpdateCallback(method = "onEntityTickerMultipliersUpdate") + public String[] entityTickerMultipliers = {}; @Configurable @Configurable.Comment(value = { @@ -131,5 +143,45 @@ public final class ModConfig { "开启后,将优先尝试从磁盘提取FE能量;反之优先消耗AE网络中的能量" }) @Configurable.Synchronized + @Configurable.ValueUpdateCallback(method = "onPrioritizeDiskEnergyUpdate") public boolean prioritizeDiskEnergy = true; + + private static final ScheduledExecutorService EXECUTOR = Executors.newSingleThreadScheduledExecutor(); + private static ScheduledFuture pendingPowerTask; + private static final Object POWER_LOCK = new Object(); + + private void onEntityTickerCostUpdate(int newValue, IValidationHandler handler) { + synchronized (POWER_LOCK) { + if (pendingPowerTask != null) { + pendingPowerTask.cancel(false); + } + pendingPowerTask = EXECUTOR.schedule(() -> { + synchronized (PowerUtils.class) { + PowerUtils.initializeCaches(); + } + }, 1000, TimeUnit.MILLISECONDS); // 1000ms 防抖 + } + } + + + private void onEntityTickerBlackListUpdate(String[] newValue, IValidationHandler handler) { + synchronized (ConfigParsingUtils.class) { + EAP$LOGGER.info("onEntityTickerBlackListUpdate"); + ConfigParsingUtils.reload(); + } + } + + private void onEntityTickerMultipliersUpdate(String[] newValue, IValidationHandler handler) { + synchronized (ConfigParsingUtils.class) { + EAP$LOGGER.info("onEntityTickerMultipliersUpdate"); + ConfigParsingUtils.reload(); + } + } + + private void onPrioritizeDiskEnergyUpdate(boolean newValue, dev.toma.configuration.client.IValidationHandler handler) { + synchronized (EntitySpeedTickerPart.class) { + EAP$LOGGER.info("onPrioritizeDiskEnergyUpdate"); + EntitySpeedTickerPart.updateCachedAttempts(newValue); + } + } } \ No newline at end of file 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 edebb1c..25a4cfc 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 @@ -85,7 +85,6 @@ public abstract class PatternProviderLogicCompatMixin implements IUpgradeableObj private void eap$onAppfluxUpgradesChanged(CallbackInfo ci) { try { if (UpgradeSlotCompat.shouldEnableChannelCard()) { - Logger.EAP$LOGGER.info("监听到appflux升级变化,处理频道卡"); // 升级变更,重置并尝试初始化频道卡 eap$compatLastChannel = -1; eap$compatHasInitialized = false; @@ -100,13 +99,9 @@ public abstract class PatternProviderLogicCompatMixin implements IUpgradeableObj at = @At("TAIL")) private void eap$compatInitUpgrades(IManagedGridNode mainNode, PatternProviderLogicHost host, int patternInventorySize, CallbackInfo ci) { try { - Logger.EAP$LOGGER.info("兼容性PatternProviderLogic初始化被调用"); - boolean upgradeSlots = UpgradeSlotCompat.shouldEnableUpgradeSlots(); boolean channelCard = UpgradeSlotCompat.shouldEnableChannelCard(); - Logger.EAP$LOGGER.info("升级槽功能: {}, 频道卡功能: {}", upgradeSlots, channelCard); - if (upgradeSlots) { // 只有在升级槽功能启用时才创建升级槽 this.eap$compatUpgrades = UpgradeInventories.forMachine( @@ -114,12 +109,9 @@ public abstract class PatternProviderLogicCompatMixin implements IUpgradeableObj 1, this::eap$compatOnUpgradesChanged ); - Logger.EAP$LOGGER.info("创建了完整的升级槽"); } else if (channelCard) { // 如果装了appflux,我们不创建自己的升级槽,而是监听appflux的升级槽 - Logger.EAP$LOGGER.info("装了appflux,将监听其升级槽来处理频道卡"); } else { - Logger.EAP$LOGGER.info("跳过升级槽创建"); } } catch (Exception e) { Logger.EAP$LOGGER.error("兼容性升级初始化失败", e); @@ -188,7 +180,6 @@ public abstract class PatternProviderLogicCompatMixin implements IUpgradeableObj } else { // 装了appflux时,这个方法不应该被调用,因为appflux的Mixin会覆盖它 // 但是为了安全起见,返回空的升级槽 - Logger.EAP$LOGGER.debug("装了appflux时getUpgrades被调用,这不应该发生"); return UpgradeInventories.empty(); } } @@ -250,7 +241,6 @@ public abstract class PatternProviderLogicCompatMixin implements IUpgradeableObj if (this instanceof IUpgradeableObject) { IUpgradeableObject upgradeableThis = (IUpgradeableObject) this; upgrades = upgradeableThis.getUpgrades(); - Logger.EAP$LOGGER.debug("从appflux获取到升级槽: {}", upgrades != null); } } catch (Exception e) { Logger.EAP$LOGGER.error("获取appflux升级槽失败", e); @@ -262,7 +252,6 @@ public abstract class PatternProviderLogicCompatMixin implements IUpgradeableObj if (!stack.isEmpty() && stack.getItem() == ModItems.CHANNEL_CARD.get()) { channel = ChannelCardItem.getChannel(stack); found = true; - Logger.EAP$LOGGER.info("找到频道卡,频道: {}", channel); break; } } diff --git a/src/main/java/com/extendedae_plus/mixin/appflux/AppfluxPatternProviderLogicMixin.java b/src/main/java/com/extendedae_plus/mixin/appflux/AppfluxPatternProviderLogicMixin.java index 4a22199..af7cf1b 100644 --- a/src/main/java/com/extendedae_plus/mixin/appflux/AppfluxPatternProviderLogicMixin.java +++ b/src/main/java/com/extendedae_plus/mixin/appflux/AppfluxPatternProviderLogicMixin.java @@ -28,12 +28,8 @@ public class AppfluxPatternProviderLogicMixin { at = @At("TAIL")) private void eap$modifyAppfluxUpgradeSlots(IManagedGridNode mainNode, PatternProviderLogicHost host, int patternInventorySize, CallbackInfo ci) { try { - Logger.EAP$LOGGER.info("AppfluxPatternProviderLogicMixin被调用!"); - // 只有当appflux存在且不启用我们的升级槽时才修改数量 if (!UpgradeSlotCompat.shouldEnableUpgradeSlots() && UpgradeSlotCompat.shouldEnableChannelCard()) { - Logger.EAP$LOGGER.info("尝试修改appflux升级槽数量为2个"); - // 使用反射找到appflux的升级槽字段并替换 try { Field upgradesField = this.getClass().getDeclaredField("af_$upgrades"); @@ -41,8 +37,6 @@ public class AppfluxPatternProviderLogicMixin { IUpgradeInventory currentUpgrades = (IUpgradeInventory) upgradesField.get(this); if (currentUpgrades != null) { - Logger.EAP$LOGGER.info("找到appflux升级槽,当前大小: {}", currentUpgrades.size()); - // 创建新的2槽升级槽 IUpgradeInventory newUpgrades = UpgradeInventories.forMachine( host.getTerminalIcon().getItem(), @@ -66,7 +60,6 @@ public class AppfluxPatternProviderLogicMixin { // 替换升级槽 upgradesField.set(this, newUpgrades); - Logger.EAP$LOGGER.info("成功将appflux升级槽替换为2个槽"); } } catch (Exception e) { Logger.EAP$LOGGER.error("反射修改appflux升级槽失败", e); diff --git a/src/main/java/com/extendedae_plus/util/entitySpeed/ConfigParsingUtils.java b/src/main/java/com/extendedae_plus/util/entitySpeed/ConfigParsingUtils.java index 11434a4..f34e020 100644 --- a/src/main/java/com/extendedae_plus/util/entitySpeed/ConfigParsingUtils.java +++ b/src/main/java/com/extendedae_plus/util/entitySpeed/ConfigParsingUtils.java @@ -6,7 +6,6 @@ import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.Map; -import java.util.concurrent.atomic.AtomicLong; import java.util.function.Supplier; import java.util.regex.Pattern; import java.util.regex.PatternSyntaxException; @@ -33,8 +32,6 @@ public final class ConfigParsingUtils { private static volatile Map blacklistResultCache = new HashMap<>(); private static volatile Map multiplierResultCache = new HashMap<>(); private static final Object CACHE_LOCK = new Object(); - private static final AtomicLong callCounter = new AtomicLong(0); - private static final long CHECK_INTERVAL = 100; /* 初始化缓存,在模组加载时调用 @@ -121,7 +118,6 @@ public final class ConfigParsingUtils { if (blockId == null || BLACKLIST_SUPPLIER.get() == null || BLACKLIST_SUPPLIER.get().length == 0) { return false; } - checkConfigChange(); return blacklistResultCache.computeIfAbsent(blockId, id -> getCachedBlacklist().values().stream().anyMatch(p -> p.matcher(id).matches()) ); @@ -134,7 +130,6 @@ public final class ConfigParsingUtils { if (blockId == null || MULTIPLIERS_SUPPLIER.get() == null || MULTIPLIERS_SUPPLIER.get().length == 0) { return 1.0; } - checkConfigChange(); return multiplierResultCache.computeIfAbsent(blockId, id -> { double maxMultiplier = 1.0; for (MultiplierEntry me : getCachedMultiplierEntries().values()) { @@ -213,22 +208,6 @@ public final class ConfigParsingUtils { } } - /** - * 每 100 次调用检查配置是否变化。 - */ - private static void checkConfigChange() { - if (callCounter.incrementAndGet() % CHECK_INTERVAL == 0) { - String[] currentBlacklist = BLACKLIST_SUPPLIER.get(); - String[] currentMultipliers = MULTIPLIERS_SUPPLIER.get(); - synchronized (CACHE_LOCK) { - if (cachedBlacklistSourceSnapshot == null || !Arrays.equals(cachedBlacklistSourceSnapshot, currentBlacklist) || - cachedMultiplierSourceSnapshot == null || !Arrays.equals(cachedMultiplierSourceSnapshot, currentMultipliers)) { - reload(); - } - } - } - } - /** * 清空缓存,下一次获取时将重新解析。 */ @@ -240,7 +219,6 @@ public final class ConfigParsingUtils { cachedMultiplierSourceSnapshot = null; blacklistResultCache.clear(); multiplierResultCache.clear(); - callCounter.set(0); } } } \ 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 index ecf44ee..5115be4 100644 --- a/src/main/java/com/extendedae_plus/util/entitySpeed/PowerUtils.java +++ b/src/main/java/com/extendedae_plus/util/entitySpeed/PowerUtils.java @@ -9,7 +9,6 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.concurrent.atomic.AtomicInteger; /** * 用于计算实体加速器的能耗与加速倍率的工具类 @@ -17,12 +16,8 @@ import java.util.concurrent.atomic.AtomicInteger; public final class PowerUtils { private static final int[] VALID_MULTIPLIERS = {2, 4, 8, 16, 32, 64, 128, 256, 512, 1024}; private static final int[] VALID_ENERGY_CARD_COUNTS = {0, 1, 2, 3, 4, 5, 6, 7, 8}; - private static final AtomicInteger checkCounter = new AtomicInteger(0); // 原子计数器 - private static final int CHECK_COUNTER_THRESHOLD = 100; // 配置检查阈值 - private static final int CHECK_COUNTER_RESET_THRESHOLD = 10000; // 计数器重置阈值 private static volatile Map> powerCache = new HashMap<>(); private static volatile Map ratioCache = new HashMap<>(); - private static volatile int lastEntityTickerCost = -1; // 静态初始化块,预计算缓存 static { @@ -34,7 +29,7 @@ public final class PowerUtils { /** * 初始化所有可能的缓存条目 */ - private static void initializeCaches() { + public static void initializeCaches() { synchronized (PowerUtils.class) { powerCache.clear(); ratioCache.clear(); @@ -54,7 +49,6 @@ public final class PowerUtils { } powerCache.put(product, energyCardMap); } - lastEntityTickerCost = ModConfig.INSTANCE.entityTickerCost; } } @@ -131,17 +125,6 @@ public final class PowerUtils { */ public static double getCachedPower(int product, int energyCardCount) { if (product <= 1) return 0.0; - // 每 100 次调用检查一次配置 - if (checkCounter.getAndIncrement() % CHECK_COUNTER_THRESHOLD == 0) { - checkCounter.set(checkCounter.get() % CHECK_COUNTER_RESET_THRESHOLD); - if (lastEntityTickerCost != ModConfig.INSTANCE.entityTickerCost) { - synchronized (PowerUtils.class) { - if (lastEntityTickerCost != ModConfig.INSTANCE.entityTickerCost) { - initializeCaches(); - } - } - } - } Map energyCardMap = powerCache.get(product); if (energyCardMap == null) return 0.0; Double cachedPower = energyCardMap.get(energyCardCount); From e7add431ce76882ebb3f2955045a65941a293062 Mon Sep 17 00:00:00 2001 From: C-H716 <1536152356@qq.com> Date: Tue, 30 Sep 2025 09:27:19 +0800 Subject: [PATCH 21/51] =?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=99=A8=E7=9B=B8=E5=85=B3=E9=85=8D?= =?UTF-8?q?=E7=BD=AE=E7=BC=93=E5=AD=98=E5=88=B7=E6=96=B0=E6=9C=BA=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ae/parts/EntitySpeedTickerPart.java | 85 ++++++++++++------- .../com/extendedae_plus/config/ModConfig.java | 66 ++++++++++++-- .../PatternProviderLogicCompatMixin.java | 11 --- .../AppfluxPatternProviderLogicMixin.java | 7 -- .../util/entitySpeed/ConfigParsingUtils.java | 22 ----- .../util/entitySpeed/PowerUtils.java | 19 +---- 6 files changed, 116 insertions(+), 94 deletions(-) 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 5a16d5b..a7454d9 100644 --- a/src/main/java/com/extendedae_plus/ae/parts/EntitySpeedTickerPart.java +++ b/src/main/java/com/extendedae_plus/ae/parts/EntitySpeedTickerPart.java @@ -51,7 +51,7 @@ import java.lang.reflect.Method; * 实体加速器部件,用于加速目标方块实体的 tick 速率,消耗 AE 网络能量,支持加速卡和能量卡升级。 * 灵感来源于 Crazy AE2 Addons。 */ -public class EntitySpeedTickerPart extends UpgradeablePart implements IGridTickable, MenuProvider, IUpgradeableObject { +public class EntitySpeedTickerPart extends UpgradeablePart implements IGridTickable, MenuProvider, IUpgradeableObject { public static final ResourceLocation MODEL_BASE = new ResourceLocation( ExtendedAEPlus.MODID, "part/entity_speed_ticker_part"); @@ -62,18 +62,14 @@ public class EntitySpeedTickerPart extends UpgradeablePart implements IGridTick @PartModels public static final PartModel MODELS_HAS_CHANNEL = new PartModel(MODEL_BASE, new ResourceLocation(ExtendedAEPlus.MODID, "part/entity_speed_ticker_has_channel")); - public EntitySpeedTickerMenu menu; // 当前打开的菜单实例 - private boolean networkEnergySufficient = true; // 网络能量是否充足 - private int cachedSpeed = -1; // 缓存的加速倍率 - private int cachedEnergyCardCount = -1; // 缓存的能量卡数量 - private BlockEntity cachedTarget = null; - private BlockPos cachedTargetPos = null; + private static volatile boolean[] cachedAttempts; private static volatile Method cachedFEExtractMethod; private static volatile boolean FE_UNAVAILABLE; - // ===== 静态块:反射初始化 FE 方法 ===== + // 静态块:初始化缓存 static { + cachedAttempts = ModConfig.INSTANCE.prioritizeDiskEnergy ? new boolean[]{false, true, true} : new boolean[]{true, false, true}; if (ModList.get().isLoaded("appflux")) { try { Class helperClass = Class.forName("com.extendedae_plus.util.FluxEnergyHelper"); @@ -90,8 +86,17 @@ public class EntitySpeedTickerPart extends UpgradeablePart implements IGridTick } } } + + public EntitySpeedTickerMenu menu; // 当前打开的菜单实例 + private boolean networkEnergySufficient = true; // 网络能量是否充足 + private int cachedSpeed = -1; // 缓存的加速倍率 + private int cachedEnergyCardCount = -1; // 缓存的能量卡数量 + private BlockEntity cachedTarget = null; + private BlockPos cachedTargetPos = null; + /** * 构造函数,初始化部件并设置网络节点属性。 + * * @param partItem 部件物品 */ public EntitySpeedTickerPart(IPartItem partItem) { @@ -108,17 +113,26 @@ public class EntitySpeedTickerPart extends UpgradeablePart implements IGridTick ); } + /** + * 更新缓存的 attempts 数组,由 ModConfig 调用。 + */ + public static void updateCachedAttempts(boolean prioritizeDiskEnergy) { + synchronized (EntitySpeedTickerPart.class) { + cachedAttempts = prioritizeDiskEnergy ? new boolean[]{false, true, true} : new boolean[]{true, false, true}; + } + } + public boolean getAccelerateEnabled() { return this.getConfigManager().getSetting(com.extendedae_plus.ae.api.config.Settings.ACCELERATE) == YesNo.YES; } /** * 设置加速开关状态并通知菜单。 + * * @param enabled 是否启用加速 */ public void setAccelerateEnabled(boolean enabled) { this.getConfigManager().putSetting(com.extendedae_plus.ae.api.config.Settings.ACCELERATE, enabled ? YesNo.YES : YesNo.NO); - // 是否启用加速 if (menu != null) { menu.setAccelerateEnabled(enabled); } @@ -130,6 +144,7 @@ public class EntitySpeedTickerPart extends UpgradeablePart implements IGridTick /** * 更新网络能量充足状态并通知菜单。 + * * @param sufficient 是否能量充足 */ private void updateNetworkEnergySufficient(boolean sufficient) { @@ -141,6 +156,7 @@ public class EntitySpeedTickerPart extends UpgradeablePart implements IGridTick /** * 获取当前状态的渲染模型。 + * * @return 当前状态的模型 */ @Override @@ -156,9 +172,10 @@ public class EntitySpeedTickerPart extends UpgradeablePart implements IGridTick /** * 处理玩家右键激活部件,打开菜单。 + * * @param player 玩家 - * @param hand 手 - * @param pos 点击位置 + * @param hand 手 + * @param pos 点击位置 * @return 总是返回 true */ @Override @@ -171,6 +188,7 @@ public class EntitySpeedTickerPart extends UpgradeablePart implements IGridTick /** * 定义部件的碰撞箱。 + * * @param bch 碰撞辅助器 */ @Override @@ -181,6 +199,7 @@ public class EntitySpeedTickerPart extends UpgradeablePart implements IGridTick /** * 获取定时请求,指定 tick 频率。 + * * @param iGridNode 网络节点 * @return TickingRequest 对象 */ @@ -205,7 +224,8 @@ public class EntitySpeedTickerPart extends UpgradeablePart implements IGridTick /** * 网络定时回调,处理目标方块实体的加速。 - * @param iGridNode 网络节点 + * + * @param iGridNode 网络节点 * @param ticksSinceLastCall 距离上次调用的 tick 数 * @return TickRateModulation.IDLE */ @@ -223,8 +243,9 @@ public class EntitySpeedTickerPart extends UpgradeablePart implements IGridTick /** * 对目标方块实体执行加速 tick 操作。 + * * @param blockEntity 目标方块实体 - * @param 方块实体类型 + * @param 方块实体类型 */ private void ticker(@NotNull T blockEntity) { if (!isValidForTicking()) { @@ -241,7 +262,6 @@ public class EntitySpeedTickerPart extends UpgradeablePart implements IGridTick return; } - // 如果缓存未初始化(第一次 tick),计算并更新缓存 if (cachedEnergyCardCount == -1 || cachedSpeed == -1) { this.cachedEnergyCardCount = getUpgrades().getInstalledUpgrades(AEItems.ENERGY_CARD); this.cachedSpeed = calculateSpeed(); @@ -251,7 +271,6 @@ public class EntitySpeedTickerPart extends UpgradeablePart implements IGridTick return; } - // 使用 PowerUtils 的缓存获取能耗,并应用方块特定的倍率 double requiredPower = PowerUtils.getCachedPower(cachedSpeed, cachedEnergyCardCount) * ConfigParsingUtils.getMultiplierForBlock(blockId); if (!extractPower(requiredPower)) { @@ -263,6 +282,7 @@ public class EntitySpeedTickerPart extends UpgradeablePart implements IGridTick /** * 检查网络节点是否有效。 + * * @return 是否可以执行 tick */ private boolean isValidForTicking() { @@ -280,8 +300,10 @@ public class EntitySpeedTickerPart extends UpgradeablePart implements IGridTick cachedTarget = getLevel().getBlockEntity(targetPos); } } + /** * 获取目标方块实体的 ticker。 + * * @param blockEntity 目标方块实体 * @return ticker 或 null */ @@ -292,6 +314,7 @@ public class EntitySpeedTickerPart extends UpgradeablePart implements IGridTick /** * 计算加速倍率。 + * * @return 生效的加速倍率 */ private int calculateSpeed() { @@ -302,6 +325,7 @@ public class EntitySpeedTickerPart extends UpgradeablePart implements IGridTick /** * 提取网络能量并更新状态,优先从 AE2 网络提取 AE 能量,不足时从磁盘提取 FE 能量。 + * * @param requiredPower 所需能量(AE 单位) * @return 是否成功提取足够能量 */ @@ -310,11 +334,8 @@ public class EntitySpeedTickerPart extends UpgradeablePart implements IGridTick MEStorage storage = getMainNode().getGrid().getStorageService().getInventory(); IActionSource source = IActionSource.ofMachine(this); - // 优先级:FE(如果优先磁盘) -> AE -> FE(如果未优先磁盘) - boolean[] attempts = ModConfig.INSTANCE.prioritizeDiskEnergy ? new boolean[]{true, false, true} : new boolean[]{false, true, false}; - - for (int i = 0; i < attempts.length; i++) { - if (!attempts[i]) continue; + for (int i = 0; i < cachedAttempts.length; i++) { + if (!cachedAttempts[i]) continue; if (i == 0 || i == 2) { // FE 提取 if (!FE_UNAVAILABLE && cachedFEExtractMethod != null) { try { @@ -329,10 +350,14 @@ public class EntitySpeedTickerPart extends UpgradeablePart implements IGridTick } } } else { // AE 提取 - double extracted = energyService.extractAEPower(requiredPower, Actionable.MODULATE, PowerMultiplier.CONFIG); - if (extracted >= requiredPower) { - updateNetworkEnergySufficient(true); - return true; + 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); + if (sufficient) { + return true; + } } } } @@ -342,14 +367,14 @@ public class EntitySpeedTickerPart extends UpgradeablePart implements IGridTick /** * 执行加速 tick 操作。 + * * @param blockEntity 目标方块实体 - * @param ticker 方块实体 ticker - * @param speed 加速倍率 + * @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++) { ticker.tick( blockEntity.getLevel(), @@ -372,9 +397,10 @@ public class EntitySpeedTickerPart extends UpgradeablePart implements IGridTick /** * 创建菜单实例。 - * @param containerId 容器ID + * + * @param containerId 容器ID * @param playerInventory 玩家背包 - * @param player 玩家 + * @param player 玩家 * @return 菜单实例 */ @Override @@ -386,6 +412,7 @@ public class EntitySpeedTickerPart extends UpgradeablePart implements IGridTick /** * 获取升级卡槽数量。 + * * @return 升级卡槽数量 */ @Override diff --git a/src/main/java/com/extendedae_plus/config/ModConfig.java b/src/main/java/com/extendedae_plus/config/ModConfig.java index 173ae83..5d33380 100644 --- a/src/main/java/com/extendedae_plus/config/ModConfig.java +++ b/src/main/java/com/extendedae_plus/config/ModConfig.java @@ -1,18 +1,30 @@ package com.extendedae_plus.config; import com.extendedae_plus.ExtendedAEPlus; +import com.extendedae_plus.ae.parts.EntitySpeedTickerPart; +import com.extendedae_plus.util.entitySpeed.ConfigParsingUtils; +import com.extendedae_plus.util.entitySpeed.PowerUtils; import dev.toma.configuration.Configuration; +import dev.toma.configuration.client.IValidationHandler; import dev.toma.configuration.config.Config; import dev.toma.configuration.config.Configurable; import dev.toma.configuration.config.format.ConfigFormats; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicLong; + +import static com.extendedae_plus.util.Logger.EAP$LOGGER; @Config(id = ExtendedAEPlus.MODID) public final class ModConfig { public static ModConfig INSTANCE; private static final Object lock = new Object(); - + private static final long DEBOUNCE_INTERVAL = 1000; // 防抖间隔,单位:毫秒 + private static final AtomicLong lastUpdateTime = new AtomicLong(0); public static void init() { synchronized (lock) { @@ -21,6 +33,7 @@ public final class ModConfig { } } } + @Configurable @Configurable.Comment(value = { "扩展样板供应器总槽位容量的倍率。", @@ -95,6 +108,7 @@ public final class ModConfig { }) @Configurable.Range(min = 0, max = Integer.MAX_VALUE) @Configurable.Synchronized + @Configurable.ValueUpdateCallback(method = "onEntityTickerCostUpdate") public int entityTickerCost = 512; @Configurable @@ -103,9 +117,8 @@ public final class ModConfig { "格式:全名或通配符/正则字符串,例如 'minecraft:chest'、'minecraft:*'、'modid:.*_fluid'" }) @Configurable.Synchronized - public String[] entityTickerBlackList = { - - }; + @Configurable.ValueUpdateCallback(method = "onEntityTickerBlackListUpdate") + public String[] entityTickerBlackList = {}; @Configurable @Configurable.Comment(value = { @@ -113,9 +126,8 @@ public final class ModConfig { "支持通配符/正则匹配(例如 'minecraft:* 2x' 会对整个命名空间生效)。" }) @Configurable.Synchronized - public String[] entityTickerMultipliers = { - - }; + @Configurable.ValueUpdateCallback(method = "onEntityTickerMultipliersUpdate") + public String[] entityTickerMultipliers = {}; @Configurable @Configurable.Comment(value = { @@ -131,5 +143,45 @@ public final class ModConfig { "开启后,将优先尝试从磁盘提取FE能量;反之优先消耗AE网络中的能量" }) @Configurable.Synchronized + @Configurable.ValueUpdateCallback(method = "onPrioritizeDiskEnergyUpdate") public boolean prioritizeDiskEnergy = true; + + private static final ScheduledExecutorService EXECUTOR = Executors.newSingleThreadScheduledExecutor(); + private static ScheduledFuture pendingPowerTask; + private static final Object POWER_LOCK = new Object(); + + private void onEntityTickerCostUpdate(int newValue, IValidationHandler handler) { + synchronized (POWER_LOCK) { + if (pendingPowerTask != null) { + pendingPowerTask.cancel(false); + } + pendingPowerTask = EXECUTOR.schedule(() -> { + synchronized (PowerUtils.class) { + PowerUtils.initializeCaches(); + } + }, 1000, TimeUnit.MILLISECONDS); // 1000ms 防抖 + } + } + + + private void onEntityTickerBlackListUpdate(String[] newValue, IValidationHandler handler) { + synchronized (ConfigParsingUtils.class) { + EAP$LOGGER.info("onEntityTickerBlackListUpdate"); + ConfigParsingUtils.reload(); + } + } + + private void onEntityTickerMultipliersUpdate(String[] newValue, IValidationHandler handler) { + synchronized (ConfigParsingUtils.class) { + EAP$LOGGER.info("onEntityTickerMultipliersUpdate"); + ConfigParsingUtils.reload(); + } + } + + private void onPrioritizeDiskEnergyUpdate(boolean newValue, dev.toma.configuration.client.IValidationHandler handler) { + synchronized (EntitySpeedTickerPart.class) { + EAP$LOGGER.info("onPrioritizeDiskEnergyUpdate"); + EntitySpeedTickerPart.updateCachedAttempts(newValue); + } + } } \ No newline at end of file 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 edebb1c..25a4cfc 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 @@ -85,7 +85,6 @@ public abstract class PatternProviderLogicCompatMixin implements IUpgradeableObj private void eap$onAppfluxUpgradesChanged(CallbackInfo ci) { try { if (UpgradeSlotCompat.shouldEnableChannelCard()) { - Logger.EAP$LOGGER.info("监听到appflux升级变化,处理频道卡"); // 升级变更,重置并尝试初始化频道卡 eap$compatLastChannel = -1; eap$compatHasInitialized = false; @@ -100,13 +99,9 @@ public abstract class PatternProviderLogicCompatMixin implements IUpgradeableObj at = @At("TAIL")) private void eap$compatInitUpgrades(IManagedGridNode mainNode, PatternProviderLogicHost host, int patternInventorySize, CallbackInfo ci) { try { - Logger.EAP$LOGGER.info("兼容性PatternProviderLogic初始化被调用"); - boolean upgradeSlots = UpgradeSlotCompat.shouldEnableUpgradeSlots(); boolean channelCard = UpgradeSlotCompat.shouldEnableChannelCard(); - Logger.EAP$LOGGER.info("升级槽功能: {}, 频道卡功能: {}", upgradeSlots, channelCard); - if (upgradeSlots) { // 只有在升级槽功能启用时才创建升级槽 this.eap$compatUpgrades = UpgradeInventories.forMachine( @@ -114,12 +109,9 @@ public abstract class PatternProviderLogicCompatMixin implements IUpgradeableObj 1, this::eap$compatOnUpgradesChanged ); - Logger.EAP$LOGGER.info("创建了完整的升级槽"); } else if (channelCard) { // 如果装了appflux,我们不创建自己的升级槽,而是监听appflux的升级槽 - Logger.EAP$LOGGER.info("装了appflux,将监听其升级槽来处理频道卡"); } else { - Logger.EAP$LOGGER.info("跳过升级槽创建"); } } catch (Exception e) { Logger.EAP$LOGGER.error("兼容性升级初始化失败", e); @@ -188,7 +180,6 @@ public abstract class PatternProviderLogicCompatMixin implements IUpgradeableObj } else { // 装了appflux时,这个方法不应该被调用,因为appflux的Mixin会覆盖它 // 但是为了安全起见,返回空的升级槽 - Logger.EAP$LOGGER.debug("装了appflux时getUpgrades被调用,这不应该发生"); return UpgradeInventories.empty(); } } @@ -250,7 +241,6 @@ public abstract class PatternProviderLogicCompatMixin implements IUpgradeableObj if (this instanceof IUpgradeableObject) { IUpgradeableObject upgradeableThis = (IUpgradeableObject) this; upgrades = upgradeableThis.getUpgrades(); - Logger.EAP$LOGGER.debug("从appflux获取到升级槽: {}", upgrades != null); } } catch (Exception e) { Logger.EAP$LOGGER.error("获取appflux升级槽失败", e); @@ -262,7 +252,6 @@ public abstract class PatternProviderLogicCompatMixin implements IUpgradeableObj if (!stack.isEmpty() && stack.getItem() == ModItems.CHANNEL_CARD.get()) { channel = ChannelCardItem.getChannel(stack); found = true; - Logger.EAP$LOGGER.info("找到频道卡,频道: {}", channel); break; } } diff --git a/src/main/java/com/extendedae_plus/mixin/appflux/AppfluxPatternProviderLogicMixin.java b/src/main/java/com/extendedae_plus/mixin/appflux/AppfluxPatternProviderLogicMixin.java index 4a22199..af7cf1b 100644 --- a/src/main/java/com/extendedae_plus/mixin/appflux/AppfluxPatternProviderLogicMixin.java +++ b/src/main/java/com/extendedae_plus/mixin/appflux/AppfluxPatternProviderLogicMixin.java @@ -28,12 +28,8 @@ public class AppfluxPatternProviderLogicMixin { at = @At("TAIL")) private void eap$modifyAppfluxUpgradeSlots(IManagedGridNode mainNode, PatternProviderLogicHost host, int patternInventorySize, CallbackInfo ci) { try { - Logger.EAP$LOGGER.info("AppfluxPatternProviderLogicMixin被调用!"); - // 只有当appflux存在且不启用我们的升级槽时才修改数量 if (!UpgradeSlotCompat.shouldEnableUpgradeSlots() && UpgradeSlotCompat.shouldEnableChannelCard()) { - Logger.EAP$LOGGER.info("尝试修改appflux升级槽数量为2个"); - // 使用反射找到appflux的升级槽字段并替换 try { Field upgradesField = this.getClass().getDeclaredField("af_$upgrades"); @@ -41,8 +37,6 @@ public class AppfluxPatternProviderLogicMixin { IUpgradeInventory currentUpgrades = (IUpgradeInventory) upgradesField.get(this); if (currentUpgrades != null) { - Logger.EAP$LOGGER.info("找到appflux升级槽,当前大小: {}", currentUpgrades.size()); - // 创建新的2槽升级槽 IUpgradeInventory newUpgrades = UpgradeInventories.forMachine( host.getTerminalIcon().getItem(), @@ -66,7 +60,6 @@ public class AppfluxPatternProviderLogicMixin { // 替换升级槽 upgradesField.set(this, newUpgrades); - Logger.EAP$LOGGER.info("成功将appflux升级槽替换为2个槽"); } } catch (Exception e) { Logger.EAP$LOGGER.error("反射修改appflux升级槽失败", e); diff --git a/src/main/java/com/extendedae_plus/util/entitySpeed/ConfigParsingUtils.java b/src/main/java/com/extendedae_plus/util/entitySpeed/ConfigParsingUtils.java index 11434a4..f34e020 100644 --- a/src/main/java/com/extendedae_plus/util/entitySpeed/ConfigParsingUtils.java +++ b/src/main/java/com/extendedae_plus/util/entitySpeed/ConfigParsingUtils.java @@ -6,7 +6,6 @@ import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.Map; -import java.util.concurrent.atomic.AtomicLong; import java.util.function.Supplier; import java.util.regex.Pattern; import java.util.regex.PatternSyntaxException; @@ -33,8 +32,6 @@ public final class ConfigParsingUtils { private static volatile Map blacklistResultCache = new HashMap<>(); private static volatile Map multiplierResultCache = new HashMap<>(); private static final Object CACHE_LOCK = new Object(); - private static final AtomicLong callCounter = new AtomicLong(0); - private static final long CHECK_INTERVAL = 100; /* 初始化缓存,在模组加载时调用 @@ -121,7 +118,6 @@ public final class ConfigParsingUtils { if (blockId == null || BLACKLIST_SUPPLIER.get() == null || BLACKLIST_SUPPLIER.get().length == 0) { return false; } - checkConfigChange(); return blacklistResultCache.computeIfAbsent(blockId, id -> getCachedBlacklist().values().stream().anyMatch(p -> p.matcher(id).matches()) ); @@ -134,7 +130,6 @@ public final class ConfigParsingUtils { if (blockId == null || MULTIPLIERS_SUPPLIER.get() == null || MULTIPLIERS_SUPPLIER.get().length == 0) { return 1.0; } - checkConfigChange(); return multiplierResultCache.computeIfAbsent(blockId, id -> { double maxMultiplier = 1.0; for (MultiplierEntry me : getCachedMultiplierEntries().values()) { @@ -213,22 +208,6 @@ public final class ConfigParsingUtils { } } - /** - * 每 100 次调用检查配置是否变化。 - */ - private static void checkConfigChange() { - if (callCounter.incrementAndGet() % CHECK_INTERVAL == 0) { - String[] currentBlacklist = BLACKLIST_SUPPLIER.get(); - String[] currentMultipliers = MULTIPLIERS_SUPPLIER.get(); - synchronized (CACHE_LOCK) { - if (cachedBlacklistSourceSnapshot == null || !Arrays.equals(cachedBlacklistSourceSnapshot, currentBlacklist) || - cachedMultiplierSourceSnapshot == null || !Arrays.equals(cachedMultiplierSourceSnapshot, currentMultipliers)) { - reload(); - } - } - } - } - /** * 清空缓存,下一次获取时将重新解析。 */ @@ -240,7 +219,6 @@ public final class ConfigParsingUtils { cachedMultiplierSourceSnapshot = null; blacklistResultCache.clear(); multiplierResultCache.clear(); - callCounter.set(0); } } } \ 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 index ecf44ee..5115be4 100644 --- a/src/main/java/com/extendedae_plus/util/entitySpeed/PowerUtils.java +++ b/src/main/java/com/extendedae_plus/util/entitySpeed/PowerUtils.java @@ -9,7 +9,6 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.concurrent.atomic.AtomicInteger; /** * 用于计算实体加速器的能耗与加速倍率的工具类 @@ -17,12 +16,8 @@ import java.util.concurrent.atomic.AtomicInteger; public final class PowerUtils { private static final int[] VALID_MULTIPLIERS = {2, 4, 8, 16, 32, 64, 128, 256, 512, 1024}; private static final int[] VALID_ENERGY_CARD_COUNTS = {0, 1, 2, 3, 4, 5, 6, 7, 8}; - private static final AtomicInteger checkCounter = new AtomicInteger(0); // 原子计数器 - private static final int CHECK_COUNTER_THRESHOLD = 100; // 配置检查阈值 - private static final int CHECK_COUNTER_RESET_THRESHOLD = 10000; // 计数器重置阈值 private static volatile Map> powerCache = new HashMap<>(); private static volatile Map ratioCache = new HashMap<>(); - private static volatile int lastEntityTickerCost = -1; // 静态初始化块,预计算缓存 static { @@ -34,7 +29,7 @@ public final class PowerUtils { /** * 初始化所有可能的缓存条目 */ - private static void initializeCaches() { + public static void initializeCaches() { synchronized (PowerUtils.class) { powerCache.clear(); ratioCache.clear(); @@ -54,7 +49,6 @@ public final class PowerUtils { } powerCache.put(product, energyCardMap); } - lastEntityTickerCost = ModConfig.INSTANCE.entityTickerCost; } } @@ -131,17 +125,6 @@ public final class PowerUtils { */ public static double getCachedPower(int product, int energyCardCount) { if (product <= 1) return 0.0; - // 每 100 次调用检查一次配置 - if (checkCounter.getAndIncrement() % CHECK_COUNTER_THRESHOLD == 0) { - checkCounter.set(checkCounter.get() % CHECK_COUNTER_RESET_THRESHOLD); - if (lastEntityTickerCost != ModConfig.INSTANCE.entityTickerCost) { - synchronized (PowerUtils.class) { - if (lastEntityTickerCost != ModConfig.INSTANCE.entityTickerCost) { - initializeCaches(); - } - } - } - } Map energyCardMap = powerCache.get(product); if (energyCardMap == null) return 0.0; Double cachedPower = energyCardMap.get(energyCardCount); From 8ba6aa4fcdf51e097c563f6a5090d52848f06370 Mon Sep 17 00:00:00 2001 From: C-H716 <1536152356@qq.com> Date: Tue, 30 Sep 2025 10:01:48 +0800 Subject: [PATCH 22/51] =?UTF-8?q?feat:=20=E4=BF=AE=E6=AD=A3=E9=80=BB?= =?UTF-8?q?=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ae/parts/EntitySpeedTickerPart.java | 47 ++++++++++++------- 1 file changed, 29 insertions(+), 18 deletions(-) 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 a7454d9..9935044 100644 --- a/src/main/java/com/extendedae_plus/ae/parts/EntitySpeedTickerPart.java +++ b/src/main/java/com/extendedae_plus/ae/parts/EntitySpeedTickerPart.java @@ -69,7 +69,10 @@ public class EntitySpeedTickerPart extends UpgradeablePart implements IGridTicka // 静态块:初始化缓存 static { - cachedAttempts = ModConfig.INSTANCE.prioritizeDiskEnergy ? new boolean[]{false, true, true} : new boolean[]{true, false, true}; + // 优先磁盘 -> FE 然后 AE;否则 AE 然后 FE + cachedAttempts = ModConfig.INSTANCE.prioritizeDiskEnergy ? + new boolean[]{ true, true, false } : + new boolean[]{ false, true, true }; if (ModList.get().isLoaded("appflux")) { try { Class helperClass = Class.forName("com.extendedae_plus.util.FluxEnergyHelper"); @@ -118,10 +121,14 @@ public class EntitySpeedTickerPart extends UpgradeablePart implements IGridTicka */ public static void updateCachedAttempts(boolean prioritizeDiskEnergy) { synchronized (EntitySpeedTickerPart.class) { - cachedAttempts = prioritizeDiskEnergy ? new boolean[]{false, true, true} : new boolean[]{true, false, true}; + // 优先磁盘 -> FE 然后 AE;否则 AE 然后 FE + cachedAttempts = prioritizeDiskEnergy ? + new boolean[]{ true, true, false } : + new boolean[]{ false, true, true }; } } + public boolean getAccelerateEnabled() { return this.getConfigManager().getSetting(com.extendedae_plus.ae.api.config.Settings.ACCELERATE) == YesNo.YES; } @@ -336,35 +343,39 @@ public class EntitySpeedTickerPart extends UpgradeablePart implements IGridTicka for (int i = 0; i < cachedAttempts.length; i++) { if (!cachedAttempts[i]) continue; - if (i == 0 || i == 2) { // FE 提取 - if (!FE_UNAVAILABLE && cachedFEExtractMethod != null) { - try { - long feRequired = (long) requiredPower << 1; - long feExtracted = (long) cachedFEExtractMethod.invoke(null, energyService, storage, feRequired, source); - if (feExtracted >= feRequired) { - updateNetworkEnergySufficient(true); - return true; - } - } catch (Exception e) { - FE_UNAVAILABLE = true; + + // FE 提取 + if ((i == 0 || i == 2) && !FE_UNAVAILABLE && cachedFEExtractMethod != null) { + try { + long feRequired = (long) requiredPower << 1; + long feExtracted = (long) cachedFEExtractMethod.invoke(null, energyService, storage, feRequired, source); + if (feExtracted >= feRequired) { + updateNetworkEnergySufficient(true); + return true; } + } catch (Exception e) { + FE_UNAVAILABLE = true; + continue; } - } else { // AE 提取 + } + // AE 提取 + else { double simulated = energyService.extractAEPower(requiredPower, Actionable.SIMULATE, PowerMultiplier.CONFIG); - if (simulated >= requiredPower) { + if (simulated >= requiredPower) { // 模拟足够 double extracted = energyService.extractAEPower(requiredPower, Actionable.MODULATE, PowerMultiplier.CONFIG); boolean sufficient = extracted >= requiredPower; updateNetworkEnergySufficient(sufficient); - if (sufficient) { - return true; - } + if (sufficient) return true; } } } + + // 所有尝试都不够 updateNetworkEnergySufficient(false); return false; } + /** * 执行加速 tick 操作。 * From 32cc796498cf441dbbe5801cfa32a414d35cd46d Mon Sep 17 00:00:00 2001 From: GaLicn <133291877+GaLicn@users.noreply.github.com> Date: Tue, 30 Sep 2025 12:29:49 +0800 Subject: [PATCH 23/51] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E4=B8=8A=E4=BC=A0?= =?UTF-8?q?=E6=97=B6=E6=9C=8D=E5=8A=A1=E7=AB=AF=E5=88=B0=E5=AE=A2=E6=88=B7?= =?UTF-8?q?=E7=AB=AF=E7=BF=BB=E8=AF=91=E9=94=AE=E5=9B=BA=E5=AE=9A=E8=8B=B1?= =?UTF-8?q?=E6=96=87=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../client/ui/ProviderSelectScreen.java | 25 ++++++++++++++++++- .../util/ExtendedAEPatternUploadUtil.java | 10 ++++++-- 2 files changed, 32 insertions(+), 3 deletions(-) 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 1984717..6b8b229 100644 --- a/src/main/java/com/extendedae_plus/client/ui/ProviderSelectScreen.java +++ b/src/main/java/com/extendedae_plus/client/ui/ProviderSelectScreen.java @@ -183,10 +183,29 @@ public class ProviderSelectScreen extends Screen { } } + /** + * 将服务器发送的名称(可能是 Component JSON)反序列化为本地化文本 + */ + private String deserializeComponentName(String name) { + try { + // 如果名称是 JSON 格式的 Component,反序列化后获取本地化文本 + if (name.startsWith("{") || name.startsWith("\"")) { + Component component = Component.Serializer.fromJson(name); + if (component != null) { + return component.getString(); + } + } + } catch (Exception e) { + // 如果不是 JSON 或解析失败,使用原始字符串 + } + return name; + } + private String buildLabel(int idx) { String name = fNames.get(idx); int totalSlots = fTotalSlots.get(idx); int count = fCount.get(idx); + // 不显示具体 id,显示合并统计:名称(总空位)x数量 return name + " (" + totalSlots + ") x" + count; } @@ -215,7 +234,11 @@ public class ProviderSelectScreen extends Screen { String name = names.get(i); long id = ids.get(i); int slots = emptySlots.get(i); - Group g = map.computeIfAbsent(name, k -> new Group()); + + // 将 Component JSON 转换为本地化文本用于分组键 + String groupKey = deserializeComponentName(name); + + Group g = map.computeIfAbsent(groupKey, k -> new Group()); g.count++; g.totalSlots += Math.max(0, slots); // 挑选空位最多的作为代表 id;若并列,保留先到者 diff --git a/src/main/java/com/extendedae_plus/util/ExtendedAEPatternUploadUtil.java b/src/main/java/com/extendedae_plus/util/ExtendedAEPatternUploadUtil.java index d9f2542..6715411 100644 --- a/src/main/java/com/extendedae_plus/util/ExtendedAEPatternUploadUtil.java +++ b/src/main/java/com/extendedae_plus/util/ExtendedAEPatternUploadUtil.java @@ -941,7 +941,9 @@ public class ExtendedAEPatternUploadUtil { // 尝试获取供应器的组信息来构建显示名称 var group = container.getTerminalGroup(); if (group != null) { - return group.name().getString(); + // 使用 Component 序列化来保持翻译键,而不是直接 getString() + // 这样客户端可以根据自己的语言设置进行翻译 + return Component.Serializer.toJson(group.name()); } } catch (Exception e) { // 忽略异常,使用默认名称 @@ -1183,7 +1185,11 @@ public class ExtendedAEPatternUploadUtil { if (container == null) return "未知供应器"; try { var group = container.getTerminalGroup(); - if (group != null) return group.name().getString(); + if (group != null) { + // 使用 Component 序列化来保持翻译键,而不是直接 getString() + // 这样客户端可以根据自己的语言设置进行翻译 + return Component.Serializer.toJson(group.name()); + } } catch (Throwable ignored) { } return "样板供应器"; From cb280b7236caa5455037645d5b25150f81aff55e Mon Sep 17 00:00:00 2001 From: C-H716 <1536152356@qq.com> Date: Wed, 1 Oct 2025 20:28:11 +0800 Subject: [PATCH 24/51] =?UTF-8?q?feat:=20=E4=BF=AE=E6=AD=A3=E9=80=BB?= =?UTF-8?q?=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ae/parts/EntitySpeedTickerPart.java | 70 ++++++++++++------- 1 file changed, 45 insertions(+), 25 deletions(-) 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 d541e22..dc5dad4 100644 --- a/src/main/java/com/extendedae_plus/ae/parts/EntitySpeedTickerPart.java +++ b/src/main/java/com/extendedae_plus/ae/parts/EntitySpeedTickerPart.java @@ -329,42 +329,62 @@ public class EntitySpeedTickerPart extends UpgradeablePart implements IGridTicka * @param requiredPower 所需能量(AE 单位) * @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); - for (int i = 0; i < cachedAttempts.length; i++) { - if (!cachedAttempts[i]) continue; - if (i == 0 || i == 2) { // FE 提取 - if (!FE_UNAVAILABLE && cachedFEExtractMethod != null) { - try { - long feRequired = (long) requiredPower << 1; - long feExtracted = (long) cachedFEExtractMethod.invoke(null, energyService, storage, feRequired, source); - if (feExtracted >= feRequired) { - updateNetworkEnergySufficient(true); - return true; - } - } catch (Exception e) { - FE_UNAVAILABLE = true; - } - } - } else { // AE 提取 - 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); - if (sufficient) { - return true; - } - } + boolean preferDiskEnergy = ModConfig.INSTANCE.prioritizeDiskEnergy; + + // 如果优先磁盘能量,先尝试 FE + if (preferDiskEnergy && tryExtractFE(energyService, storage, requiredPower, source)) { + return true; + } + + // 先尝试 AE 能量 + 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); + + // 如果没成功,且不是优先磁盘能量,再尝试 FE + if (!preferDiskEnergy) { + return tryExtractFE(energyService, storage, requiredPower, source); + } + + return false; + } + + private boolean tryExtractFE(IEnergyService energyService, MEStorage storage, double requiredPower, IActionSource source) { + if (FE_UNAVAILABLE || cachedFEExtractMethod == null) { + updateNetworkEnergySufficient(false); + return false; + } + try { + long feRequired = (long) requiredPower << 1; // 1 AE = 2 FE + long feExtracted = (long) cachedFEExtractMethod.invoke(null, energyService, storage, feRequired, source); + if (feExtracted >= feRequired) { + updateNetworkEnergySufficient(true); + return true; } + } catch (Exception e) { + // 如果反射调用失败,标记为不可用,避免下次继续尝试 + FE_UNAVAILABLE = true; } updateNetworkEnergySufficient(false); return false; } + /** * 执行加速 tick 操作。 * From 7c0bf38f060f1b8278ea01eb7b54ed4faf55b8b3 Mon Sep 17 00:00:00 2001 From: GaLicn <133291877+GaLicn@users.noreply.github.com> Date: Fri, 3 Oct 2025 13:40:09 +0800 Subject: [PATCH 25/51] =?UTF-8?q?jade=E6=B7=BB=E5=8A=A0=E6=97=A0=E7=BA=BF?= =?UTF-8?q?=E6=94=B6=E5=8F=91=E5=99=A8=E9=A2=91=E9=81=93=E4=BD=BF=E7=94=A8?= =?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 --- gradle.properties | 2 +- ...relessTransceiverJadePluginComponents.java | 17 ++++++++++++++ .../jade/WirelessTransceiverProvider.java | 22 +++++++++++++++++++ .../assets/extendedae_plus/lang/en_us.json | 3 +++ .../assets/extendedae_plus/lang/zh_cn.json | 3 +++ 5 files changed, 46 insertions(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index ba9b599..688e318 100644 --- a/gradle.properties +++ b/gradle.properties @@ -3,7 +3,7 @@ org.gradle.jvmargs=-Xmx1G loom.platform = forge # Mod properties -mod_version = 1.4.3 +mod_version = 1.4.4-BETA maven_group = com.extendedae_plus archives_name = extendedae_plus 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 2505db5..a9c7543 100644 --- a/src/main/java/com/extendedae_plus/integration/jade/WirelessTransceiverJadePluginComponents.java +++ b/src/main/java/com/extendedae_plus/integration/jade/WirelessTransceiverJadePluginComponents.java @@ -66,6 +66,23 @@ public enum WirelessTransceiverJadePluginComponents implements IBlockComponentPr tooltip.add(Component.literal((usable ? "设备在线" : "设备离线"))); } } + }, + CHANNELS("wt_channels") { + @Override + protected void add(BlockAccessor accessor, ITooltip tooltip, IPluginConfig config, CompoundTag data) { + if (data.contains("usedChannels") && data.contains("maxChannels")) { + int usedChannels = data.getInt("usedChannels"); + int maxChannels = data.getInt("maxChannels"); + // 参考AE2的显示方式 + if (maxChannels <= 0) { + // 无限频道或未设置 + tooltip.add(Component.translatable("extendedae_plus.tooltip.channels", usedChannels)); + } else { + // 显示 "已使用/最大" + tooltip.add(Component.translatable("extendedae_plus.tooltip.channels_of", usedChannels, maxChannels)); + } + } + } }; private final ResourceLocation uid; 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 d0529fc..1578639 100644 --- a/src/main/java/com/extendedae_plus/integration/jade/WirelessTransceiverProvider.java +++ b/src/main/java/com/extendedae_plus/integration/jade/WirelessTransceiverProvider.java @@ -40,6 +40,28 @@ public enum WirelessTransceiverProvider implements IServerDataProvider Date: Fri, 3 Oct 2025 13:59:54 +0800 Subject: [PATCH 26/51] =?UTF-8?q?=E5=B9=B6=E8=A1=8C=E5=A4=84=E7=90=86?= =?UTF-8?q?=E5=8D=95=E5=85=83mixin=E8=B0=83=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mixin/ae2/CraftingCPUClusterMixin.java | 84 ++++++++++++++----- 1 file changed, 61 insertions(+), 23 deletions(-) diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/CraftingCPUClusterMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/CraftingCPUClusterMixin.java index ec1a5c8..7332895 100644 --- a/src/main/java/com/extendedae_plus/mixin/ae2/CraftingCPUClusterMixin.java +++ b/src/main/java/com/extendedae_plus/mixin/ae2/CraftingCPUClusterMixin.java @@ -1,36 +1,74 @@ package com.extendedae_plus.mixin.ae2; import appeng.blockentity.crafting.CraftingBlockEntity; +import appeng.blockentity.crafting.CraftingMonitorBlockEntity; import appeng.me.cluster.implementations.CraftingCPUCluster; +import appeng.me.helpers.MachineSource; +import org.spongepowered.asm.mixin.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.Constant; -import org.spongepowered.asm.mixin.injection.ModifyConstant; -import org.spongepowered.asm.mixin.injection.Redirect; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import java.util.List; @Mixin(value = CraftingCPUCluster.class, remap = false, priority = 2000) public abstract class CraftingCPUClusterMixin { - // 1) 提升“单方块线程上限”的常量,避免抛出 IAE 的 IllegalArgumentException - @ModifyConstant( + + @Shadow(remap = false) + private MachineSource machineSrc; + + @Shadow(remap = false) + @Final + private List blockEntities; + + @Shadow(remap = false) + @Final + private List status; + + @Shadow(remap = false) + private long storage; + + @Shadow(remap = false) + private int accelerator; + + /** + * 完全替换 addBlockEntity 方法,移除 16 线程限制 + * 在方法开始时注入,检查线程数,如果超过 16 就手动处理并取消原方法 + */ + @Inject( method = "addBlockEntity(Lappeng/blockentity/crafting/CraftingBlockEntity;)V", - constant = @Constant(intValue = 16) + at = @At("HEAD"), + cancellable = true, + require = 0 // 设置为 0,即使注入失败也不会崩溃 ) - private int extendedae_plus$raisePerUnitLimit(int original) { - // 放宽到极大值,完全取消单方块 16 线程的硬限制 - return Integer.MAX_VALUE; - } - - // 2) 保持统计使用原始线程值(若存在多处调用),不再返回固定 16 - @Redirect( - method = "addBlockEntity(Lappeng/blockentity/crafting/CraftingBlockEntity;)V", - at = @At( - value = "INVOKE", - target = "Lappeng/blockentity/crafting/CraftingBlockEntity;getAcceleratorThreads()I", - ordinal = 1 - ) - ) - private int extendedae_plus$onGetThreadsForLimitCheck(CraftingBlockEntity te) { - // 返回原始线程数,确保总并行单元不被错误下限 - return te.getAcceleratorThreads(); + private void extendedae_plus$removeThreadLimit(CraftingBlockEntity te, CallbackInfo ci) { + // 只有当线程数超过 16 时才接管处理 + if (te.getAcceleratorThreads() > 16) { + // 手动实现 addBlockEntity 的逻辑,但不检查 16 的限制 + if (this.machineSrc == null || te.isCoreBlock()) { + this.machineSrc = new MachineSource(te); + } + + te.setCoreBlock(false); + te.saveChanges(); + this.blockEntities.add(0, te); + + if (te instanceof CraftingMonitorBlockEntity) { + this.status.add((CraftingMonitorBlockEntity) te); + } + if (te.getStorageBytes() > 0) { + this.storage += te.getStorageBytes(); + } + if (te.getAcceleratorThreads() > 0) { + // 这里不检查 <= 16 的限制,直接添加 + this.accelerator += te.getAcceleratorThreads(); + } + + // 取消原方法执行 + ci.cancel(); + } + // 如果 <= 16,让原方法正常执行 } } From abce3ffe414952073c5361cdcf193c5337c8ecdf Mon Sep 17 00:00:00 2001 From: GaLicn <133291877+GaLicn@users.noreply.github.com> Date: Sun, 5 Oct 2025 00:36:17 +0800 Subject: [PATCH 27/51] =?UTF-8?q?=E4=B8=BA=E5=AE=9E=E4=BD=93=E5=8A=A0?= =?UTF-8?q?=E9=80=9F=E6=B7=BB=E5=8A=A0=E5=BC=82=E5=B8=B8=E6=8D=95=E6=8D=89?= =?UTF-8?q?=EF=BC=8C=E9=98=B2=E6=AD=A2=E5=B4=A9=E6=BA=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ae/parts/EntitySpeedTickerPart.java | 42 ++++++++++++++++--- 1 file changed, 36 insertions(+), 6 deletions(-) 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 dc5dad4..1867e6a 100644 --- a/src/main/java/com/extendedae_plus/ae/parts/EntitySpeedTickerPart.java +++ b/src/main/java/com/extendedae_plus/ae/parts/EntitySpeedTickerPart.java @@ -28,6 +28,7 @@ import com.extendedae_plus.init.ModItems; import com.extendedae_plus.init.ModMenuTypes; import com.extendedae_plus.util.entitySpeed.ConfigParsingUtils; import com.extendedae_plus.util.entitySpeed.PowerUtils; +import com.extendedae_plus.util.Logger; import net.minecraft.core.BlockPos; import net.minecraft.network.chat.Component; import net.minecraft.resources.ResourceLocation; @@ -396,12 +397,41 @@ public class EntitySpeedTickerPart extends UpgradeablePart implements IGridTicka BlockEntityTicker ticker, int speed) { for (int i = 0; i < speed - 1; i++) { - ticker.tick( - blockEntity.getLevel(), - blockEntity.getBlockPos(), - blockEntity.getBlockState(), - blockEntity - ); + try { + ticker.tick( + blockEntity.getLevel(), + blockEntity.getBlockPos(), + blockEntity.getBlockState(), + blockEntity + ); + } catch (IllegalStateException e) { + // 捕获随机数生成器的多线程访问异常 + // 这通常发生在某些模组(如 Thermal)的机器使用随机数时 + // 由于加速导致在同一tick内多次访问随机数生成器而触发 ThreadingDetector + if (e.getMessage() != null && e.getMessage().contains("LegacyRandomSource")) { + // 记录警告并停止当前加速循环,避免崩溃 + Logger.EAP$LOGGER.warn( + "检测到方块实体 {} 在位置 {} 的随机数访问冲突,已停止本次加速以避免崩溃。" + + "建议将此方块类型添加到配置黑名单中。", + blockEntity.getType().toString(), + blockEntity.getBlockPos() + ); + break; // 停止后续的加速 tick + } else { + // 如果是其他类型的 IllegalStateException,继续抛出 + throw e; + } + } catch (Exception e) { + // 捕获其他可能的异常,防止崩溃 + Logger.EAP$LOGGER.error( + "在加速方块实体 {} 位置 {} 时发生错误: {}", + blockEntity.getType().toString(), + blockEntity.getBlockPos(), + e.getMessage(), + e + ); + break; // 停止后续的加速 tick + } } } From 60c5dc7cf747812b46119d750bf0ac10aee4bc2e Mon Sep 17 00:00:00 2001 From: GaLicn <133291877+GaLicn@users.noreply.github.com> Date: Sun, 5 Oct 2025 10:23:47 +0800 Subject: [PATCH 28/51] =?UTF-8?q?ftbteams=E4=BE=9D=E8=B5=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/build.gradle b/build.gradle index 9e12294..9fec603 100644 --- a/build.gradle +++ b/build.gradle @@ -121,6 +121,10 @@ dependencies { // compileOnly 供代码编译 include "curse.maven:configuration-444699:4710266" modImplementation "curse.maven:configuration-444699:4710266" + + //ftbteams + modRuntimeOnly "curse.maven:ftb-teams-forge-404468:6130786" + modRuntimeOnly "curse.maven:ftb-library-forge-404465:6807424" } allprojects { From b31ec61b54c0f22e674d3aa411003854d196c9f8 Mon Sep 17 00:00:00 2001 From: GaLicn <133291877+GaLicn@users.noreply.github.com> Date: Sun, 5 Oct 2025 11:51:04 +0800 Subject: [PATCH 29/51] =?UTF-8?q?=E4=B8=BA=E6=97=A0=E7=BA=BF=E6=94=B6?= =?UTF-8?q?=E5=8F=91=E5=99=A8=E7=BB=91=E5=AE=9Aftbteams?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 2 + .../ae/items/ChannelCardItem.java | 142 ++++++++++- .../ae/wireless/WirelessMasterLink.java | 17 +- .../ae/wireless/WirelessMasterRegistry.java | 45 ++-- .../ae/wireless/WirelessSlaveLink.java | 14 +- .../event/ChannelCardClientHandler.java | 45 ++++ .../wireless/WirelessTransceiverBlock.java | 76 ++++-- .../WirelessTransceiverBlockEntity.java | 38 +++ .../com/extendedae_plus/init/ModNetwork.java | 6 + .../jade/WirelessTransceiverProvider.java | 3 +- .../PatternProviderLogicCompatMixin.java | 17 ++ .../InterfaceLogicChannelCardMixin.java | 4 + .../automation/IOBusPartChannelCardMixin.java | 4 + .../StorageBusPartChannelCardMixin.java | 4 + .../network/ChannelCardBindPacket.java | 77 ++++++ .../util/WirelessTeamUtil.java | 221 ++++++++++++++++++ .../assets/extendedae_plus/lang/en_us.json | 5 + .../assets/extendedae_plus/lang/zh_cn.json | 5 + 18 files changed, 687 insertions(+), 38 deletions(-) create mode 100644 src/main/java/com/extendedae_plus/client/event/ChannelCardClientHandler.java create mode 100644 src/main/java/com/extendedae_plus/network/ChannelCardBindPacket.java create mode 100644 src/main/java/com/extendedae_plus/util/WirelessTeamUtil.java diff --git a/build.gradle b/build.gradle index 9fec603..df22770 100644 --- a/build.gradle +++ b/build.gradle @@ -123,6 +123,8 @@ dependencies { modImplementation "curse.maven:configuration-444699:4710266" //ftbteams + modCompileOnly "curse.maven:ftb-teams-forge-404468:6130786" + modCompileOnly "curse.maven:ftb-library-forge-404465:6807424" modRuntimeOnly "curse.maven:ftb-teams-forge-404468:6130786" modRuntimeOnly "curse.maven:ftb-library-forge-404465:6807424" } diff --git a/src/main/java/com/extendedae_plus/ae/items/ChannelCardItem.java b/src/main/java/com/extendedae_plus/ae/items/ChannelCardItem.java index 1d68551..ef186cd 100644 --- a/src/main/java/com/extendedae_plus/ae/items/ChannelCardItem.java +++ b/src/main/java/com/extendedae_plus/ae/items/ChannelCardItem.java @@ -1,8 +1,10 @@ package com.extendedae_plus.ae.items; import appeng.items.materials.UpgradeCardItem; +import com.extendedae_plus.util.WirelessTeamUtil; import net.minecraft.nbt.CompoundTag; import net.minecraft.network.chat.Component; +import net.minecraft.server.level.ServerLevel; import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.TooltipFlag; @@ -13,13 +15,19 @@ import net.minecraft.world.level.Level; import org.jetbrains.annotations.Nullable; import java.util.List; +import java.util.UUID; /** - * 频道卡(MVP):仅存储一个 long 类型的频道号到 NBT:"channel"。 + * 频道卡:存储频道号、所有者UUID和团队信息 + * - 右键空气:增加频道号 + * - 潜行左键空气:写入/清除玩家UUID和团队信息 + * - 潜行左键收发器:将频道卡的所有者信息写入收发器 * 继承 AE2 的 UpgradeCardItem 以复用升级卡判定与提示框架。 */ public class ChannelCardItem extends UpgradeCardItem { public static final String TAG_CHANNEL = "channel"; + public static final String TAG_OWNER_UUID = "ownerUUID"; + public static final String TAG_TEAM_NAME = "teamName"; // 用于显示 public ChannelCardItem(Item.Properties properties) { super(properties); @@ -35,29 +43,157 @@ public class ChannelCardItem extends UpgradeCardItem { return tag != null && tag.contains(TAG_CHANNEL) ? tag.getLong(TAG_CHANNEL) : 0L; } + /** + * 设置频道卡的所有者UUID + */ + public static void setOwnerUUID(ItemStack stack, UUID ownerUUID) { + CompoundTag tag = stack.getOrCreateTag(); + tag.putUUID(TAG_OWNER_UUID, ownerUUID); + } + + /** + * 获取频道卡的所有者UUID + */ + @Nullable + public static UUID getOwnerUUID(ItemStack stack) { + CompoundTag tag = stack.getTag(); + return tag != null && tag.hasUUID(TAG_OWNER_UUID) ? tag.getUUID(TAG_OWNER_UUID) : null; + } + + /** + * 设置团队名称(用于显示) + */ + public static void setTeamName(ItemStack stack, String teamName) { + CompoundTag tag = stack.getOrCreateTag(); + tag.putString(TAG_TEAM_NAME, teamName); + } + + /** + * 获取团队名称 + */ + @Nullable + public static String getTeamName(ItemStack stack) { + CompoundTag tag = stack.getTag(); + return tag != null && tag.contains(TAG_TEAM_NAME) ? tag.getString(TAG_TEAM_NAME) : null; + } + + /** + * 清除所有者信息 + */ + public static void clearOwner(ItemStack stack) { + CompoundTag tag = stack.getTag(); + if (tag != null) { + tag.remove(TAG_OWNER_UUID); + tag.remove(TAG_TEAM_NAME); + } + } + @Override public void appendHoverText(ItemStack stack, @Nullable Level level, List lines, TooltipFlag flag) { super.appendHoverText(stack, level, lines, flag); + + // 显示频道 long ch = getChannel(stack); if (ch == 0L) { lines.add(Component.translatable("item.extendedae_plus.channel_card.channel.unset")); } else { lines.add(Component.translatable("item.extendedae_plus.channel_card.channel", ch)); } + + // 显示所有者信息 + UUID ownerUUID = getOwnerUUID(stack); + String teamName = getTeamName(stack); + + if (ownerUUID != null) { + if (teamName != null && !teamName.isEmpty()) { + lines.add(Component.translatable("item.extendedae_plus.channel_card.owner.team", teamName)); + } else { + lines.add(Component.translatable("item.extendedae_plus.channel_card.owner.player", ownerUUID.toString().substring(0, 8))); + } + } else { + lines.add(Component.translatable("item.extendedae_plus.channel_card.owner.unset")); + } } @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; + long next; + + if (player.isShiftKeyDown()) { + // 潜行右键:减少频率 + next = Math.max(0L, ch - 1L); + } else { + // 普通右键:增加频率 + next = 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); } + + @Override + public boolean onLeftClickEntity(ItemStack stack, Player player, net.minecraft.world.entity.Entity entity) { + // 左键实体时不做任何事,避免伤害实体 + if (player.isShiftKeyDown()) { + return true; // 取消默认行为 + } + return super.onLeftClickEntity(stack, player, entity); + } + + @Override + public boolean onBlockStartBreak(ItemStack stack, net.minecraft.core.BlockPos pos, Player player) { + // 潜行左键方块时触发绑定/解绑 + if (!player.isShiftKeyDown()) { + return false; // 不拦截 + } + + if (player.level().isClientSide) { + return true; // 客户端拦截,防止破坏方块 + } + + // 服务端处理 + if (player.level() instanceof ServerLevel serverLevel) { + // 检查是否是收发器(让Block类处理) + var blockState = player.level().getBlockState(pos); + if (blockState.getBlock() instanceof com.extendedae_plus.content.wireless.WirelessTransceiverBlock) { + return false; // 不拦截,让Block类处理 + } + + // 执行绑定/解绑 + UUID currentOwner = getOwnerUUID(stack); + + if (currentOwner != null) { + // 已有所有者,清除 + clearOwner(stack); + player.displayClientMessage( + Component.translatable("item.extendedae_plus.channel_card.owner.cleared"), + true + ); + } else { + // 写入当前玩家的UUID和团队信息 + UUID playerUUID = player.getUUID(); + setOwnerUUID(stack, playerUUID); + + // 获取团队名称用于显示 + Component teamName = WirelessTeamUtil.getNetworkOwnerName(serverLevel, playerUUID); + setTeamName(stack, teamName.getString()); + + player.displayClientMessage( + Component.translatable("item.extendedae_plus.channel_card.owner.bound", teamName), + true + ); + } + } + + return true; // 拦截,防止破坏方块 + } } diff --git a/src/main/java/com/extendedae_plus/ae/wireless/WirelessMasterLink.java b/src/main/java/com/extendedae_plus/ae/wireless/WirelessMasterLink.java index 6d40cc2..98a3436 100644 --- a/src/main/java/com/extendedae_plus/ae/wireless/WirelessMasterLink.java +++ b/src/main/java/com/extendedae_plus/ae/wireless/WirelessMasterLink.java @@ -1,6 +1,9 @@ package com.extendedae_plus.ae.wireless; import net.minecraft.server.level.ServerLevel; +import org.jetbrains.annotations.Nullable; + +import java.util.UUID; /** * 主收发器端逻辑:负责在频率变化/加载时向注册中心登记唯一主端,卸载时反注册。 @@ -10,10 +13,16 @@ public class WirelessMasterLink { private final IWirelessEndpoint host; private long frequency; // 0 为未设置 private boolean registered; + @Nullable + private UUID placerId; // 放置者UUID public WirelessMasterLink(IWirelessEndpoint host) { this.host = host; } + + public void setPlacerId(@Nullable UUID placerId) { + this.placerId = placerId; + } public long getFrequency() { return frequency; } @@ -42,16 +51,16 @@ public class WirelessMasterLink { public boolean register() { ServerLevel level = host.getServerLevel(); - if (level == null || frequency == 0L) return false; - boolean ok = WirelessMasterRegistry.register(level, frequency, host); + if (level == null || frequency == 0L || placerId == null) return false; + boolean ok = WirelessMasterRegistry.register(level, frequency, placerId, host); this.registered = ok; return ok; } public void unregister() { ServerLevel level = host.getServerLevel(); - if (!registered || level == null || frequency == 0L) return; - WirelessMasterRegistry.unregister(level, frequency, host); + if (!registered || level == null || frequency == 0L || placerId == null) return; + WirelessMasterRegistry.unregister(level, frequency, placerId, host); registered = false; } diff --git a/src/main/java/com/extendedae_plus/ae/wireless/WirelessMasterRegistry.java b/src/main/java/com/extendedae_plus/ae/wireless/WirelessMasterRegistry.java index 6d411a8..110d2e1 100644 --- a/src/main/java/com/extendedae_plus/ae/wireless/WirelessMasterRegistry.java +++ b/src/main/java/com/extendedae_plus/ae/wireless/WirelessMasterRegistry.java @@ -1,43 +1,54 @@ package com.extendedae_plus.ae.wireless; import com.extendedae_plus.config.ModConfig; +import com.extendedae_plus.util.WirelessTeamUtil; import net.minecraft.resources.ResourceKey; import net.minecraft.server.level.ServerLevel; import net.minecraft.world.level.Level; +import org.jetbrains.annotations.Nullable; import java.lang.ref.WeakReference; import java.util.HashMap; import java.util.Map; import java.util.Objects; +import java.util.UUID; /** - * 无线主端注册中心:按 维度 + 频率 唯一注册一个主收发器端点。 + * 无线主端注册中心:按 维度 + 频率 + 所有者 唯一注册一个主收发器端点。 * 从端通过本注册中心按频率查找主端,实现一对多连接。 + * 所有者隔离:有FTBTeams时同队共享,没有时每个玩家独立。 */ public final class WirelessMasterRegistry { private WirelessMasterRegistry() {} private static final Map> MASTERS = new HashMap<>(); - public static synchronized boolean register(ServerLevel level, long frequency, IWirelessEndpoint endpoint) { + public static synchronized boolean register(ServerLevel level, long frequency, @Nullable UUID placerId, IWirelessEndpoint endpoint) { Objects.requireNonNull(level, "level"); Objects.requireNonNull(endpoint, "endpoint"); - if (frequency == 0L) return false; - final Key key = new Key(useGlobal() ? null : level.dimension(), frequency); + if (frequency == 0L || placerId == null) return false; + + // 获取网络所有者UUID(FTBTeams队伍UUID或玩家UUID) + UUID ownerUUID = WirelessTeamUtil.getNetworkOwnerUUID(level, placerId); + final Key key = new Key(useGlobal() ? null : level.dimension(), frequency, ownerUUID); + cleanupIfCleared(key); var existing = MASTERS.get(key); var existingVal = existing == null ? null : existing.get(); if (existingVal != null && !existingVal.isEndpointRemoved()) { - // 同维度同频率已经有主端 + // 同维度同频率同所有者已经有主端 return false; } MASTERS.put(key, new WeakReference<>(endpoint)); return true; } - public static synchronized void unregister(ServerLevel level, long frequency, IWirelessEndpoint endpoint) { - if (frequency == 0L || level == null) return; - final Key key = new Key(useGlobal() ? null : level.dimension(), frequency); + public static synchronized void unregister(ServerLevel level, long frequency, @Nullable UUID placerId, IWirelessEndpoint endpoint) { + if (frequency == 0L || level == null || placerId == null) return; + + UUID ownerUUID = WirelessTeamUtil.getNetworkOwnerUUID(level, placerId); + final Key key = new Key(useGlobal() ? null : level.dimension(), frequency, ownerUUID); + var ref = MASTERS.get(key); if (ref != null) { var cur = ref.get(); @@ -47,9 +58,12 @@ public final class WirelessMasterRegistry { } } - public static synchronized IWirelessEndpoint get(ServerLevel level, long frequency) { - if (frequency == 0L || level == null) return null; - final Key key = new Key(useGlobal() ? null : level.dimension(), frequency); + public static synchronized IWirelessEndpoint get(ServerLevel level, long frequency, @Nullable UUID placerId) { + if (frequency == 0L || level == null || placerId == null) return null; + + UUID ownerUUID = WirelessTeamUtil.getNetworkOwnerUUID(level, placerId); + final Key key = new Key(useGlobal() ? null : level.dimension(), frequency, ownerUUID); + cleanupIfCleared(key); var ref = MASTERS.get(key); return ref == null ? null : ref.get(); @@ -66,9 +80,12 @@ public final class WirelessMasterRegistry { return ModConfig.INSTANCE.wirelessCrossDimEnable; } - private record Key(ResourceKey dim, long freq) { - @Override public String toString() { - return (dim == null ? "*" : dim.location().toString()) + "#" + freq; + private record Key(@Nullable ResourceKey dim, long freq, UUID owner) { + @Override + public String toString() { + return (dim == null ? "*" : dim.location().toString()) + + "#" + freq + + "@" + owner; } } } diff --git a/src/main/java/com/extendedae_plus/ae/wireless/WirelessSlaveLink.java b/src/main/java/com/extendedae_plus/ae/wireless/WirelessSlaveLink.java index 8196349..da1123c 100644 --- a/src/main/java/com/extendedae_plus/ae/wireless/WirelessSlaveLink.java +++ b/src/main/java/com/extendedae_plus/ae/wireless/WirelessSlaveLink.java @@ -5,18 +5,22 @@ import appeng.api.networking.IGridNode; import appeng.me.service.helpers.ConnectionWrapper; import com.extendedae_plus.config.ModConfig; import net.minecraft.server.level.ServerLevel; +import org.jetbrains.annotations.Nullable; import java.util.Objects; +import java.util.UUID; /** * 从收发器连接器: * - 通过频率查找同维度主收发器; * - 校验距离(<= ModConfigs.WIRELESS_MAX_RANGE); - * - 动态创建/销毁 AE2 连接(GridConnection),实现“一主多从”。 + * - 动态创建/销毁 AE2 连接(GridConnection),实现"一主多从"。 */ public class WirelessSlaveLink { private final IWirelessEndpoint host; private long frequency; // 0 未设置 + @Nullable + private UUID placerId; // 放置者UUID private ConnectionWrapper connection = new ConnectionWrapper(null); private boolean shutdown = true; @@ -25,6 +29,10 @@ public class WirelessSlaveLink { public WirelessSlaveLink(IWirelessEndpoint host) { this.host = Objects.requireNonNull(host); } + + public void setPlacerId(@Nullable UUID placerId) { + this.placerId = placerId; + } public void setFrequency(long frequency) { if (this.frequency != frequency) { @@ -55,12 +63,12 @@ public class WirelessSlaveLink { return; } final ServerLevel level = host.getServerLevel(); - if (level == null || frequency == 0L) { + if (level == null || frequency == 0L || placerId == null) { destroyConnection(); return; } - IWirelessEndpoint master = WirelessMasterRegistry.get(level, frequency); + IWirelessEndpoint master = WirelessMasterRegistry.get(level, frequency, placerId); shutdown = false; distance = 0.0D; diff --git a/src/main/java/com/extendedae_plus/client/event/ChannelCardClientHandler.java b/src/main/java/com/extendedae_plus/client/event/ChannelCardClientHandler.java new file mode 100644 index 0000000..a952edd --- /dev/null +++ b/src/main/java/com/extendedae_plus/client/event/ChannelCardClientHandler.java @@ -0,0 +1,45 @@ +package com.extendedae_plus.client.event; + +import com.extendedae_plus.ExtendedAEPlus; +import com.extendedae_plus.init.ModItems; +import com.extendedae_plus.init.ModNetwork; +import com.extendedae_plus.network.ChannelCardBindPacket; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.ItemStack; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.event.entity.player.PlayerInteractEvent; +import net.minecraftforge.eventbus.api.SubscribeEvent; +import net.minecraftforge.fml.common.Mod; + +/** + * 频道卡客户端事件处理器 + * 处理左键空气事件并发送网络包到服务端 + */ +@Mod.EventBusSubscriber(modid = ExtendedAEPlus.MODID, value = Dist.CLIENT, bus = Mod.EventBusSubscriber.Bus.FORGE) +public class ChannelCardClientHandler { + + /** + * 左键空气事件(仅客户端) + */ + @SubscribeEvent + public static void onLeftClickEmpty(PlayerInteractEvent.LeftClickEmpty event) { + Player player = event.getEntity(); + + // 只处理潜行 + if (!player.isShiftKeyDown()) { + return; + } + + // 检查是否手持频道卡 + ItemStack stack = event.getItemStack(); + if (stack.getItem() != ModItems.CHANNEL_CARD.get()) { + return; + } + + // 发送网络包到服务端 + InteractionHand hand = event.getHand(); + ModNetwork.CHANNEL.sendToServer(new ChannelCardBindPacket(hand)); + } +} + diff --git a/src/main/java/com/extendedae_plus/content/wireless/WirelessTransceiverBlock.java b/src/main/java/com/extendedae_plus/content/wireless/WirelessTransceiverBlock.java index 0f0c9fc..8d2b47a 100644 --- a/src/main/java/com/extendedae_plus/content/wireless/WirelessTransceiverBlock.java +++ b/src/main/java/com/extendedae_plus/content/wireless/WirelessTransceiverBlock.java @@ -1,11 +1,15 @@ package com.extendedae_plus.content.wireless; +import com.extendedae_plus.ae.items.ChannelCardItem; import com.extendedae_plus.init.ModBlockEntities; +import com.extendedae_plus.init.ModItems; import net.minecraft.core.BlockPos; import net.minecraft.network.chat.Component; import net.minecraft.world.InteractionHand; import net.minecraft.world.InteractionResult; +import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.Items; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.Block; @@ -15,6 +19,9 @@ import net.minecraft.world.level.block.entity.BlockEntityTicker; import net.minecraft.world.level.block.entity.BlockEntityType; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.phys.BlockHitResult; +import org.jetbrains.annotations.Nullable; + +import java.util.UUID; public class WirelessTransceiverBlock extends Block implements EntityBlock { public WirelessTransceiverBlock(Properties props) { @@ -27,28 +34,71 @@ public class WirelessTransceiverBlock extends Block implements EntityBlock { } @Override - public void attack(BlockState state, Level level, BlockPos pos, Player player) { - // 潜行左键:减频(-1 或 -10) - if (!level.isClientSide && player.isShiftKeyDown()) { + public void setPlacedBy(Level level, BlockPos pos, BlockState state, @Nullable LivingEntity placer, ItemStack stack) { + super.setPlacedBy(level, pos, state, placer, stack); + if (!level.isClientSide && placer instanceof Player player) { BlockEntity be = level.getBlockEntity(pos); if (be instanceof WirelessTransceiverBlockEntity te) { - if (te.isLocked()) { - player.displayClientMessage(Component.literal("收发器已锁定,无法修改频道"), true); + te.setPlacerId(player.getUUID()); + } + } + } + + @Override + public void attack(BlockState state, Level level, BlockPos pos, Player player) { + if (!level.isClientSide) { + BlockEntity be = level.getBlockEntity(pos); + if (be instanceof WirelessTransceiverBlockEntity te) { + ItemStack mainHand = player.getMainHandItem(); + + // 潜行左键频道卡:写入频道卡信息到收发器 + if (player.isShiftKeyDown() && mainHand.getItem() == ModItems.CHANNEL_CARD.get()) { + handleChannelCardBinding(te, mainHand, player); super.attack(state, level, pos, player); return; } - int step = 1; - if (player.getMainHandItem().is(Items.REDSTONE_TORCH)) step = 10; - if (player.getMainHandItem().is(Items.STICK)) step = 10; - long f = te.getFrequency(); - f -= step; - if (f < 0) f = 0; - te.setFrequency(f); - player.displayClientMessage(Component.literal("频道:" + te.getFrequency()), true); + + // 潜行左键(其他物品):减频(-1 或 -10) + if (player.isShiftKeyDown()) { + if (te.isLocked()) { + player.displayClientMessage(Component.literal("收发器已锁定,无法修改频道"), true); + super.attack(state, level, pos, player); + return; + } + int step = 1; + if (mainHand.is(Items.REDSTONE_TORCH)) step = 10; + if (mainHand.is(Items.STICK)) step = 10; + long f = te.getFrequency(); + f -= step; + if (f < 0) f = 0; + te.setFrequency(f); + player.displayClientMessage(Component.literal("频道:" + te.getFrequency()), true); + } } } super.attack(state, level, pos, player); } + + /** + * 处理频道卡绑定到收发器 + */ + private void handleChannelCardBinding(WirelessTransceiverBlockEntity te, ItemStack channelCard, Player player) { + UUID cardOwner = ChannelCardItem.getOwnerUUID(channelCard); + + if (cardOwner != null) { + // 写入频道卡的所有者到收发器 + te.setPlacerId(cardOwner); + String teamName = ChannelCardItem.getTeamName(channelCard); + player.displayClientMessage( + Component.literal("已将收发器绑定至:" + (teamName != null ? teamName : cardOwner.toString().substring(0, 8))), + true + ); + } else { + // 频道卡未绑定所有者,使用当前玩家 + te.setPlacerId(player.getUUID()); + player.displayClientMessage(Component.literal("频道卡未绑定,已使用当前玩家"), true); + } + } @Override public InteractionResult use(BlockState state, Level level, BlockPos pos, Player player, InteractionHand hand, BlockHitResult hit) { 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 6884308..1c6bd07 100644 --- a/src/main/java/com/extendedae_plus/content/wireless/WirelessTransceiverBlockEntity.java +++ b/src/main/java/com/extendedae_plus/content/wireless/WirelessTransceiverBlockEntity.java @@ -18,6 +18,7 @@ import org.jetbrains.annotations.Nullable; import java.util.EnumSet; import java.util.Objects; +import java.util.UUID; /** * 无线收发器方块实体(骨架): @@ -25,6 +26,7 @@ import java.util.Objects; * - 频率设置; * - 集成 AE2 节点; * - 集成无线主/从逻辑。 + * - 支持FTBTeams队伍隔离(软依赖) */ public class WirelessTransceiverBlockEntity extends AEBaseBlockEntity implements IWirelessEndpoint, IInWorldGridNodeHost { @@ -33,6 +35,9 @@ public class WirelessTransceiverBlockEntity extends AEBaseBlockEntity implements private long frequency = 1L; private boolean masterMode = false; private boolean locked = false; + + @Nullable + private UUID placerId; // 放置者UUID,用于队伍隔离 private WirelessMasterLink masterLink; private WirelessSlaveLink slaveLink; @@ -96,6 +101,30 @@ public class WirelessTransceiverBlockEntity extends AEBaseBlockEntity implements } /* ===================== 公共方法(交互调用) ===================== */ + + /** + * 设置放置者UUID(在方块放置时调用) + */ + public void setPlacerId(@Nullable UUID placerId) { + if (this.placerId != null && !this.placerId.equals(placerId)) { + // 如果所有者改变,需要重新注册 + if (this.masterMode) { + masterLink.onUnloadOrRemove(); + } else { + slaveLink.onUnloadOrRemove(); + } + } + this.placerId = placerId; + this.masterLink.setPlacerId(placerId); + this.slaveLink.setPlacerId(placerId); + setChanged(); + } + + @Nullable + public UUID getPlacerId() { + return placerId; + } + public long getFrequency() { return frequency; } @@ -192,6 +221,9 @@ public class WirelessTransceiverBlockEntity extends AEBaseBlockEntity implements tag.putLong("frequency", frequency); tag.putBoolean("master", masterMode); tag.putBoolean("locked", locked); + if (placerId != null) { + tag.putUUID("placerId", placerId); + } if (managedNode != null) { managedNode.saveToNBT(tag); } @@ -203,6 +235,12 @@ public class WirelessTransceiverBlockEntity extends AEBaseBlockEntity implements this.frequency = tag.getLong("frequency"); this.masterMode = tag.getBoolean("master"); this.locked = tag.getBoolean("locked"); + + if (tag.hasUUID("placerId")) { + this.placerId = tag.getUUID("placerId"); + this.masterLink.setPlacerId(this.placerId); + this.slaveLink.setPlacerId(this.placerId); + } if (managedNode != null) { managedNode.loadFromNBT(tag); diff --git a/src/main/java/com/extendedae_plus/init/ModNetwork.java b/src/main/java/com/extendedae_plus/init/ModNetwork.java index 666991a..2ffe168 100644 --- a/src/main/java/com/extendedae_plus/init/ModNetwork.java +++ b/src/main/java/com/extendedae_plus/init/ModNetwork.java @@ -125,6 +125,12 @@ public class ModNetwork { .decoder(CraftingMonitorOpenProviderC2SPacket::decode) .consumerNetworkThread(CraftingMonitorOpenProviderC2SPacket::handle) .add(); + + CHANNEL.messageBuilder(ChannelCardBindPacket.class, nextId(), NetworkDirection.PLAY_TO_SERVER) + .encoder(ChannelCardBindPacket::encode) + .decoder(ChannelCardBindPacket::decode) + .consumerNetworkThread(ChannelCardBindPacket::handle) + .add(); } private static int nextId() { return id++; } 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 1578639..83213fe 100644 --- a/src/main/java/com/extendedae_plus/integration/jade/WirelessTransceiverProvider.java +++ b/src/main/java/com/extendedae_plus/integration/jade/WirelessTransceiverProvider.java @@ -66,7 +66,8 @@ public enum WirelessTransceiverProvider implements IServerDataProvider ctx) { + ctx.get().enqueueWork(() -> { + ServerPlayer player = ctx.get().getSender(); + if (player == null) { + return; + } + + ItemStack stack = player.getItemInHand(packet.hand); + if (stack.getItem() != ModItems.CHANNEL_CARD.get()) { + return; + } + + ServerLevel level = player.serverLevel(); + UUID currentOwner = ChannelCardItem.getOwnerUUID(stack); + + if (currentOwner != null) { + // 已有所有者,清除 + ChannelCardItem.clearOwner(stack); + player.displayClientMessage( + Component.translatable("item.extendedae_plus.channel_card.owner.cleared"), + true + ); + } else { + // 写入当前玩家的UUID和团队信息 + UUID playerUUID = player.getUUID(); + ChannelCardItem.setOwnerUUID(stack, playerUUID); + + // 获取团队名称用于显示 + Component teamName = WirelessTeamUtil.getNetworkOwnerName(level, playerUUID); + ChannelCardItem.setTeamName(stack, teamName.getString()); + + player.displayClientMessage( + Component.translatable("item.extendedae_plus.channel_card.owner.bound", teamName), + true + ); + } + }); + ctx.get().setPacketHandled(true); + } +} + diff --git a/src/main/java/com/extendedae_plus/util/WirelessTeamUtil.java b/src/main/java/com/extendedae_plus/util/WirelessTeamUtil.java new file mode 100644 index 0000000..51401ef --- /dev/null +++ b/src/main/java/com/extendedae_plus/util/WirelessTeamUtil.java @@ -0,0 +1,221 @@ +package com.extendedae_plus.util; + +import net.minecraft.network.chat.Component; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.ServerPlayer; +import net.minecraftforge.fml.ModList; +import org.jetbrains.annotations.Nullable; + +import java.util.UUID; + +/** + * 无线收发器队伍工具类 + * 实现FTBTeams软依赖:有FTBTeams时使用队伍UUID,没有时使用玩家UUID + */ +public class WirelessTeamUtil { + + private static final boolean FTB_TEAMS_LOADED = ModList.get().isLoaded("ftbteams"); + + /** + * 获取用于无线网络隔离的UUID + * - 如果安装了FTBTeams且玩家在队伍中,返回队伍UUID(同队玩家共享) + * - 否则返回玩家自己的UUID(独立网络) + * + * @param level 服务端世界 + * @param playerUUID 玩家UUID + * @return 网络所有者UUID + */ + public static UUID getNetworkOwnerUUID(@Nullable ServerLevel level, UUID playerUUID) { + if (playerUUID == null) { + return null; + } + + if (!FTB_TEAMS_LOADED) { + return playerUUID; + } + + if (level == null) { + return playerUUID; + } + + try { + return getTeamUUID(level, playerUUID); + } catch (Exception e) { + // 如果FTBTeams API调用失败,回退到玩家UUID + return playerUUID; + } + } + + /** + * 获取网络所有者的显示名称(用于UI显示) + * + * @param level 服务端世界 + * @param playerUUID 玩家UUID + * @return 显示名称 + */ + public static Component getNetworkOwnerName(@Nullable ServerLevel level, UUID playerUUID) { + if (FTB_TEAMS_LOADED && level != null) { + try { + return getTeamName(level, playerUUID); + } catch (Exception ignored) { + } + } + + // 尝试获取玩家名称 + if (level != null) { + ServerPlayer player = level.getServer().getPlayerList().getPlayer(playerUUID); + if (player != null) { + return player.getName(); + } + } + + return Component.literal(playerUUID.toString()); + } + + /** + * 检查网络所有者是否有效(玩家在线或队伍存在) + * + * @param level 服务端世界 + * @param playerUUID 玩家UUID + * @return 是否有效 + */ + public static boolean hasNetworkOwner(@Nullable ServerLevel level, UUID playerUUID) { + if (FTB_TEAMS_LOADED && level != null) { + try { + return hasTeamOwner(level, playerUUID); + } catch (Exception ignored) { + } + } + + // 检查玩家是否在线 + if (level != null) { + return level.getServer().getPlayerList().getPlayer(playerUUID) != null; + } + + return false; + } + + // ==================== FTBTeams 集成(通过反射调用避免硬依赖)==================== + + private static UUID getTeamUUID(ServerLevel level, UUID playerUUID) { + try { + // 使用FTBTeams API + var apiClass = Class.forName("dev.ftb.mods.ftbteams.api.FTBTeamsAPI"); + var api = apiClass.getMethod("api").invoke(null); // 静态方法,返回API实例 + + // 检查Manager是否已加载(在api实例上调用) + Boolean isLoaded = (Boolean) api.getClass().getMethod("isManagerLoaded").invoke(api); + + if (!isLoaded) { + return playerUUID; + } + + var getManager = api.getClass().getMethod("getManager").invoke(api); + + if (getManager == null) { + return playerUUID; + } + + var managerClass = getManager.getClass(); + var getTeamForPlayer = managerClass.getMethod("getTeamForPlayerID", UUID.class); + var teamOptional = getTeamForPlayer.invoke(getManager, playerUUID); + + if (teamOptional != null) { + var optionalClass = teamOptional.getClass(); + var isPresent = (boolean) optionalClass.getMethod("isPresent").invoke(teamOptional); + + if (isPresent) { + var team = optionalClass.getMethod("get").invoke(teamOptional); + var teamClass = team.getClass(); + return (UUID) teamClass.getMethod("getTeamId").invoke(team); + } + } + } catch (Exception e) { + // 反射调用失败,回退 + } + + return playerUUID; + } + + private static Component getTeamName(ServerLevel level, UUID playerUUID) { + try { + var apiClass = Class.forName("dev.ftb.mods.ftbteams.api.FTBTeamsAPI"); + var api = apiClass.getMethod("api").invoke(null); + + // 检查Manager是否已加载 + Boolean isLoaded = (Boolean) api.getClass().getMethod("isManagerLoaded").invoke(api); + if (!isLoaded) { + // Manager未加载,回退到玩家名称 + ServerPlayer player = level.getServer().getPlayerList().getPlayer(playerUUID); + if (player != null) { + return player.getName(); + } + return Component.literal(playerUUID.toString()); + } + + var getManager = api.getClass().getMethod("getManager").invoke(api); + + if (getManager == null) { + return Component.literal(playerUUID.toString()); + } + + var managerClass = getManager.getClass(); + var getTeamForPlayer = managerClass.getMethod("getTeamForPlayerID", UUID.class); + var teamOptional = getTeamForPlayer.invoke(getManager, playerUUID); + + if (teamOptional != null) { + var optionalClass = teamOptional.getClass(); + var isPresent = (boolean) optionalClass.getMethod("isPresent").invoke(teamOptional); + + if (isPresent) { + var team = optionalClass.getMethod("get").invoke(teamOptional); + var teamClass = team.getClass(); + return (Component) teamClass.getMethod("getName").invoke(team); + } + } + } catch (Exception e) { + // 反射调用失败,回退 + } + + // 回退到玩家名称 + ServerPlayer player = level.getServer().getPlayerList().getPlayer(playerUUID); + if (player != null) { + return player.getName(); + } + + return Component.literal(playerUUID.toString()); + } + + private static boolean hasTeamOwner(ServerLevel level, UUID playerUUID) { + try { + var apiClass = Class.forName("dev.ftb.mods.ftbteams.api.FTBTeamsAPI"); + var api = apiClass.getMethod("api").invoke(null); + + // 检查Manager是否已加载 + Boolean isLoaded = (Boolean) api.getClass().getMethod("isManagerLoaded").invoke(api); + if (!isLoaded) { + return level.getServer().getPlayerList().getPlayer(playerUUID) != null; + } + + var getManager = api.getClass().getMethod("getManager").invoke(api); + + if (getManager == null) { + return level.getServer().getPlayerList().getPlayer(playerUUID) != null; + } + + var managerClass = getManager.getClass(); + var getTeamForPlayer = managerClass.getMethod("getTeamForPlayerID", UUID.class); + var teamOptional = getTeamForPlayer.invoke(getManager, playerUUID); + + if (teamOptional != null) { + var optionalClass = teamOptional.getClass(); + return (boolean) optionalClass.getMethod("isPresent").invoke(teamOptional); + } + } catch (Exception e) { + // 反射调用失败,回退 + } + + return level.getServer().getPlayerList().getPlayer(playerUUID) != null; + } +} + 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 392adcb..2fd413c 100644 --- a/src/main/resources/assets/extendedae_plus/lang/en_us.json +++ b/src/main/resources/assets/extendedae_plus/lang/en_us.json @@ -103,6 +103,11 @@ "item.extendedae_plus.channel_card.channel": "Frequency: %s", "item.extendedae_plus.channel_card.channel.unset": "Frequency: Unset", "item.extendedae_plus.channel_card.set": "Frequency set to: %s", + "item.extendedae_plus.channel_card.owner.unset": "Owner: Unbound", + "item.extendedae_plus.channel_card.owner.team": "Owner: %s", + "item.extendedae_plus.channel_card.owner.player": "Owner: Player %s", + "item.extendedae_plus.channel_card.owner.bound": "Bound to: %s", + "item.extendedae_plus.channel_card.owner.cleared": "Owner binding cleared", "group.pattern_provider.name": "Pattern Provider", "group.entity_ticker.name": "Entity Accelerator", "group.storage.name": "StorageBus" 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 5860c5d..fed6481 100644 --- a/src/main/resources/assets/extendedae_plus/lang/zh_cn.json +++ b/src/main/resources/assets/extendedae_plus/lang/zh_cn.json @@ -104,6 +104,11 @@ "item.extendedae_plus.channel_card.channel": "频率:%s", "item.extendedae_plus.channel_card.channel.unset": "频率:未设置", "item.extendedae_plus.channel_card.set": "已设置频率:%s", + "item.extendedae_plus.channel_card.owner.unset": "所有者:未绑定", + "item.extendedae_plus.channel_card.owner.team": "所有者:%s", + "item.extendedae_plus.channel_card.owner.player": "所有者:玩家 %s", + "item.extendedae_plus.channel_card.owner.bound": "已绑定至:%s", + "item.extendedae_plus.channel_card.owner.cleared": "已清除所有者绑定", "group.pattern_provider.name": "样板供应器", "group.entity_ticker.name": "实体加速器", "group.storage.name": "存储总线" From 30bb52e2e9ad6997ce1ef803ca96cf228b57cd67 Mon Sep 17 00:00:00 2001 From: GaLicn <133291877+GaLicn@users.noreply.github.com> Date: Sun, 5 Oct 2025 12:33:25 +0800 Subject: [PATCH 30/51] =?UTF-8?q?=E6=97=A0=E7=BA=BF=E6=94=B6=E5=8F=91?= =?UTF-8?q?=E5=99=A8=E6=B7=BB=E5=8A=A0jade=E6=98=BE=E7=A4=BA=E6=89=80?= =?UTF-8?q?=E6=9C=89=E8=80=85/=E5=9B=A2=E9=98=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../wireless/WirelessTransceiverBlock.java | 6 ++--- .../WirelessTransceiverBlockEntity.java | 26 +++++++++++++++++-- ...relessTransceiverJadePluginComponents.java | 16 ++++++++++++ .../jade/WirelessTransceiverProvider.java | 16 +++++++++++- .../assets/extendedae_plus/lang/en_us.json | 5 ++++ .../assets/extendedae_plus/lang/zh_cn.json | 5 ++++ 6 files changed, 68 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/extendedae_plus/content/wireless/WirelessTransceiverBlock.java b/src/main/java/com/extendedae_plus/content/wireless/WirelessTransceiverBlock.java index 8d2b47a..8041566 100644 --- a/src/main/java/com/extendedae_plus/content/wireless/WirelessTransceiverBlock.java +++ b/src/main/java/com/extendedae_plus/content/wireless/WirelessTransceiverBlock.java @@ -39,7 +39,7 @@ public class WirelessTransceiverBlock extends Block implements EntityBlock { if (!level.isClientSide && placer instanceof Player player) { BlockEntity be = level.getBlockEntity(pos); if (be instanceof WirelessTransceiverBlockEntity te) { - te.setPlacerId(player.getUUID()); + te.setPlacerId(player.getUUID(), player.getName().getString()); } } } @@ -87,15 +87,15 @@ public class WirelessTransceiverBlock extends Block implements EntityBlock { if (cardOwner != null) { // 写入频道卡的所有者到收发器 - te.setPlacerId(cardOwner); String teamName = ChannelCardItem.getTeamName(channelCard); + te.setPlacerId(cardOwner, teamName); player.displayClientMessage( Component.literal("已将收发器绑定至:" + (teamName != null ? teamName : cardOwner.toString().substring(0, 8))), true ); } else { // 频道卡未绑定所有者,使用当前玩家 - te.setPlacerId(player.getUUID()); + te.setPlacerId(player.getUUID(), player.getName().getString()); player.displayClientMessage(Component.literal("频道卡未绑定,已使用当前玩家"), true); } } 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 1c6bd07..ca61349 100644 --- a/src/main/java/com/extendedae_plus/content/wireless/WirelessTransceiverBlockEntity.java +++ b/src/main/java/com/extendedae_plus/content/wireless/WirelessTransceiverBlockEntity.java @@ -38,6 +38,8 @@ public class WirelessTransceiverBlockEntity extends AEBaseBlockEntity implements @Nullable private UUID placerId; // 放置者UUID,用于队伍隔离 + @Nullable + private String placerName; // 放置者名称,用于显示 private WirelessMasterLink masterLink; private WirelessSlaveLink slaveLink; @@ -103,9 +105,9 @@ public class WirelessTransceiverBlockEntity extends AEBaseBlockEntity implements /* ===================== 公共方法(交互调用) ===================== */ /** - * 设置放置者UUID(在方块放置时调用) + * 设置放置者UUID和名称(在方块放置时调用) */ - public void setPlacerId(@Nullable UUID placerId) { + public void setPlacerId(@Nullable UUID placerId, @Nullable String placerName) { if (this.placerId != null && !this.placerId.equals(placerId)) { // 如果所有者改变,需要重新注册 if (this.masterMode) { @@ -115,16 +117,29 @@ public class WirelessTransceiverBlockEntity extends AEBaseBlockEntity implements } } this.placerId = placerId; + this.placerName = placerName; this.masterLink.setPlacerId(placerId); this.slaveLink.setPlacerId(placerId); setChanged(); } + /** + * 仅设置UUID(兼容旧代码) + */ + public void setPlacerId(@Nullable UUID placerId) { + setPlacerId(placerId, null); + } + @Nullable public UUID getPlacerId() { return placerId; } + @Nullable + public String getPlacerName() { + return placerName; + } + public long getFrequency() { return frequency; } @@ -224,6 +239,9 @@ public class WirelessTransceiverBlockEntity extends AEBaseBlockEntity implements if (placerId != null) { tag.putUUID("placerId", placerId); } + if (placerName != null) { + tag.putString("placerName", placerName); + } if (managedNode != null) { managedNode.saveToNBT(tag); } @@ -241,6 +259,10 @@ public class WirelessTransceiverBlockEntity extends AEBaseBlockEntity implements this.masterLink.setPlacerId(this.placerId); this.slaveLink.setPlacerId(this.placerId); } + + if (tag.contains("placerName")) { + this.placerName = tag.getString("placerName"); + } if (managedNode != null) { managedNode.loadFromNBT(tag); 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 a9c7543..2c06c2b 100644 --- a/src/main/java/com/extendedae_plus/integration/jade/WirelessTransceiverJadePluginComponents.java +++ b/src/main/java/com/extendedae_plus/integration/jade/WirelessTransceiverJadePluginComponents.java @@ -83,6 +83,22 @@ public enum WirelessTransceiverJadePluginComponents implements IBlockComponentPr } } } + }, + OWNER("wt_owner") { + @Override + protected void add(BlockAccessor accessor, ITooltip tooltip, IPluginConfig config, CompoundTag data) { + if (data.contains("ownerName")) { + String ownerName = data.getString("ownerName"); + tooltip.add(Component.translatable("extendedae_plus.tooltip.owner", ownerName)); + } else if (data.contains("placerId")) { + // 有placerId但没有名称,显示UUID + java.util.UUID placerId = data.getUUID("placerId"); + tooltip.add(Component.translatable("extendedae_plus.tooltip.owner", placerId.toString().substring(0, 8) + "...")); + } else { + // 没有所有者信息(旧版本存档) + tooltip.add(Component.translatable("extendedae_plus.tooltip.owner.unset")); + } + } }; private final ResourceLocation uid; 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 83213fe..d5ea951 100644 --- a/src/main/java/com/extendedae_plus/integration/jade/WirelessTransceiverProvider.java +++ b/src/main/java/com/extendedae_plus/integration/jade/WirelessTransceiverProvider.java @@ -5,8 +5,10 @@ import appeng.api.networking.IGridNode; import com.extendedae_plus.ae.wireless.IWirelessEndpoint; import com.extendedae_plus.ae.wireless.WirelessMasterRegistry; import com.extendedae_plus.content.wireless.WirelessTransceiverBlockEntity; +import com.extendedae_plus.util.WirelessTeamUtil; import net.minecraft.core.BlockPos; import net.minecraft.nbt.CompoundTag; +import net.minecraft.network.chat.Component; import net.minecraft.resources.ResourceLocation; import snownee.jade.api.BlockAccessor; import snownee.jade.api.IServerDataProvider; @@ -62,11 +64,23 @@ public enum WirelessTransceiverProvider implements IServerDataProvider Date: Sun, 5 Oct 2025 12:41:41 +0800 Subject: [PATCH 31/51] =?UTF-8?q?=E5=85=BC=E5=AE=B9=E6=9C=AA=E8=AE=BE?= =?UTF-8?q?=E7=BD=AEowner=E7=9A=84=E6=94=B6=E5=8F=91=E5=99=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ae/wireless/WirelessMasterLink.java | 6 ++-- .../ae/wireless/WirelessMasterRegistry.java | 31 ++++++++++++++----- .../ae/wireless/WirelessSlaveLink.java | 3 +- ...relessTransceiverJadePluginComponents.java | 4 +-- .../assets/extendedae_plus/lang/en_us.json | 1 + .../assets/extendedae_plus/lang/zh_cn.json | 1 + 6 files changed, 34 insertions(+), 12 deletions(-) diff --git a/src/main/java/com/extendedae_plus/ae/wireless/WirelessMasterLink.java b/src/main/java/com/extendedae_plus/ae/wireless/WirelessMasterLink.java index 98a3436..b055611 100644 --- a/src/main/java/com/extendedae_plus/ae/wireless/WirelessMasterLink.java +++ b/src/main/java/com/extendedae_plus/ae/wireless/WirelessMasterLink.java @@ -51,7 +51,8 @@ public class WirelessMasterLink { public boolean register() { ServerLevel level = host.getServerLevel(); - if (level == null || frequency == 0L || placerId == null) return false; + if (level == null || frequency == 0L) return false; + // placerId可以为null(公共收发器模式) boolean ok = WirelessMasterRegistry.register(level, frequency, placerId, host); this.registered = ok; return ok; @@ -59,7 +60,8 @@ public class WirelessMasterLink { public void unregister() { ServerLevel level = host.getServerLevel(); - if (!registered || level == null || frequency == 0L || placerId == null) return; + if (!registered || level == null || frequency == 0L) return; + // placerId可以为null(公共收发器模式) WirelessMasterRegistry.unregister(level, frequency, placerId, host); registered = false; } diff --git a/src/main/java/com/extendedae_plus/ae/wireless/WirelessMasterRegistry.java b/src/main/java/com/extendedae_plus/ae/wireless/WirelessMasterRegistry.java index 110d2e1..d856d98 100644 --- a/src/main/java/com/extendedae_plus/ae/wireless/WirelessMasterRegistry.java +++ b/src/main/java/com/extendedae_plus/ae/wireless/WirelessMasterRegistry.java @@ -17,19 +17,30 @@ import java.util.UUID; * 无线主端注册中心:按 维度 + 频率 + 所有者 唯一注册一个主收发器端点。 * 从端通过本注册中心按频率查找主端,实现一对多连接。 * 所有者隔离:有FTBTeams时同队共享,没有时每个玩家独立。 + * 公共模式:placerId为null时使用公共UUID,所有人都能访问(向下兼容旧版本)。 */ public final class WirelessMasterRegistry { private WirelessMasterRegistry() {} private static final Map> MASTERS = new HashMap<>(); + + /** + * 公共收发器UUID(用于没有设置所有者的收发器) + * 所有placerId为null的收发器都使用这个UUID,实现公共访问 + */ + public static final UUID PUBLIC_NETWORK_UUID = new UUID(0, 0); public static synchronized boolean register(ServerLevel level, long frequency, @Nullable UUID placerId, IWirelessEndpoint endpoint) { Objects.requireNonNull(level, "level"); Objects.requireNonNull(endpoint, "endpoint"); - if (frequency == 0L || placerId == null) return false; + if (frequency == 0L) return false; + + // 获取网络所有者UUID + // placerId为null时使用公共UUID(向下兼容旧版本收发器) + UUID ownerUUID = placerId != null + ? WirelessTeamUtil.getNetworkOwnerUUID(level, placerId) + : PUBLIC_NETWORK_UUID; - // 获取网络所有者UUID(FTBTeams队伍UUID或玩家UUID) - UUID ownerUUID = WirelessTeamUtil.getNetworkOwnerUUID(level, placerId); final Key key = new Key(useGlobal() ? null : level.dimension(), frequency, ownerUUID); cleanupIfCleared(key); @@ -44,9 +55,12 @@ public final class WirelessMasterRegistry { } public static synchronized void unregister(ServerLevel level, long frequency, @Nullable UUID placerId, IWirelessEndpoint endpoint) { - if (frequency == 0L || level == null || placerId == null) return; + if (frequency == 0L || level == null) return; + + UUID ownerUUID = placerId != null + ? WirelessTeamUtil.getNetworkOwnerUUID(level, placerId) + : PUBLIC_NETWORK_UUID; - UUID ownerUUID = WirelessTeamUtil.getNetworkOwnerUUID(level, placerId); final Key key = new Key(useGlobal() ? null : level.dimension(), frequency, ownerUUID); var ref = MASTERS.get(key); @@ -59,9 +73,12 @@ public final class WirelessMasterRegistry { } public static synchronized IWirelessEndpoint get(ServerLevel level, long frequency, @Nullable UUID placerId) { - if (frequency == 0L || level == null || placerId == null) return null; + if (frequency == 0L || level == null) return null; + + UUID ownerUUID = placerId != null + ? WirelessTeamUtil.getNetworkOwnerUUID(level, placerId) + : PUBLIC_NETWORK_UUID; - UUID ownerUUID = WirelessTeamUtil.getNetworkOwnerUUID(level, placerId); final Key key = new Key(useGlobal() ? null : level.dimension(), frequency, ownerUUID); cleanupIfCleared(key); diff --git a/src/main/java/com/extendedae_plus/ae/wireless/WirelessSlaveLink.java b/src/main/java/com/extendedae_plus/ae/wireless/WirelessSlaveLink.java index da1123c..c97ab58 100644 --- a/src/main/java/com/extendedae_plus/ae/wireless/WirelessSlaveLink.java +++ b/src/main/java/com/extendedae_plus/ae/wireless/WirelessSlaveLink.java @@ -63,11 +63,12 @@ public class WirelessSlaveLink { return; } final ServerLevel level = host.getServerLevel(); - if (level == null || frequency == 0L || placerId == null) { + if (level == null || frequency == 0L) { destroyConnection(); return; } + // placerId可以为null(公共收发器模式) IWirelessEndpoint master = WirelessMasterRegistry.get(level, frequency, placerId); shutdown = false; distance = 0.0D; 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 2c06c2b..3f8f1d4 100644 --- a/src/main/java/com/extendedae_plus/integration/jade/WirelessTransceiverJadePluginComponents.java +++ b/src/main/java/com/extendedae_plus/integration/jade/WirelessTransceiverJadePluginComponents.java @@ -95,8 +95,8 @@ public enum WirelessTransceiverJadePluginComponents implements IBlockComponentPr java.util.UUID placerId = data.getUUID("placerId"); tooltip.add(Component.translatable("extendedae_plus.tooltip.owner", placerId.toString().substring(0, 8) + "...")); } else { - // 没有所有者信息(旧版本存档) - tooltip.add(Component.translatable("extendedae_plus.tooltip.owner.unset")); + // 没有所有者信息(公共收发器) + tooltip.add(Component.translatable("extendedae_plus.tooltip.owner.public")); } } }; 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 b52e8d2..3c8e58a 100644 --- a/src/main/resources/assets/extendedae_plus/lang/en_us.json +++ b/src/main/resources/assets/extendedae_plus/lang/en_us.json @@ -112,6 +112,7 @@ "extendedae_plus.tooltip.owner": "Owner: %s", "extendedae_plus.tooltip.owner.unset": "Owner: Unset", + "extendedae_plus.tooltip.owner.public": "Owner: Public", "group.pattern_provider.name": "Pattern Provider", "group.entity_ticker.name": "Entity Accelerator", 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 af35f2e..405cb16 100644 --- a/src/main/resources/assets/extendedae_plus/lang/zh_cn.json +++ b/src/main/resources/assets/extendedae_plus/lang/zh_cn.json @@ -113,6 +113,7 @@ "extendedae_plus.tooltip.owner": "所有者: %s", "extendedae_plus.tooltip.owner.unset": "所有者: 未设置", + "extendedae_plus.tooltip.owner.public": "所有者: 公共", "group.pattern_provider.name": "样板供应器", "group.entity_ticker.name": "实体加速器", From 3fffc27b0345d1c6bc3672068e44aea2e6a2951c Mon Sep 17 00:00:00 2001 From: GaLicn <133291877+GaLicn@users.noreply.github.com> Date: Sun, 5 Oct 2025 13:46:23 +0800 Subject: [PATCH 32/51] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E6=89=B3=E6=89=8B?= =?UTF-8?q?=E8=B0=83=E6=95=B4=E6=97=A0=E7=BA=BF=E6=94=B6=E5=8F=91=E5=99=A8?= =?UTF-8?q?=E9=A2=91=E7=8E=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../client/ui/FrequencyInputScreen.java | 122 ++++++++++++++++++ .../com/extendedae_plus/hooks/WrenchHook.java | 21 ++- .../com/extendedae_plus/init/ModNetwork.java | 6 + .../SetWirelessFrequencyC2SPacket.java | 80 ++++++++++++ .../assets/extendedae_plus/lang/en_us.json | 5 + .../assets/extendedae_plus/lang/zh_cn.json | 5 + 6 files changed, 233 insertions(+), 6 deletions(-) create mode 100644 src/main/java/com/extendedae_plus/client/ui/FrequencyInputScreen.java create mode 100644 src/main/java/com/extendedae_plus/network/SetWirelessFrequencyC2SPacket.java diff --git a/src/main/java/com/extendedae_plus/client/ui/FrequencyInputScreen.java b/src/main/java/com/extendedae_plus/client/ui/FrequencyInputScreen.java new file mode 100644 index 0000000..595664f --- /dev/null +++ b/src/main/java/com/extendedae_plus/client/ui/FrequencyInputScreen.java @@ -0,0 +1,122 @@ +package com.extendedae_plus.client.ui; + +import com.extendedae_plus.init.ModNetwork; +import com.extendedae_plus.network.SetWirelessFrequencyC2SPacket; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.components.Button; +import net.minecraft.client.gui.components.EditBox; +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.core.BlockPos; +import net.minecraft.network.chat.Component; + +/** + * 频率输入界面 + * 用于通过输入框设置无线收发器的频率 + */ +public class FrequencyInputScreen extends Screen { + private final BlockPos pos; + private final long currentFrequency; + private EditBox frequencyInput; + + public FrequencyInputScreen(BlockPos pos, long currentFrequency) { + super(Component.translatable("extendedae_plus.screen.frequency_input.title")); + this.pos = pos; + this.currentFrequency = currentFrequency; + } + + @Override + protected void init() { + int centerX = this.width / 2; + int centerY = this.height / 2; + + // 频率输入框 + this.frequencyInput = new EditBox(this.font, centerX - 100, centerY - 10, 200, 20, + Component.translatable("extendedae_plus.screen.frequency_input.field")); + this.frequencyInput.setValue(String.valueOf(currentFrequency)); + this.frequencyInput.setMaxLength(19); // long最大值是19位 + this.frequencyInput.setFilter(text -> { + // 只允许输入数字 + if (text.isEmpty()) return true; + try { + Long.parseLong(text); + return true; + } catch (NumberFormatException e) { + return false; + } + }); + this.addRenderableWidget(this.frequencyInput); + this.setInitialFocus(this.frequencyInput); + + // 确认按钮 + Button confirmButton = Button.builder(Component.translatable("extendedae_plus.screen.frequency_input.confirm"), + button -> onConfirm()) + .bounds(centerX - 105, centerY + 20, 100, 20) + .build(); + this.addRenderableWidget(confirmButton); + + // 取消按钮 + Button cancelButton = Button.builder(Component.translatable("gui.cancel"), + button -> onClose()) + .bounds(centerX + 5, centerY + 20, 100, 20) + .build(); + this.addRenderableWidget(cancelButton); + } + + private void onConfirm() { + try { + String text = this.frequencyInput.getValue(); + if (text.isEmpty()) { + // 空值视为0 + text = "0"; + } + long frequency = Long.parseLong(text); + if (frequency < 0) { + frequency = 0; + } + + // 发送数据包到服务端 + ModNetwork.CHANNEL.sendToServer(new SetWirelessFrequencyC2SPacket(pos, frequency)); + this.onClose(); + } catch (NumberFormatException e) { + // 输入无效,不做处理 + } + } + + @Override + public void render(net.minecraft.client.gui.GuiGraphics guiGraphics, int mouseX, int mouseY, float partialTicks) { + this.renderBackground(guiGraphics); + super.render(guiGraphics, mouseX, mouseY, partialTicks); + + // 绘制标题 + guiGraphics.drawCenteredString(this.font, this.title, this.width / 2, this.height / 2 - 40, 0xFFFFFF); + } + + @Override + public boolean isPauseScreen() { + return false; + } + + @Override + public boolean keyPressed(int keyCode, int scanCode, int modifiers) { + // 按回车键确认 + if (keyCode == 257 || keyCode == 335) { // ENTER or NUMPAD_ENTER + onConfirm(); + return true; + } + // 按ESC键取消 + if (keyCode == 256) { // ESC + onClose(); + return true; + } + return super.keyPressed(keyCode, scanCode, modifiers); + } + + @Override + public void tick() { + super.tick(); + if (this.frequencyInput != null) { + this.frequencyInput.tick(); + } + } +} + diff --git a/src/main/java/com/extendedae_plus/hooks/WrenchHook.java b/src/main/java/com/extendedae_plus/hooks/WrenchHook.java index f098b45..5890cb1 100644 --- a/src/main/java/com/extendedae_plus/hooks/WrenchHook.java +++ b/src/main/java/com/extendedae_plus/hooks/WrenchHook.java @@ -2,9 +2,11 @@ package com.extendedae_plus.hooks; import appeng.util.InteractionUtil; import com.extendedae_plus.ExtendedAEPlus; +import com.extendedae_plus.client.ui.FrequencyInputScreen; import com.extendedae_plus.content.wireless.WirelessTransceiverBlockEntity; import appeng.block.crafting.CraftingUnitBlock; import appeng.blockentity.crafting.CraftingBlockEntity; +import net.minecraft.client.Minecraft; import net.minecraft.network.chat.Component; import net.minecraft.sounds.SoundEvents; import net.minecraft.sounds.SoundSource; @@ -14,6 +16,8 @@ import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.state.BlockState; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; import net.minecraftforge.event.entity.player.PlayerInteractEvent; import net.minecraftforge.eventbus.api.Event; import net.minecraftforge.eventbus.api.SubscribeEvent; @@ -86,19 +90,24 @@ public final class WrenchHook { } } } else if (!InteractionUtil.isInAlternateUseMode(player) && InteractionUtil.canWrenchRotate(stack)) { - // 未潜行 + 扳手:切换锁定状态 + // 未潜行 + 扳手:打开频率输入界面 BlockEntity be = level.getBlockEntity(hit.getBlockPos()); if (be instanceof WirelessTransceiverBlockEntity te) { - boolean newLocked = !te.isLocked(); - te.setLocked(newLocked); - // 提示玩家 - player.displayClientMessage(Component.literal(newLocked ? "已锁定收发器" : "已解锁收发器"), true); + if (level.isClientSide) { + // 客户端打开GUI + openFrequencyInputScreen(hit.getBlockPos(), te.getFrequency()); + } // 轻微反馈音效 - level.playSound(player, hit.getBlockPos(), SoundEvents.LEVER_CLICK, SoundSource.BLOCKS, 0.5F, newLocked ? 0.6F : 0.9F); + level.playSound(player, hit.getBlockPos(), SoundEvents.UI_BUTTON_CLICK.get(), SoundSource.BLOCKS, 0.5F, 1.0F); event.setCanceled(true); event.setCancellationResult(InteractionResult.sidedSuccess(level.isClientSide)); } } } + + @OnlyIn(Dist.CLIENT) + private static void openFrequencyInputScreen(net.minecraft.core.BlockPos pos, long currentFrequency) { + Minecraft.getInstance().setScreen(new FrequencyInputScreen(pos, currentFrequency)); + } } diff --git a/src/main/java/com/extendedae_plus/init/ModNetwork.java b/src/main/java/com/extendedae_plus/init/ModNetwork.java index 2ffe168..85a039e 100644 --- a/src/main/java/com/extendedae_plus/init/ModNetwork.java +++ b/src/main/java/com/extendedae_plus/init/ModNetwork.java @@ -131,6 +131,12 @@ public class ModNetwork { .decoder(ChannelCardBindPacket::decode) .consumerNetworkThread(ChannelCardBindPacket::handle) .add(); + + CHANNEL.messageBuilder(SetWirelessFrequencyC2SPacket.class, nextId(), NetworkDirection.PLAY_TO_SERVER) + .encoder(SetWirelessFrequencyC2SPacket::encode) + .decoder(SetWirelessFrequencyC2SPacket::decode) + .consumerNetworkThread(SetWirelessFrequencyC2SPacket::handle) + .add(); } private static int nextId() { return id++; } diff --git a/src/main/java/com/extendedae_plus/network/SetWirelessFrequencyC2SPacket.java b/src/main/java/com/extendedae_plus/network/SetWirelessFrequencyC2SPacket.java new file mode 100644 index 0000000..f3d398f --- /dev/null +++ b/src/main/java/com/extendedae_plus/network/SetWirelessFrequencyC2SPacket.java @@ -0,0 +1,80 @@ +package com.extendedae_plus.network; + +import com.extendedae_plus.content.wireless.WirelessTransceiverBlockEntity; +import net.minecraft.core.BlockPos; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.network.chat.Component; +import net.minecraft.server.level.ServerPlayer; +import net.minecraftforge.network.NetworkEvent; + +import java.util.function.Supplier; + +/** + * 设置无线收发器频率的网络数据包 + * 客户端发送到服务端,用于通过输入框设置频率 + */ +public class SetWirelessFrequencyC2SPacket { + + private final BlockPos pos; + private final long frequency; + + public SetWirelessFrequencyC2SPacket(BlockPos pos, long frequency) { + this.pos = pos; + this.frequency = frequency; + } + + public static void encode(SetWirelessFrequencyC2SPacket packet, FriendlyByteBuf buf) { + buf.writeBlockPos(packet.pos); + buf.writeLong(packet.frequency); + } + + public static SetWirelessFrequencyC2SPacket decode(FriendlyByteBuf buf) { + BlockPos pos = buf.readBlockPos(); + long frequency = buf.readLong(); + return new SetWirelessFrequencyC2SPacket(pos, frequency); + } + + public static void handle(SetWirelessFrequencyC2SPacket packet, Supplier ctx) { + ctx.get().enqueueWork(() -> { + ServerPlayer player = ctx.get().getSender(); + if (player == null) { + return; + } + + // 检查方块实体是否存在 + var level = player.serverLevel(); + if (!level.hasChunkAt(packet.pos)) { + return; + } + + var blockEntity = level.getBlockEntity(packet.pos); + if (!(blockEntity instanceof WirelessTransceiverBlockEntity te)) { + return; + } + + // 检查是否锁定 + if (te.isLocked()) { + player.displayClientMessage( + Component.literal("收发器已锁定,无法修改频道"), + true + ); + return; + } + + // 设置频率 + long newFreq = packet.frequency; + if (newFreq < 0) { + newFreq = 0; + } + te.setFrequency(newFreq); + + // 发送反馈消息 + player.displayClientMessage( + Component.literal("频道已设置为:" + te.getFrequency()), + true + ); + }); + ctx.get().setPacketHandled(true); + } +} + 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 3c8e58a..44be39c 100644 --- a/src/main/resources/assets/extendedae_plus/lang/en_us.json +++ b/src/main/resources/assets/extendedae_plus/lang/en_us.json @@ -114,6 +114,11 @@ "extendedae_plus.tooltip.owner.unset": "Owner: Unset", "extendedae_plus.tooltip.owner.public": "Owner: Public", + "extendedae_plus.screen.frequency_input.title": "Set Frequency", + "extendedae_plus.screen.frequency_input.current": "Current Frequency: %s", + "extendedae_plus.screen.frequency_input.field": "Frequency", + "extendedae_plus.screen.frequency_input.confirm": "Confirm", + "group.pattern_provider.name": "Pattern Provider", "group.entity_ticker.name": "Entity Accelerator", "group.storage.name": "StorageBus" 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 405cb16..f143c87 100644 --- a/src/main/resources/assets/extendedae_plus/lang/zh_cn.json +++ b/src/main/resources/assets/extendedae_plus/lang/zh_cn.json @@ -115,6 +115,11 @@ "extendedae_plus.tooltip.owner.unset": "所有者: 未设置", "extendedae_plus.tooltip.owner.public": "所有者: 公共", + "extendedae_plus.screen.frequency_input.title": "设置频率", + "extendedae_plus.screen.frequency_input.current": "当前频率:%s", + "extendedae_plus.screen.frequency_input.field": "频率", + "extendedae_plus.screen.frequency_input.confirm": "确认", + "group.pattern_provider.name": "样板供应器", "group.entity_ticker.name": "实体加速器", "group.storage.name": "存储总线" From 379a8353e524bc9525c698a8b08487c563bd0087 Mon Sep 17 00:00:00 2001 From: GaLicn <133291877+GaLicn@users.noreply.github.com> Date: Sun, 5 Oct 2025 14:04:39 +0800 Subject: [PATCH 33/51] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E6=89=B3=E6=89=8B?= =?UTF-8?q?=E8=B0=83=E6=95=B4=E6=97=A0=E7=BA=BF=E6=94=B6=E5=8F=91=E5=99=A8?= =?UTF-8?q?=E9=A2=91=E7=8E=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/extendedae_plus/hooks/WrenchHook.java | 40 ++++++++++++++++--- .../SetWirelessFrequencyC2SPacket.java | 19 +++++---- 2 files changed, 46 insertions(+), 13 deletions(-) diff --git a/src/main/java/com/extendedae_plus/hooks/WrenchHook.java b/src/main/java/com/extendedae_plus/hooks/WrenchHook.java index 5890cb1..1976a88 100644 --- a/src/main/java/com/extendedae_plus/hooks/WrenchHook.java +++ b/src/main/java/com/extendedae_plus/hooks/WrenchHook.java @@ -90,15 +90,15 @@ public final class WrenchHook { } } } else if (!InteractionUtil.isInAlternateUseMode(player) && InteractionUtil.canWrenchRotate(stack)) { - // 未潜行 + 扳手:打开频率输入界面 + // 未潜行 + 扳手:切换锁定状态 BlockEntity be = level.getBlockEntity(hit.getBlockPos()); if (be instanceof WirelessTransceiverBlockEntity te) { - if (level.isClientSide) { - // 客户端打开GUI - openFrequencyInputScreen(hit.getBlockPos(), te.getFrequency()); - } + boolean newLocked = !te.isLocked(); + te.setLocked(newLocked); + // 提示玩家 + player.displayClientMessage(Component.literal(newLocked ? "已锁定收发器" : "已解锁收发器"), true); // 轻微反馈音效 - level.playSound(player, hit.getBlockPos(), SoundEvents.UI_BUTTON_CLICK.get(), SoundSource.BLOCKS, 0.5F, 1.0F); + level.playSound(player, hit.getBlockPos(), SoundEvents.LEVER_CLICK, SoundSource.BLOCKS, 0.5F, newLocked ? 0.6F : 0.9F); event.setCanceled(true); event.setCancellationResult(InteractionResult.sidedSuccess(level.isClientSide)); @@ -106,6 +106,34 @@ public final class WrenchHook { } } + @SubscribeEvent + public static void onPlayerLeftClickBlock(PlayerInteractEvent.LeftClickBlock event) { + var player = event.getEntity(); + var level = event.getLevel(); + var pos = event.getPos(); + + // 非旁观者 + if (player.isSpectator()) { + return; + } + + ItemStack stack = player.getMainHandItem(); + // 潜行 + 扳手 + 无线收发器:打开频率输入界面 + if (InteractionUtil.isInAlternateUseMode(player) && InteractionUtil.canWrenchRotate(stack)) { + BlockEntity be = level.getBlockEntity(pos); + if (be instanceof WirelessTransceiverBlockEntity te) { + if (level.isClientSide) { + // 客户端打开GUI + openFrequencyInputScreen(pos, te.getFrequency()); + } + // 轻微反馈音效 + level.playSound(player, pos, SoundEvents.UI_BUTTON_CLICK.get(), SoundSource.BLOCKS, 0.5F, 1.0F); + + event.setCanceled(true); + } + } + } + @OnlyIn(Dist.CLIENT) private static void openFrequencyInputScreen(net.minecraft.core.BlockPos pos, long currentFrequency) { Minecraft.getInstance().setScreen(new FrequencyInputScreen(pos, currentFrequency)); diff --git a/src/main/java/com/extendedae_plus/network/SetWirelessFrequencyC2SPacket.java b/src/main/java/com/extendedae_plus/network/SetWirelessFrequencyC2SPacket.java index f3d398f..31413a5 100644 --- a/src/main/java/com/extendedae_plus/network/SetWirelessFrequencyC2SPacket.java +++ b/src/main/java/com/extendedae_plus/network/SetWirelessFrequencyC2SPacket.java @@ -52,13 +52,13 @@ public class SetWirelessFrequencyC2SPacket { return; } - // 检查是否锁定 - if (te.isLocked()) { - player.displayClientMessage( - Component.literal("收发器已锁定,无法修改频道"), - true - ); - return; + // 通过GUI设置频率时,忽略锁定状态 + // 临时保存锁定状态 + boolean wasLocked = te.isLocked(); + + // 临时解锁以允许设置 + if (wasLocked) { + te.setLocked(false); } // 设置频率 @@ -68,6 +68,11 @@ public class SetWirelessFrequencyC2SPacket { } te.setFrequency(newFreq); + // 恢复锁定状态 + if (wasLocked) { + te.setLocked(true); + } + // 发送反馈消息 player.displayClientMessage( Component.literal("频道已设置为:" + te.getFrequency()), From f634411a2838cb10367291533d2e14013db8ba55 Mon Sep 17 00:00:00 2001 From: GaLicn <133291877+GaLicn@users.noreply.github.com> Date: Sun, 5 Oct 2025 15:06:08 +0800 Subject: [PATCH 34/51] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E6=A8=A1=E7=BB=84?= =?UTF-8?q?=E5=B0=81=E9=9D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/META-INF/mods.toml | 2 +- src/main/resources/extendedae_plus.png | Bin 0 -> 204428 bytes 2 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 src/main/resources/extendedae_plus.png diff --git a/src/main/resources/META-INF/mods.toml b/src/main/resources/META-INF/mods.toml index 42a1ca5..e5b482e 100644 --- a/src/main/resources/META-INF/mods.toml +++ b/src/main/resources/META-INF/mods.toml @@ -9,7 +9,7 @@ version = "${version}" displayName = "ExtendedAE Plus" authors = "GaLi" description = '为extendedAE模组增强更多实用功能' -#logoFile = "" +logoFile = "extendedae_plus.png" [[dependencies.extendedae_plus]] modId = "forge" diff --git a/src/main/resources/extendedae_plus.png b/src/main/resources/extendedae_plus.png new file mode 100644 index 0000000000000000000000000000000000000000..842eabe02a89e8c0d4e9c19adef0fbe663bb69f9 GIT binary patch literal 204428 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