diff --git a/src/main/java/com/extendedae_plus/init/ModItems.java b/src/main/java/com/extendedae_plus/init/ModItems.java index ba08c8c..baeff56 100644 --- a/src/main/java/com/extendedae_plus/init/ModItems.java +++ b/src/main/java/com/extendedae_plus/init/ModItems.java @@ -9,6 +9,7 @@ import com.extendedae_plus.items.EntitySpeedTickerPartItem; import com.extendedae_plus.items.InfinityBigIntegerCellItem; import com.extendedae_plus.items.materials.ChannelCardItem; import com.extendedae_plus.items.materials.EntitySpeedCardItem; +import com.extendedae_plus.items.materials.VirtualCraftingCardItem; import com.extendedae_plus.util.ModCheckUtils; import net.minecraft.world.item.BlockItem; import net.minecraft.world.item.Item; @@ -84,6 +85,12 @@ public final class ModItems { () -> new ChannelCardItem(new Item.Properties()) ); + // 虚拟合成卡 + public static final RegistryObject VIRTUAL_CRAFTING_CARD = ITEMS.register( + "virtual_crafting_card", + () -> new VirtualCraftingCardItem(new Item.Properties()) + ); + public static final RegistryObject BASIC_CORE = ITEMS.register( "basic_core", () -> new BasicCoreItem(new Item.Properties()) diff --git a/src/main/java/com/extendedae_plus/init/UpgradeCards.java b/src/main/java/com/extendedae_plus/init/UpgradeCards.java index e6df658..35b4266 100644 --- a/src/main/java/com/extendedae_plus/init/UpgradeCards.java +++ b/src/main/java/com/extendedae_plus/init/UpgradeCards.java @@ -25,14 +25,18 @@ public final class UpgradeCards { Upgrades.add(ModItems.CHANNEL_CARD.get(), AEBlocks.INTERFACE, 1, interfaceGroup); Upgrades.add(ModItems.CHANNEL_CARD.get(), AEParts.INTERFACE, 1, interfaceGroup); - // 新增:样板供应器(方块与部件)支持频道卡,每台最多 1 张 + // 新增:样板供应器(方块与部件)支持频道卡、虚拟合成卡,每台最多 1 张 String patternProviderGroup = "group.pattern_provider.name"; Upgrades.add(ModItems.CHANNEL_CARD.get(), AEBlocks.PATTERN_PROVIDER, 1, patternProviderGroup); Upgrades.add(ModItems.CHANNEL_CARD.get(), AEParts.PATTERN_PROVIDER, 1, patternProviderGroup); + Upgrades.add(ModItems.VIRTUAL_CRAFTING_CARD.get(), AEBlocks.PATTERN_PROVIDER, 1, patternProviderGroup); + Upgrades.add(ModItems.VIRTUAL_CRAFTING_CARD.get(), AEParts.PATTERN_PROVIDER, 1, patternProviderGroup); // ExtendedAE 的扩展样板供应器(方块与部件) Upgrades.add(ModItems.CHANNEL_CARD.get(),EX_PATTERN_PROVIDER, 1, patternProviderGroup); Upgrades.add(ModItems.CHANNEL_CARD.get(),EX_PATTERN_PROVIDER_PART, 1, patternProviderGroup); + Upgrades.add(ModItems.VIRTUAL_CRAFTING_CARD.get(),EX_PATTERN_PROVIDER, 1, patternProviderGroup); + Upgrades.add(ModItems.VIRTUAL_CRAFTING_CARD.get(),EX_PATTERN_PROVIDER_PART, 1, patternProviderGroup); //EAE 的扩展接口与超大接口(方块与部件)支持频道卡 Upgrades.add(ModItems.CHANNEL_CARD.get(), EX_INTERFACE, 1, interfaceGroup); diff --git a/src/main/java/com/extendedae_plus/items/materials/VirtualCraftingCardItem.java b/src/main/java/com/extendedae_plus/items/materials/VirtualCraftingCardItem.java new file mode 100644 index 0000000..203a19c --- /dev/null +++ b/src/main/java/com/extendedae_plus/items/materials/VirtualCraftingCardItem.java @@ -0,0 +1,31 @@ +package com.extendedae_plus.items.materials; + +import appeng.items.materials.UpgradeCardItem; +import net.minecraft.network.chat.Component; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.TooltipFlag; +import net.minecraft.world.level.Level; +import org.jetbrains.annotations.Nullable; + +import java.util.List; + +/** + * 虚拟合成卡:安装在样板供应器中以触发自动完成逻辑。 + */ +public class VirtualCraftingCardItem extends UpgradeCardItem { + + public VirtualCraftingCardItem(Item.Properties properties) { + super(properties); + } + + @Override + public void appendHoverText(ItemStack stack, + @Nullable Level level, + List lines, + TooltipFlag flag) { + super.appendHoverText(stack, level, lines, flag); + lines.add(Component.translatable("item.extendedae_plus.virtual_crafting_card.tooltip_main")); + lines.add(Component.translatable("item.extendedae_plus.virtual_crafting_card.tooltip_detail")); + } +} diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/accessor/CraftingCpuLogicAccessor.java b/src/main/java/com/extendedae_plus/mixin/ae2/accessor/CraftingCpuLogicAccessor.java new file mode 100644 index 0000000..f6d9590 --- /dev/null +++ b/src/main/java/com/extendedae_plus/mixin/ae2/accessor/CraftingCpuLogicAccessor.java @@ -0,0 +1,13 @@ +package com.extendedae_plus.mixin.ae2.accessor; + +import appeng.crafting.execution.CraftingCpuLogic; +import appeng.crafting.execution.ExecutingCraftingJob; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +@Mixin(value = CraftingCpuLogic.class, remap = false) +public interface CraftingCpuLogicAccessor { + + @Accessor("job") + ExecutingCraftingJob extendedae_plus$getJob(); +} diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/accessor/ExecutingCraftingJobAccessor.java b/src/main/java/com/extendedae_plus/mixin/ae2/accessor/ExecutingCraftingJobAccessor.java new file mode 100644 index 0000000..e59809b --- /dev/null +++ b/src/main/java/com/extendedae_plus/mixin/ae2/accessor/ExecutingCraftingJobAccessor.java @@ -0,0 +1,15 @@ +package com.extendedae_plus.mixin.ae2.accessor; + +import appeng.api.crafting.IPatternDetails; +import appeng.crafting.execution.ExecutingCraftingJob; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +import java.util.Map; + +@Mixin(value = ExecutingCraftingJob.class, remap = false) +public interface ExecutingCraftingJobAccessor { + + @Accessor("tasks") + Map extendedae_plus$getTasks(); +} diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/accessor/ExecutingCraftingJobTaskProgressAccessor.java b/src/main/java/com/extendedae_plus/mixin/ae2/accessor/ExecutingCraftingJobTaskProgressAccessor.java new file mode 100644 index 0000000..411fc8b --- /dev/null +++ b/src/main/java/com/extendedae_plus/mixin/ae2/accessor/ExecutingCraftingJobTaskProgressAccessor.java @@ -0,0 +1,11 @@ +package com.extendedae_plus.mixin.ae2.accessor; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +@Mixin(targets = "appeng.crafting.execution.ExecutingCraftingJob$TaskProgress", remap = false) +public interface ExecutingCraftingJobTaskProgressAccessor { + + @Accessor("value") + long extendedae_plus$getValue(); +} 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 d5f4425..f7d3f62 100644 --- a/src/main/java/com/extendedae_plus/mixin/ae2/compat/PatternProviderLogicCompatMixin.java +++ b/src/main/java/com/extendedae_plus/mixin/ae2/compat/PatternProviderLogicCompatMixin.java @@ -1,18 +1,25 @@ package com.extendedae_plus.mixin.ae2.compat; +import appeng.api.crafting.IPatternDetails; +import appeng.api.networking.IGrid; import appeng.api.networking.IManagedGridNode; +import appeng.api.networking.crafting.ICraftingCPU; import appeng.api.networking.security.IActionSource; +import appeng.api.stacks.KeyCounter; import appeng.api.upgrades.IUpgradeInventory; import appeng.api.upgrades.IUpgradeableObject; import appeng.api.upgrades.UpgradeInventories; import appeng.helpers.patternprovider.PatternProviderLogic; import appeng.helpers.patternprovider.PatternProviderLogicHost; +import appeng.me.cluster.implementations.CraftingCPUCluster; 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.items.materials.ChannelCardItem; +import com.extendedae_plus.mixin.ae2.accessor.CraftingCpuLogicAccessor; +import com.extendedae_plus.mixin.ae2.accessor.ExecutingCraftingJobAccessor; import com.extendedae_plus.util.Logger; import net.minecraft.nbt.CompoundTag; import net.minecraft.world.item.ItemStack; @@ -23,7 +30,9 @@ 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 org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +import java.lang.reflect.Field; import java.util.List; /** @@ -64,13 +73,21 @@ public abstract class PatternProviderLogicCompatMixin implements IUpgradeableObj @Shadow private IActionSource actionSource; + @Unique + private boolean eap$compatVirtualCraftingEnabled = false; + + @Unique + private static Field eap$compatAppfluxUpgradesField; + + @Shadow + public abstract IGrid getGrid(); + @Unique private void eap$compatOnUpgradesChanged() { try { this.host.saveChanges(); - // 频道卡功能独立于升级槽功能,总是处理 + eap$compatSyncVirtualCraftingState(); if (UpgradeSlotCompat.shouldEnableChannelCard()) { - // 升级变更,重置并尝试初始化频道卡 eap$compatLastChannel = -1; eap$compatHasInitialized = false; eap$compatInitializeChannelLink(); @@ -79,11 +96,63 @@ public abstract class PatternProviderLogicCompatMixin implements IUpgradeableObj Logger.EAP$LOGGER.error("兼容性升级变更处理失败", e); } } + + @Unique + private void eap$compatSyncVirtualCraftingState() { + boolean hasCard = false; + var inventory = eap$compatGetEffectiveUpgradeInventory(); + if (inventory != null) { + for (ItemStack stack : inventory) { + if (!stack.isEmpty() && stack.getItem() == ModItems.VIRTUAL_CRAFTING_CARD.get()) { + hasCard = true; + break; + } + } + } + eap$compatVirtualCraftingEnabled = hasCard; + } + + @Unique + private void eap$compatTryVirtualCompletion(IPatternDetails patternDetails) { + if (!eap$compatVirtualCraftingEnabled) { + return; + } + + var grid = getGrid(); + if (grid == null) { + return; + } + + var craftingService = grid.getCraftingService(); + if (craftingService == null) { + return; + } + + for (ICraftingCPU cpu : craftingService.getCpus()) { + if (!cpu.isBusy()) { + continue; + } + if (cpu instanceof CraftingCPUCluster cluster) { + if (cluster.craftingLogic instanceof CraftingCpuLogicAccessor logicAccessor) { + var job = logicAccessor.extendedae_plus$getJob(); + if (job instanceof ExecutingCraftingJobAccessor accessor) { + var tasks = accessor.extendedae_plus$getTasks(); + var progress = tasks.get(patternDetails); + if (progress != null && progress.extendedae_plus$getValue() <= 1) { + cluster.cancelJob(); + break; + } + } + } + } + } + } // 监听appflux的升级变化 - 通过注入到appflux的af_$onUpgradesChanged方法 @Inject(method = "af_$onUpgradesChanged", at = @At("TAIL"), remap = false, require = 0) private void eap$onAppfluxUpgradesChanged(CallbackInfo ci) { try { + eap$compatSyncVirtualCraftingState(); if (UpgradeSlotCompat.shouldEnableChannelCard()) { // 升级变更,重置并尝试初始化频道卡 eap$compatLastChannel = -1; @@ -136,12 +205,12 @@ public abstract class PatternProviderLogicCompatMixin implements IUpgradeableObj try { if (UpgradeSlotCompat.shouldEnableUpgradeSlots() || UpgradeSlotCompat.shouldEnableChannelCard()) { this.eap$compatUpgrades.readFromNBT(tag, "compat_upgrades"); - // 从 NBT 加载后,重置并尝试初始化频道卡 if (UpgradeSlotCompat.shouldEnableChannelCard()) { eap$compatLastChannel = -1; eap$compatHasInitialized = false; eap$compatInitializeChannelLink(); } + eap$compatSyncVirtualCraftingState(); } } catch (Exception e) { Logger.EAP$LOGGER.error("兼容性升级加载失败", e); @@ -168,6 +237,7 @@ public abstract class PatternProviderLogicCompatMixin implements IUpgradeableObj try { if (UpgradeSlotCompat.shouldEnableUpgradeSlots() || UpgradeSlotCompat.shouldEnableChannelCard()) { this.eap$compatUpgrades.clear(); + eap$compatVirtualCraftingEnabled = false; } } catch (Exception e) { Logger.EAP$LOGGER.error("兼容性升级清理失败", e); @@ -177,15 +247,17 @@ public abstract class PatternProviderLogicCompatMixin implements IUpgradeableObj @Override public IUpgradeInventory getUpgrades() { if (UpgradeSlotCompat.shouldEnableUpgradeSlots()) { - // 不装appflux时,返回我们自己的升级槽 return this.eap$compatUpgrades != null ? this.eap$compatUpgrades : UpgradeInventories.empty(); } else { - // 装了appflux时,这个方法不应该被调用,因为appflux的Mixin会覆盖它 - // 但是为了安全起见,返回空的升级槽 - return UpgradeInventories.empty(); + return eap$compatGetEffectiveUpgradeInventory(); } } + @Inject(method = "pushPattern", at = @At("HEAD")) + private void eap$compatOnPushPattern(IPatternDetails patternDetails, KeyCounter[] inputHolder, CallbackInfoReturnable cir) { + eap$compatTryVirtualCompletion(patternDetails); + } + @Override public void eap$updateWirelessLink() { if (!UpgradeSlotCompat.shouldEnableChannelCard()) { @@ -202,7 +274,7 @@ public abstract class PatternProviderLogicCompatMixin implements IUpgradeableObj } @Unique - public void eap$compatInitializeChannelLink() { + private void eap$compatInitializeChannelLink() { if (!UpgradeSlotCompat.shouldEnableChannelCard()) { return; } @@ -233,21 +305,7 @@ public abstract class PatternProviderLogicCompatMixin implements IUpgradeableObj boolean found = false; // 获取升级槽 - 如果装了appflux则从appflux获取,否则从我们自己的获取 - IUpgradeInventory upgrades = null; - if (UpgradeSlotCompat.shouldEnableUpgradeSlots()) { - // 不装appflux时使用我们自己的升级槽 - upgrades = this.eap$compatUpgrades; - } else if (UpgradeSlotCompat.shouldEnableChannelCard()) { - // 装了appflux时,尝试从PatternProviderLogic获取升级槽 - try { - if (this instanceof IUpgradeableObject) { - IUpgradeableObject upgradeableThis = (IUpgradeableObject) this; - upgrades = upgradeableThis.getUpgrades(); - } - } catch (Exception e) { - Logger.EAP$LOGGER.error("获取appflux升级槽失败", e); - } - } + IUpgradeInventory upgrades = eap$compatGetEffectiveUpgradeInventory(); if (upgrades != null) { for (ItemStack stack : upgrades) { @@ -310,6 +368,36 @@ public abstract class PatternProviderLogicCompatMixin implements IUpgradeableObj } } + @Unique + private IUpgradeInventory eap$compatGetEffectiveUpgradeInventory() { + if (UpgradeSlotCompat.shouldEnableUpgradeSlots()) { + return this.eap$compatUpgrades; + } + + if (!UpgradeSlotCompat.shouldEnableChannelCard()) { + return null; + } + + if (this.eap$compatUpgrades != null && this.eap$compatUpgrades != UpgradeInventories.empty()) { + return this.eap$compatUpgrades; + } + + try { + if (eap$compatAppfluxUpgradesField == null) { + eap$compatAppfluxUpgradesField = PatternProviderLogic.class.getDeclaredField("af_$upgrades"); + eap$compatAppfluxUpgradesField.setAccessible(true); + } + Object value = eap$compatAppfluxUpgradesField.get(this); + if (value instanceof IUpgradeInventory inventory) { + return inventory; + } + } catch (Exception e) { + Logger.EAP$LOGGER.error("获取appflux升级槽失败", e); + } + + return UpgradeInventories.empty(); + } + @Override public void eap$setClientWirelessState(boolean connected) { if (UpgradeSlotCompat.shouldEnableChannelCard()) { 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 4ffe9fe..96721d3 100644 --- a/src/main/resources/assets/extendedae_plus/lang/en_us.json +++ b/src/main/resources/assets/extendedae_plus/lang/en_us.json @@ -152,6 +152,9 @@ "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", + "item.extendedae_plus.virtual_crafting_card": "Virtual Crafting Card", + "item.extendedae_plus.virtual_crafting_card.tooltip_main": "Auto-completes crafting jobs once the current pattern is about to finish", + "item.extendedae_plus.virtual_crafting_card.tooltip_detail": "Install inside a Pattern Provider upgrade slot", "extendedae_plus.screen.frequency_input.title": "Set Frequency", "extendedae_plus.screen.frequency_input.current": "Current Frequency: %s", 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 229c609..2bd631a 100644 --- a/src/main/resources/assets/extendedae_plus/lang/zh_cn.json +++ b/src/main/resources/assets/extendedae_plus/lang/zh_cn.json @@ -152,6 +152,9 @@ "item.extendedae_plus.channel_card.owner.player": "所有者:玩家 %s", "item.extendedae_plus.channel_card.owner.bound": "已绑定至:%s", "item.extendedae_plus.channel_card.owner.cleared": "已清除所有者绑定", + "item.extendedae_plus.virtual_crafting_card": "虚拟合成卡", + "item.extendedae_plus.virtual_crafting_card.tooltip_main": "当前样板即将完成时尝试终止剩余合成", + "item.extendedae_plus.virtual_crafting_card.tooltip_detail": "需安装在样板供应器的升级槽内", "extendedae_plus.screen.frequency_input.title": "设置频率", "extendedae_plus.screen.frequency_input.current": "当前频率:%s", diff --git a/src/main/resources/extendedae_plus.mixins.json b/src/main/resources/extendedae_plus.mixins.json index 987b176..3780494 100644 --- a/src/main/resources/extendedae_plus.mixins.json +++ b/src/main/resources/extendedae_plus.mixins.json @@ -50,6 +50,9 @@ "ae2.accessor.MEStorageMenuAccessor", "ae2.accessor.PatternEncodingTermMenuAccessor", "ae2.accessor.PatternProviderLogicAccessor", + "ae2.accessor.CraftingCpuLogicAccessor", + "ae2.accessor.ExecutingCraftingJobAccessor", + "ae2.accessor.ExecutingCraftingJobTaskProgressAccessor", "ae2.accessor.PatternProviderMenuAccessor", "ae2.autopattern.CraftingCalculationMixin", "ae2.autopattern.CraftingServiceGetProvidersMixin",