diff --git a/build.gradle b/build.gradle index 2436a25..2875566 100644 --- a/build.gradle +++ b/build.gradle @@ -60,6 +60,15 @@ repositories { } maven { url "https://repo.spongepowered.org/maven" } maven { url "https://dl.cloudsmith.io/public/geckolib3/geckolib/maven/" } + maven { // Registrate + url = "https://maven.tterrag.com/" + content { + // need to be specific here due to version overlaps + includeGroup("com.jozufozu.flywheel") + includeGroup("com.tterrag.registrate") + includeGroup("com.simibubi.create") + } + } } dependencies { @@ -70,7 +79,7 @@ dependencies { //exendedae前置 modImplementation "curse.maven:glodium-957920:${glodium_version}" //extendedAE - modImplementation files('libs/ExtendedAE-1.20-1.4.2-forge.jar') + modImplementation files('libs/ExtendedAE-1.20-1.4.7-forge.jar') modRuntimeOnly fileTree(dir: 'libs', includes: ['*.jar']) //ae2 @@ -87,8 +96,19 @@ dependencies { modCompileOnly "curse.maven:jade-324717:${jade_version}" // GregTech - modCompileOnly "curse.maven:gregtechceu-modern-890405:${gregtech_version}" - modCompileOnly "curse.maven:ldlib-626676:${ldlib_version}" + modImplementation "curse.maven:gregtechceu-modern-890405:${gregtech_version}" + modImplementation "curse.maven:ldlib-626676:5775541" + modImplementation files('libs/gtlcore-1.2.2.4-fix8.jar') + modImplementation("dev:gtmthings-1.3.5.b") + modImplementation("dev:resourcefullib-forge-1.20.1-2.1.29") + modImplementation("dev:resourcefulconfig-forge-1.20.1-2.1.3") + modImplementation("dev:botarium-forge-1.20.1-2.3.4") + modImplementation("dev:ad_astra-forge-1.20.1-1.15.20") + implementation(annotationProcessor("io.github.llamalad7:mixinextras-common:0.2.0")) + implementation(include("io.github.llamalad7:mixinextras-forge:0.2.0")) + modImplementation("com.tterrag.registrate:Registrate:MC1.20-1.3.11") + modImplementation("curse.maven:kubejs-238086:5454840") + modImplementation("curse.maven:rhino-416294:6186971") //curios modRuntimeOnly "curse.maven:curios-309927:${curios_version}" @@ -96,7 +116,7 @@ dependencies { // Runtime test modRuntimeOnly "curse.maven:curios-309927:${curios_version}" - modRuntimeOnly "curse.maven:jade-324717:${jade_version}" + modImplementation "curse.maven:jade-324717:5339264" modRuntimeOnly "curse.maven:architectury-api-419699:${architectury_version}" modRuntimeOnly "curse.maven:applied-energistics-2-wireless-terminals-459929:${wireless_terminals_version}" modRuntimeOnly "mezz.jei:jei-${minecraft_version}-forge:${jei_version}" @@ -105,9 +125,14 @@ dependencies { //jec modCompileOnly "curse.maven:just-enough-characters-250702:6680042" + //拼音搜索 + modRuntimeOnly("dev:jecharacters-1.20.1-forge-4.5.11-dev-shadow") + //输入法冲突 + modRuntimeOnly("curse.maven:cloth-config-348521:5729105") + modRuntimeOnly("curse.maven:IMBlocker-483760:6922546") //mae2 - // modRuntimeOnly "curse.maven:modern-ae2-additions-1028068:6827727" + // modRuntimeOnly "curse.maven:modern-ae2-additions-1028068:6827727" modCompileOnly "curse.maven:modern-ae2-additions-1028068:6827727" //aea @@ -125,8 +150,10 @@ dependencies { //ftbteams modCompileOnly "curse.maven:ftb-teams-forge-404468:6130786" modCompileOnly "curse.maven:ftb-library-forge-404465:6807424" + modCompileOnly "curse.maven:ftb-chunk-forge-314906:6431735" modRuntimeOnly "curse.maven:ftb-teams-forge-404468:6130786" modRuntimeOnly "curse.maven:ftb-library-forge-404465:6807424" + modRuntimeOnly "curse.maven:ftb-chunk-forge-314906:6431735" } compileJava { diff --git a/gradle.properties b/gradle.properties index fa4c87d..d360992 100644 --- a/gradle.properties +++ b/gradle.properties @@ -26,7 +26,7 @@ rei_version=12.0.622 cloth_config_version=9.0.94 projecte_version=4901949 appliede_version=5364294 -gregtech_version=5369020 +gregtech_version=5753724 ldlib_version=5394816 ie_version=5224387 mixin_version=0.8.4 diff --git a/libs/ExtendedAE-1.20-1.4.7-forge.jar b/libs/ExtendedAE-1.20-1.4.7-forge.jar new file mode 100644 index 0000000..6cd5e65 Binary files /dev/null and b/libs/ExtendedAE-1.20-1.4.7-forge.jar differ diff --git a/libs/ad_astra-forge-1.20.1-1.15.20.jar b/libs/ad_astra-forge-1.20.1-1.15.20.jar new file mode 100644 index 0000000..b0210cb Binary files /dev/null and b/libs/ad_astra-forge-1.20.1-1.15.20.jar differ diff --git a/libs/botarium-forge-1.20.1-2.3.4.jar b/libs/botarium-forge-1.20.1-2.3.4.jar new file mode 100644 index 0000000..506c8d1 Binary files /dev/null and b/libs/botarium-forge-1.20.1-2.3.4.jar differ diff --git a/libs/gtlcore-1.2.2.4-fix8.jar b/libs/gtlcore-1.2.2.4-fix8.jar new file mode 100644 index 0000000..245fe48 Binary files /dev/null and b/libs/gtlcore-1.2.2.4-fix8.jar differ diff --git a/libs/gtmthings-1.3.5.b.jar b/libs/gtmthings-1.3.5.b.jar new file mode 100644 index 0000000..374ae1a Binary files /dev/null and b/libs/gtmthings-1.3.5.b.jar differ diff --git a/libs/jecharacters-1.20.1-forge-4.5.11-dev-shadow.jar b/libs/jecharacters-1.20.1-forge-4.5.11-dev-shadow.jar new file mode 100644 index 0000000..66a71ab Binary files /dev/null and b/libs/jecharacters-1.20.1-forge-4.5.11-dev-shadow.jar differ diff --git a/libs/resourcefulconfig-forge-1.20.1-2.1.3.jar b/libs/resourcefulconfig-forge-1.20.1-2.1.3.jar new file mode 100644 index 0000000..4326a1d Binary files /dev/null and b/libs/resourcefulconfig-forge-1.20.1-2.1.3.jar differ diff --git a/libs/resourcefullib-forge-1.20.1-2.1.29.jar b/libs/resourcefullib-forge-1.20.1-2.1.29.jar new file mode 100644 index 0000000..dd8e169 Binary files /dev/null and b/libs/resourcefullib-forge-1.20.1-2.1.29.jar differ diff --git a/src/main/java/com/extendedae_plus/config/ModConfig.java b/src/main/java/com/extendedae_plus/config/ModConfig.java index 57a1096..6454ec5 100644 --- a/src/main/java/com/extendedae_plus/config/ModConfig.java +++ b/src/main/java/com/extendedae_plus/config/ModConfig.java @@ -132,6 +132,14 @@ public final class ModConfig { @Configurable.ValueUpdateCallback(method = "onEntityTickerMultipliersUpdate") public String[] entityTickerMultipliers = {}; + @Configurable + @Configurable.Comment(value = { + "限制合成样板自动上传仅进入分子操纵者", + "开启后,合成样板将只自动上传到分子操纵者,不再上传至装配矩阵" + }) + @Configurable.Synchronized + public boolean restrictCraftingPatternToMolecular = false; + private static final ScheduledExecutorService EXECUTOR = Executors.newSingleThreadScheduledExecutor(); private static ScheduledFuture pendingPowerTask; private static final Object POWER_LOCK = new Object(); diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/autopattern/gtceu/GTLCoreMEPatternBufferPartMachineMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/autopattern/gtceu/GTLCoreMEPatternBufferPartMachineMixin.java new file mode 100644 index 0000000..9d405db --- /dev/null +++ b/src/main/java/com/extendedae_plus/mixin/ae2/autopattern/gtceu/GTLCoreMEPatternBufferPartMachineMixin.java @@ -0,0 +1,105 @@ +package com.extendedae_plus.mixin.ae2.autopattern.gtceu; + +import appeng.api.crafting.IPatternDetails; +import appeng.crafting.pattern.AEProcessingPattern; +import com.extendedae_plus.ae.api.crafting.ScaledProcessingPattern; +import com.extendedae_plus.api.smartDoubling.ISmartDoublingAwarePattern; +import com.google.common.collect.BiMap; +import org.gtlcore.gtlcore.common.machine.multiblock.part.ae.MEPatternBufferPartMachine; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Pseudo; +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.Redirect; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +import java.util.List; +import java.util.Map; +import java.util.Objects; + +@Pseudo +@Mixin(value = MEPatternBufferPartMachine.class, remap = false) +public class GTLCoreMEPatternBufferPartMachineMixin { + + @Final + @Shadow + private BiMap patternSlotMap; + + // 设置样板总成是否翻倍 + @Inject(method = "getAvailablePatterns", at = @At(value = "INVOKE", target = "Ljava/util/stream/Stream;toList()Ljava/util/List;", shift = At.Shift.BEFORE)) + private void beforeToList(CallbackInfoReturnable> cir) { + if (patternSlotMap == null) return; + for (Map.Entry entry : patternSlotMap.entrySet()) { + IPatternDetails key = entry.getKey(); + if (key instanceof AEProcessingPattern proc && proc instanceof ISmartDoublingAwarePattern aware) { + aware.eap$setAllowScaling(true); + } + } + } + + // 重定向containsKey检查 + @Redirect(method = "pushPattern(Lappeng/api/crafting/IPatternDetails;[Lappeng/api/stacks/KeyCounter;)Z", + at = @At(value = "INVOKE", target = "Lcom/google/common/collect/BiMap;containsKey(Ljava/lang/Object;)Z")) + private boolean redirectContainsKey(BiMap detailsSlotMap, Object key) { + try { + // 如果key是ScaledProcessingPattern类型,尝试用其原始pattern进行判断 + if (key instanceof ScaledProcessingPattern scaled) { + IPatternDetails base = scaled.getOriginal(); + if (base != null) { + // 避免递归重定向,直接遍历keySet判断 + for (IPatternDetails d : detailsSlotMap.keySet()) { + if (Objects.equals(d, base)) return true; + } + } + } + // 常规判断,遍历keySet + for (IPatternDetails d : detailsSlotMap.keySet()) { + if (Objects.equals(d, key)) return true; + } + return false; + } catch (Throwable t) { + // 出现异常时,回退到常规判断 + for (IPatternDetails d : detailsSlotMap.keySet()) { + if (Objects.equals(d, key)) return true; + } + return false; + } + } + + @Redirect(method = "pushPattern(Lappeng/api/crafting/IPatternDetails;[Lappeng/api/stacks/KeyCounter;)Z", + at = @At(value = "INVOKE", target = "Lcom/google/common/collect/BiMap;get(Ljava/lang/Object;)Ljava/lang/Object;")) + private Object redirectGet(BiMap detailsSlotMap, Object key) { + try { + // 如果是 ScaledProcessingPattern,优先尝试其原始 pattern 对应的值 + if (key instanceof ScaledProcessingPattern scaled) { + IPatternDetails base = scaled.getOriginal(); + if (base != null) { + for (Map.Entry e : detailsSlotMap.entrySet()) { + if (Objects.equals(e.getKey(), base)) { + return e.getValue(); + } + } + } + } + + // 常规查找:遍历 entrySet 避免再次调用 BiMap.get 导致递归重定向 + for (Map.Entry e : detailsSlotMap.entrySet()) { + if (Objects.equals(e.getKey(), key)) { + return e.getValue(); + } + } + return null; + } catch (Throwable t) { + for (Map.Entry e : detailsSlotMap.entrySet()) { + if (Objects.equals(e.getKey(), key)) { + return e.getValue(); + } + } + return null; + } + } +} + + diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/autopattern/gtceu/MEPatternBufferPartMachineMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/autopattern/gtceu/MEPatternBufferPartMachineMixin.java new file mode 100644 index 0000000..80032d6 --- /dev/null +++ b/src/main/java/com/extendedae_plus/mixin/ae2/autopattern/gtceu/MEPatternBufferPartMachineMixin.java @@ -0,0 +1,105 @@ +package com.extendedae_plus.mixin.ae2.autopattern.gtceu; + +import appeng.api.crafting.IPatternDetails; +import appeng.crafting.pattern.AEProcessingPattern; +import com.extendedae_plus.ae.api.crafting.ScaledProcessingPattern; +import com.extendedae_plus.api.smartDoubling.ISmartDoublingAwarePattern; +import com.google.common.collect.BiMap; +import com.gregtechceu.gtceu.integration.ae2.machine.MEPatternBufferPartMachine; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Pseudo; +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.Redirect; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +import java.util.List; +import java.util.Map; +import java.util.Objects; + +@Pseudo +@Mixin(value = MEPatternBufferPartMachine.class, remap = false) +public class MEPatternBufferPartMachineMixin { + + @Shadow + @Final + private BiMap detailsSlotMap; + + // 设置样板总成是否翻倍 + @Inject(method = "getAvailablePatterns", at = @At(value = "INVOKE", target = "Ljava/util/stream/Stream;toList()Ljava/util/List;", shift = At.Shift.BEFORE)) + private void beforeToList(CallbackInfoReturnable> cir) { + if (detailsSlotMap == null) return; + for (Map.Entry entry : detailsSlotMap.entrySet()) { + IPatternDetails key = entry.getKey(); + if (key instanceof AEProcessingPattern proc && proc instanceof ISmartDoublingAwarePattern aware) { + aware.eap$setAllowScaling(true); + } + } + } + + // 重定向containsKey检查 + @Redirect(method = "pushPattern(Lappeng/api/crafting/IPatternDetails;[Lappeng/api/stacks/KeyCounter;)Z", + at = @At(value = "INVOKE", target = "Lcom/google/common/collect/BiMap;containsKey(Ljava/lang/Object;)Z")) + private boolean redirectContainsKey(BiMap detailsSlotMap, Object key) { + try { + // 如果key是ScaledProcessingPattern类型,尝试用其原始pattern进行判断 + if (key instanceof ScaledProcessingPattern scaled) { + IPatternDetails base = scaled.getOriginal(); + if (base != null) { + // 避免递归重定向,直接遍历keySet判断 + for (IPatternDetails d : detailsSlotMap.keySet()) { + if (Objects.equals(d, base)) return true; + } + } + } + // 常规判断,遍历keySet + for (IPatternDetails d : detailsSlotMap.keySet()) { + if (Objects.equals(d, key)) return true; + } + return false; + } catch (Throwable t) { + // 出现异常时,回退到常规判断 + for (IPatternDetails d : detailsSlotMap.keySet()) { + if (Objects.equals(d, key)) return true; + } + return false; + } + } + + @Redirect(method = "pushPattern(Lappeng/api/crafting/IPatternDetails;[Lappeng/api/stacks/KeyCounter;)Z", + at = @At(value = "INVOKE", target = "Lcom/google/common/collect/BiMap;get(Ljava/lang/Object;)Ljava/lang/Object;")) + private Object redirectGet(BiMap detailsSlotMap, Object key) { + try { + // 如果是 ScaledProcessingPattern,优先尝试其原始 pattern 对应的值 + if (key instanceof ScaledProcessingPattern scaled) { + IPatternDetails base = scaled.getOriginal(); + if (base != null) { + for (Map.Entry e : detailsSlotMap.entrySet()) { + if (Objects.equals(e.getKey(), base)) { + return e.getValue(); + } + } + } + } + + // 常规查找:遍历 entrySet 避免再次调用 BiMap.get 导致递归重定向 + for (Map.Entry e : detailsSlotMap.entrySet()) { + if (Objects.equals(e.getKey(), key)) { + return e.getValue(); + } + } + return null; + } catch (Throwable t) { + for (Map.Entry e : detailsSlotMap.entrySet()) { + if (Objects.equals(e.getKey(), key)) { + return e.getValue(); + } + } + return null; + } + } +} + + diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/menu/ContainerPatternEncodingTermMenuMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/menu/ContainerPatternEncodingTermMenuMixin.java index 2018545..9fd0842 100644 --- a/src/main/java/com/extendedae_plus/mixin/ae2/menu/ContainerPatternEncodingTermMenuMixin.java +++ b/src/main/java/com/extendedae_plus/mixin/ae2/menu/ContainerPatternEncodingTermMenuMixin.java @@ -4,6 +4,8 @@ import appeng.api.crafting.PatternDetailsHelper; import appeng.menu.me.items.PatternEncodingTermMenu; import appeng.menu.slot.RestrictedInputSlot; import appeng.parts.encoding.EncodingMode; +import com.extendedae_plus.config.ModConfig; +import com.extendedae_plus.util.uploadPattern.GTMatrixUploadUtil; import com.extendedae_plus.util.uploadPattern.MatrixUploadUtil; import com.glodblock.github.glodium.network.packet.sync.IActionHolder; import com.glodblock.github.glodium.network.packet.sync.Paras; @@ -47,7 +49,9 @@ public abstract class ContainerPatternEncodingTermMenuMixin implements IActionHo } var stack = this.encodedPatternSlot != null ? this.encodedPatternSlot.getItem() : net.minecraft.world.item.ItemStack.EMPTY; if (stack != null && !stack.isEmpty() && PatternDetailsHelper.isEncodedPattern(stack)) { - MatrixUploadUtil.uploadFromEncodingMenuToMatrix(sp, menu); + if (!ModConfig.INSTANCE.restrictCraftingPatternToMolecular) + MatrixUploadUtil.uploadFromEncodingMenuToMatrix(sp, menu); + else GTMatrixUploadUtil.uploadFromEncodingMenuToMatrix(sp, menu); } else { // 槽位可能尚未同步到位,继续下一 tick 重试 if (attemptsLeft > 0) { @@ -105,7 +109,9 @@ public abstract class ContainerPatternEncodingTermMenuMixin implements IActionHo // 为避免与 AE2 后续同步竞争,切到下一 tick 执行 sp.server.execute(() -> { try { - MatrixUploadUtil.uploadFromEncodingMenuToMatrix(sp, menu); + if (!ModConfig.INSTANCE.restrictCraftingPatternToMolecular) + MatrixUploadUtil.uploadFromEncodingMenuToMatrix(sp, menu); + else GTMatrixUploadUtil.uploadFromEncodingMenuToMatrix(sp, menu); } catch (Throwable ignored) { } }); @@ -117,7 +123,9 @@ public abstract class ContainerPatternEncodingTermMenuMixin implements IActionHo private void onEncodePatternReturn(CallbackInfoReturnable cir) { ItemStack itemStack = cir.getReturnValue(); if (itemStack != null && !itemStack.isEmpty()) { - itemStack.getOrCreateTag().putString("encodePlayer", this.epp$player.getGameProfile().getName()); + itemStack.getOrCreateTag() + .putString("encodePlayer", this.epp$player.getGameProfile() + .getName()); cir.setReturnValue(itemStack); } } diff --git a/src/main/java/com/extendedae_plus/mixin/gtceu/AEPatternViewSlotWidgetMixin.java b/src/main/java/com/extendedae_plus/mixin/gtceu/AEPatternViewSlotWidgetMixin.java new file mode 100644 index 0000000..9ab8018 --- /dev/null +++ b/src/main/java/com/extendedae_plus/mixin/gtceu/AEPatternViewSlotWidgetMixin.java @@ -0,0 +1,54 @@ +package com.extendedae_plus.mixin.gtceu; + +import appeng.api.crafting.PatternDetailsHelper; +import appeng.api.stacks.AEKey; +import com.extendedae_plus.content.ClientPatternHighlightStore; +import com.extendedae_plus.util.GuiUtil; +import com.gregtechceu.gtceu.integration.ae2.gui.widget.slot.AEPatternViewSlotWidget; +import com.lowdragmc.lowdraglib.gui.widget.SlotWidget; +import com.lowdragmc.lowdraglib.utils.Position; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.world.inventory.Slot; +import net.minecraft.world.item.ItemStack; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; +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; + +@OnlyIn(Dist.CLIENT) +@Mixin(value = AEPatternViewSlotWidget.class, remap = false) +public abstract class AEPatternViewSlotWidgetMixin { + + /** + * 在 AEPatternViewSlotWidget.drawBackgroundTexture(...) 尾部注入绘制数字逻辑 + */ + @Inject(method = "drawBackgroundTexture", at = @At("TAIL")) + private void onDrawInBackgroundTail(GuiGraphics graphics, int mouseX, int mouseY, CallbackInfo ci) { + if (!AEPatternViewSlotWidget.class.isInstance(this)) return; + // 把 this 强转为 SlotWidget(目标类继承自 SlotWidget) + SlotWidget self = (SlotWidget) (Object) this; + + Slot handler = self.getHandler(); + if (handler == null) return; + + // 使用 getRealStack 来尊重 setItemHook 的渲染替换 + ItemStack displayStack = self.getRealStack(handler.getItem()); + if (displayStack == null || displayStack.isEmpty()) return; + + ItemStack stack = handler.getItem(); + Position pos = self.getPosition(); // 来自 Widget 的方法,继承可用 + try { + var details = PatternDetailsHelper.decodePattern(stack, Minecraft.getInstance().level, false); + if (details != null && details.getOutputs() != null && details.getOutputs().length > 0) { + AEKey key = details.getOutputs()[0].what(); + if (key != null && ClientPatternHighlightStore.hasHighlight(key)) { + GuiUtil.drawSlotRainbowHighlight(graphics, pos.x + 1, pos.y + 1); + } + } + } catch (Throwable ignore) { + } + } +} \ No newline at end of file diff --git a/src/main/java/com/extendedae_plus/mixin/gtceu/ModularUIGuiContainerCloseMixin.java b/src/main/java/com/extendedae_plus/mixin/gtceu/ModularUIGuiContainerCloseMixin.java new file mode 100644 index 0000000..c2fb91f --- /dev/null +++ b/src/main/java/com/extendedae_plus/mixin/gtceu/ModularUIGuiContainerCloseMixin.java @@ -0,0 +1,19 @@ +package com.extendedae_plus.mixin.gtceu; + +import com.extendedae_plus.content.ClientPatternHighlightStore; +import com.lowdragmc.lowdraglib.gui.modular.ModularUIGuiContainer; +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; + +@Mixin(value = ModularUIGuiContainer.class) +public class ModularUIGuiContainerCloseMixin { + + @Inject(method = "removed", at = @At("HEAD")) + private void onRemoved(CallbackInfo ci) { + try { + ClientPatternHighlightStore.clearAll(); + } catch (Throwable ignored) {} + } +} \ No newline at end of file diff --git a/src/main/java/com/extendedae_plus/mixin/gtceu/SlotWidgetMixin.java b/src/main/java/com/extendedae_plus/mixin/gtceu/SlotWidgetMixin.java new file mode 100644 index 0000000..82e45f3 --- /dev/null +++ b/src/main/java/com/extendedae_plus/mixin/gtceu/SlotWidgetMixin.java @@ -0,0 +1,61 @@ +package com.extendedae_plus.mixin.gtceu; + +import com.extendedae_plus.util.GuiUtil; +import com.gregtechceu.gtceu.integration.ae2.gui.widget.slot.AEPatternViewSlotWidget; +import com.lowdragmc.lowdraglib.gui.util.DrawerHelper; +import com.lowdragmc.lowdraglib.gui.widget.SlotWidget; +import com.lowdragmc.lowdraglib.utils.Position; +import com.lowdragmc.lowdraglib.utils.Size; +import com.mojang.blaze3d.systems.RenderSystem; +import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.world.inventory.Slot; +import net.minecraft.world.item.ItemStack; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; +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; + + +@OnlyIn(Dist.CLIENT) +@Mixin(value = SlotWidget.class, remap = false) +public abstract class SlotWidgetMixin { + /** + * 在 SlotWidget.drawInBackground(...) 尾部注入绘制数字逻辑 + */ + @Inject(method = "drawInBackground(Lnet/minecraft/client/gui/GuiGraphics;IIF)V", at = @At("TAIL")) + private void onDrawInBackgroundTail(GuiGraphics graphics, int mouseX, int mouseY, float partialTicks, CallbackInfo ci) { + if (!AEPatternViewSlotWidget.class.isInstance(this)) return; + // 把 this 强转为 SlotWidget(目标类继承自 SlotWidget) + SlotWidget self = (SlotWidget) (Object) this; + + Slot handler = self.getHandler(); + if (handler == null) return; + + // 使用 getRealStack 来尊重 setItemHook 的渲染替换 + ItemStack displayStack = self.getRealStack(handler.getItem()); + if (displayStack == null || displayStack.isEmpty()) return; + + ItemStack stack = handler.getItem(); + + String patternOutputText = GuiUtil.getPatternOutputText(stack); + + Position pos = self.getPosition(); // 来自 Widget 的方法,继承可用 + Size size = self.getSize(); // 同上 + + graphics.pose().pushPose(); + graphics.pose().translate(0.0F, 0.0F, 300.0F); + RenderSystem.disableDepthTest(); + DrawerHelper.drawStringFixedCorner( + graphics, + patternOutputText, + pos.x + size.width, + pos.y + size.height, + 0xFFFFFFFF, + true, + 0.75f); + RenderSystem.enableDepthTest(); + graphics.pose().popPose(); + } +} \ No newline at end of file diff --git a/src/main/java/com/extendedae_plus/network/crafting/CraftingMonitorOpenProviderC2SPacket.java b/src/main/java/com/extendedae_plus/network/crafting/CraftingMonitorOpenProviderC2SPacket.java index de8bf52..4f02901 100644 --- a/src/main/java/com/extendedae_plus/network/crafting/CraftingMonitorOpenProviderC2SPacket.java +++ b/src/main/java/com/extendedae_plus/network/crafting/CraftingMonitorOpenProviderC2SPacket.java @@ -70,8 +70,18 @@ public class CraftingMonitorOpenProviderC2SPacket { // 遍历所有样板,找到第一个可用 Provider 并打开 UI for (var pattern : patterns) { - var provider = PatternLocator.findValidProvider(craftingService, pattern, grid); - if (provider == null) continue; + var provider = CraftingMonitorOpenProviderC2SPacket.PatternLocator.findValidProvider(craftingService, pattern, grid); + if (provider == null) { + for (var pd : craftingService.getProviders(pattern)) { + if (pd instanceof com.gregtechceu.gtceu.integration.ae2.machine.MEPatternBufferPartMachine machine) { + gtmOpenUI(machine, player, pattern); + return; + } else if (pd instanceof org.gtlcore.gtlcore.common.machine.multiblock.part.ae.MEPatternBufferPartMachine machine) { + gtmOpenUI(machine, player, pattern); + return; + } + } + } try { ProviderUIHelper.openProviderUI(provider, pattern, player); @@ -83,6 +93,56 @@ public class CraftingMonitorOpenProviderC2SPacket { // ===================== 内部工具类 ===================== + /** + * 兼容gtm + * + * @param machine 样板总成 + * @param player 玩家 + * @param pattern 样板 + */ + private static void gtmOpenUI(MetaMachine machine, ServerPlayer player, IPatternDetails pattern) { + try { + BlockPos pos = machine.getPos(); + Level level = machine.getLevel(); + if (pos == null || level == null) return; + if (!level.isClientSide) { // 确保在服务器端执行 + MachineUIFactory.INSTANCE.openUI(MetaMachine.getMachine(level, pos), player); + } + + // 聊天提示 + if (CompareModVersionUtil.compareTo("expatternprovider", "1.4.7")) { + player.displayClientMessage( + MessageUtil.createEnhancedHighlightMessage( + player, + pos, + level.dimension(), + "chat.ex_pattern_access_terminal.pos"), + false + ); + } else { + player.displayClientMessage( + Component.translatable( + "chat.ex_pattern_access_terminal.pos", + pos.toShortString(), + level.dimension() + .location() + .getPath() + ), + false + ); + } + + // 最后发送高亮包,保证界面已打开 + if (pattern.getOutputs() != null && pattern.getOutputs().length > 0 && pattern.getOutputs()[0] != null) { + AEKey key = pattern.getOutputs()[0].what(); + ModNetwork.CHANNEL.sendTo(new SetPatternHighlightS2CPacket(key, true), player.connection.connection, NetworkDirection.PLAY_TO_CLIENT); + } + + EAEHighlightHandler.highlight(pos, level.dimension(), System.currentTimeMillis() + 15000); + } catch (Exception ignored) { + } + } + /** * GridHelper: 从菜单中获取网格实例 */ diff --git a/src/main/java/com/extendedae_plus/util/CompareModVersionUtil.java b/src/main/java/com/extendedae_plus/util/CompareModVersionUtil.java new file mode 100644 index 0000000..98342c3 --- /dev/null +++ b/src/main/java/com/extendedae_plus/util/CompareModVersionUtil.java @@ -0,0 +1,35 @@ +package com.extendedae_plus.util; + +import net.minecraftforge.fml.ModList; +import net.minecraftforge.fml.loading.LoadingModList; +import org.apache.maven.artifact.versioning.ComparableVersion; + +import java.util.Optional; + +//此类代码借鉴于gtlcore +public class CompareModVersionUtil { + private static final ModList modList = ModList.get(); + private static final LoadingModList loadingModList = LoadingModList.get(); + + public static boolean compareTo(String modId, String version) { + Optional versionStr; + if (modList == null) { + versionStr = loadingModList + .getMods() + .stream() + .filter(mi -> mi.getModId() + .equals(modId)) + .findFirst() + .map(mi -> mi.getVersion() + .toString()); + } else { + versionStr = modList.getModContainerById(modId) + .map(mc -> mc.getModInfo() + .getVersion() + .toString()); + } + return versionStr + .map(v -> new ComparableVersion(v).compareTo(new ComparableVersion(version)) >= 0) + .orElse(false); + } +} diff --git a/src/main/java/com/extendedae_plus/util/uploadPattern/GTMatrixUploadUtil.java b/src/main/java/com/extendedae_plus/util/uploadPattern/GTMatrixUploadUtil.java new file mode 100644 index 0000000..8ff26a2 --- /dev/null +++ b/src/main/java/com/extendedae_plus/util/uploadPattern/GTMatrixUploadUtil.java @@ -0,0 +1,177 @@ +package com.extendedae_plus.util.uploadPattern; + +import appeng.api.crafting.IPatternDetails; +import appeng.api.crafting.PatternDetailsHelper; +import appeng.api.inventories.InternalInventory; +import appeng.api.networking.IGrid; +import appeng.api.networking.IGridNode; +import appeng.core.definitions.AEItems; +import appeng.crafting.pattern.AECraftingPattern; +import appeng.crafting.pattern.AESmithingTablePattern; +import appeng.crafting.pattern.AEStonecuttingPattern; +import appeng.menu.me.items.PatternEncodingTermMenu; +import appeng.menu.slot.RestrictedInputSlot; +import com.extendedae_plus.config.ModConfig; +import com.extendedae_plus.content.matrix.UploadCoreBlockEntity; +import com.extendedae_plus.mixin.ae2.accessor.PatternEncodingTermMenuAccessor; +import com.glodblock.github.extendedae.common.me.matrix.ClusterAssemblerMatrix; +import com.glodblock.github.extendedae.common.tileentities.matrix.TileAssemblerMatrixPattern; +import net.minecraft.network.chat.Component; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.item.ItemStack; +import org.gtlcore.gtlcore.common.machine.multiblock.part.ae.MEMolecularAssemblerIOPartMachine; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import static com.extendedae_plus.util.GlobalSendMessage.sendPlayerMessage; + +/** + * gtlcore 分子操纵者样板上传 + * 用于从 AE2 的样板编码终端上传至分子操纵者(仅合成样板)。 + */ +public final class GTMatrixUploadUtil { + private GTMatrixUploadUtil() { + } + + /** + * 从 AE2 的样板编码终端菜单上传当前“已编码合成样板”至 gtlcore 分子操纵者(仅合成样板) + * + * @param player 服务器玩家 + * @param menu PatternEncodingTermMenu + */ + public static void uploadFromEncodingMenuToMatrix(ServerPlayer player, PatternEncodingTermMenu menu) { + if (player == null || menu == null) return; + // 读取已编码槽位的物品 + RestrictedInputSlot encodedSlot = ((PatternEncodingTermMenuAccessor) menu).eap$getEncodedPatternSlot(); + ItemStack stack = encodedSlot.getItem(); + if (stack.isEmpty() || !PatternDetailsHelper.isEncodedPattern(stack)) return; + + // 仅允许“合成/锻造台/切石机样板” + IPatternDetails details = PatternDetailsHelper.decodePattern(stack, player.level()); + if (!(details instanceof AECraftingPattern + || details instanceof AESmithingTablePattern + || details instanceof AEStonecuttingPattern)) { + return; + } + + // 获取 AE 网络 + IGridNode node = menu.getNetworkNode(); + if (node == null) return; + + IGrid grid = node.getGrid(); + if (grid == null) return; + + int stackCount = stack.getCount(); + ItemStack toInsert = stack.copy(); + + // 收集所有可用的装配矩阵(图样模块)内部库存并逐一尝试(遵循其过滤规则) + List inventories = findAllGTMatrixPatternInventories(grid); + + // 在尝试上传之前,检查装配矩阵是否已经存在相同样板(物品与NBT完全一致) + if (GTMatrixContainsPattern(inventories, stack)) { + // 直接提醒并跳过上传,并将同等数量的空白样板放回空白样板槽,否则退回玩家背包 + sendPlayerMessage(player, Component.translatable("extendedae_plus.upload_to_GTMatrix.repetition")); + refundBlankPattern(player, menu, stackCount); + encodedSlot.set(ItemStack.EMPTY); + return; + } + // 尝试插入 + for (InternalInventory inv : inventories) { + if (inv == null) continue; + ItemStack remain = inv.addItems(toInsert); + if (remain.getCount() < stackCount) { + completeUploadSuccess(player, encodedSlot, stack, remain); + return; + } + } + + // 未找到可用矩阵或全部拒收 + sendPlayerMessage(player, + inventories.isEmpty() + ? Component.translatable("extendedae_plus.upload_to_matrix.fail_no_GTMatrix") + : Component.translatable("extendedae_plus.upload_to_GTMatrix.fail_full")); + } + + /** + * 在给定 AE Grid 中收集在线的GT分子的用于外部插入的内部库存。 + */ + private static List findAllGTMatrixPatternInventories(IGrid grid) { + List result = new ArrayList<>(); + if (grid == null) return result; + + try { + // 获取网络中所有 Pattern Tile + Set allTiles = grid.getMachines(MEMolecularAssemblerIOPartMachine.class); + + for (MEMolecularAssemblerIOPartMachine tile : allTiles) { + if (tile == null || !tile.isFormed() || !tile.getMainNode() + .isActive()) continue; + InternalInventory inv = tile.getTerminalPatternInventory(); + if (inv != null) { + result.add(inv); + } + } + } catch (Throwable ignored) { + } + return result; + } + + /** + * 检查GT分子装配矩阵中是否已存在与给定样板完全相同的物品(含NBT)。 + */ + // todo + private static boolean GTMatrixContainsPattern(@NotNull List inventories, @NotNull ItemStack pattern) { + for (InternalInventory inv : inventories) { + if (inv == null) continue; + for (int i = 0; i < inv.size(); i++) { + ItemStack s = inv.getStackInSlot(i); + if (!s.isEmpty() && ItemStack.isSameItemSameTags(s, pattern)) { + return true; + } + } + } + return false; + } + + /** + * 上传成功后处理:清空编码槽,发送提示。 + */ + private static void completeUploadSuccess(ServerPlayer player, RestrictedInputSlot encodedSlot, ItemStack stack, ItemStack remain) { + int inserted = stack.getCount() - remain.getCount(); + if (inserted > 0) { + stack.shrink(inserted); + if (stack.isEmpty()) encodedSlot.set(ItemStack.EMPTY); + sendPlayerMessage(player, Component.translatable("extendedae_plus.upload_to_GTMatrix.success")); + } + } + + /** + * 当发现重复样板时返还空白样板。 + */ + private static void refundBlankPattern(ServerPlayer player, PatternEncodingTermMenu menu, int count) { + try { + var accessor = (PatternEncodingTermMenuAccessor) menu; + var blankSlot = accessor.eap$getBlankPatternSlot(); + ItemStack blanks = AEItems.BLANK_PATTERN.stack(count); + if (blankSlot != null && blankSlot.mayPlace(blanks)) { + ItemStack remain = blankSlot.safeInsert(blanks); + if (!remain.isEmpty() && player != null) { + player.getInventory() + .placeItemBackInInventory(remain, false); + } + } else if (player != null) { + player.getInventory() + .placeItemBackInInventory(blanks, false); + } + } catch (Throwable t) { + if (player != null) { + player.getInventory() + .placeItemBackInInventory(AEItems.BLANK_PATTERN.stack(count), false); + } + } + } +} 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 a23fd44..a7656bb 100644 --- a/src/main/resources/assets/extendedae_plus/lang/en_us.json +++ b/src/main/resources/assets/extendedae_plus/lang/en_us.json @@ -47,9 +47,15 @@ "extendedae_plus.upload_to_matrix": "Upload to Assembly Matrix", "extendedae_plus.upload_to_matrix.success": "Pattern uploaded to the assembly matrix", + "extendedae_plus.upload_to_GTMatrix.success": "The template has been uploaded to the molecular manipulator", "extendedae_plus.upload_to_matrix.fail_not_crafting": "Only crafting patterns are supported; processing patterns will be ignored", "extendedae_plus.upload_to_matrix.fail_no_matrix": "No formed assembly matrix found in the current network", + "extendedae_plus.upload_to_matrix.fail_no_GTMatrix": "No formed molecular manipulators were found in the current network", "extendedae_plus.upload_to_matrix.fail_full": "The assembly matrix pattern inventory is full or cannot insert", + "extendedae_plus.upload_to_GTMatrix.fail_full": "The sample compartment of the molecular manipulator is full or cannot be inserted", + "extendedae_plus.upload_to_matrix.repetition": "The assembly matrix already exists on the same board, and the upload is skipped and a blank template is returned", + "extendedae_plus.upload_to_GTMatrix.repetition": "The same board already exists for the molecular manipulator, skipping the upload and returning the blank template", + "extendedae_plus.upload_to_matrix.repetition": "The Assembly Matrix already contains the same pattern. Upload skipped and blank pattern returned.", "chat.extendedae_plus.terminal.pos": "The crafting plan's corresponding provider is now highlighted at: %s, Dimension: %s (%s blocks away)", @@ -120,6 +126,7 @@ "config.extendedae_plus.option.entityTickerMultipliers": "Entity Ticker Extra Consumption Multipliers", "config.extendedae_plus.option.craftingPauseThreshold": "AE synthesis calculation pause check threshold", "config.extendedae_plus.option.prioritizeDiskEnergy": "Prioritize FE energy from disk (requires Applied Flux)", + "config.extendedae_plus.option.restrictCraftingPatternToMolecular": "Restrict automatic upload of synthetic templates to only molecular manipulators", "item.extendedae_plus.channel_card": "Channel Card", "item.extendedae_plus.channel_card.channel": "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 7d16255..1f4c60c 100644 --- a/src/main/resources/assets/extendedae_plus/lang/zh_cn.json +++ b/src/main/resources/assets/extendedae_plus/lang/zh_cn.json @@ -47,10 +47,14 @@ "extendedae_plus.upload_to_matrix": "上传到装配矩阵", "extendedae_plus.upload_to_matrix.success": "样板已上传到装配矩阵", + "extendedae_plus.upload_to_GTMatrix.success": "样板已上传到分子操纵者", "extendedae_plus.upload_to_matrix.fail_not_crafting": "仅支持上传合成样板,处理样板将被忽略", "extendedae_plus.upload_to_matrix.fail_no_matrix": "未在当前网络中找到已成型的装配矩阵", + "extendedae_plus.upload_to_matrix.fail_no_GTMatrix": "未在当前网络中找到已成型的分子操纵者", "extendedae_plus.upload_to_matrix.fail_full": "装配矩阵的样板仓已满或无法插入", + "extendedae_plus.upload_to_GTMatrix.fail_full": "分子操纵者的样板仓已满或无法插入", "extendedae_plus.upload_to_matrix.repetition": "装配矩阵已存在相同样板,已跳过上传并返还空白样板", + "extendedae_plus.upload_to_GTMatrix.repetition": "分子操纵者已存在相同样板,已跳过上传并返还空白样板", "chat.extendedae_plus.terminal.pos": "合成计划对应供应器现在突出显示在:%s,维度:%s (%s个方块外)", @@ -120,6 +124,7 @@ "config.extendedae_plus.option.entityTickerMultipliers": "实体加速器额外消耗倍率", "config.extendedae_plus.option.prioritizeDiskEnergy": "优先从磁盘提取FE能量(仅当Applied Flux模组存在时生效)", "config.extendedae_plus.option.craftingPauseThreshold": "AE合成计算暂停检查阈值", + "config.extendedae_plus.option.restrictCraftingPatternToMolecular": "限制合成样板自动上传仅进入分子操纵者", "item.extendedae_plus.channel_card": "频道卡", "item.extendedae_plus.channel_card.channel": "频率:%s", @@ -130,7 +135,7 @@ "item.extendedae_plus.channel_card.owner.player": "所有者:玩家 %s", "item.extendedae_plus.channel_card.owner.bound": "已绑定至:%s", "item.extendedae_plus.channel_card.owner.cleared": "已清除所有者绑定", - + "extendedae_plus.tooltip.owner": "所有者: %s", "extendedae_plus.tooltip.owner.unset": "所有者: 未设置", "extendedae_plus.tooltip.owner.public": "所有者: 公共", @@ -139,7 +144,7 @@ "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": "存储总线"