合并fix分支,抽离合成样板上传分子操纵工具类

This commit is contained in:
Savitor 2025-10-28 22:44:47 +08:00 committed by C-H716
parent 124c008700
commit 50d74cafb0
22 changed files with 684 additions and 13 deletions

View File

@ -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 {

View File

@ -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

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
libs/gtmthings-1.3.5.b.jar Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -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();

View File

@ -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<IPatternDetails, Integer> 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<List<IPatternDetails>> cir) {
if (patternSlotMap == null) return;
for (Map.Entry<IPatternDetails, Integer> 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<IPatternDetails, ?> 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<IPatternDetails, ?> detailsSlotMap, Object key) {
try {
// 如果是 ScaledProcessingPattern优先尝试其原始 pattern 对应的值
if (key instanceof ScaledProcessingPattern scaled) {
IPatternDetails base = scaled.getOriginal();
if (base != null) {
for (Map.Entry<IPatternDetails, ?> e : detailsSlotMap.entrySet()) {
if (Objects.equals(e.getKey(), base)) {
return e.getValue();
}
}
}
}
// 常规查找遍历 entrySet 避免再次调用 BiMap.get 导致递归重定向
for (Map.Entry<IPatternDetails, ?> e : detailsSlotMap.entrySet()) {
if (Objects.equals(e.getKey(), key)) {
return e.getValue();
}
}
return null;
} catch (Throwable t) {
for (Map.Entry<IPatternDetails, ?> e : detailsSlotMap.entrySet()) {
if (Objects.equals(e.getKey(), key)) {
return e.getValue();
}
}
return null;
}
}
}

View File

@ -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<IPatternDetails, Integer> 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<List<IPatternDetails>> cir) {
if (detailsSlotMap == null) return;
for (Map.Entry<IPatternDetails, Integer> 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<IPatternDetails, ?> 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<IPatternDetails, ?> detailsSlotMap, Object key) {
try {
// 如果是 ScaledProcessingPattern优先尝试其原始 pattern 对应的值
if (key instanceof ScaledProcessingPattern scaled) {
IPatternDetails base = scaled.getOriginal();
if (base != null) {
for (Map.Entry<IPatternDetails, ?> e : detailsSlotMap.entrySet()) {
if (Objects.equals(e.getKey(), base)) {
return e.getValue();
}
}
}
}
// 常规查找遍历 entrySet 避免再次调用 BiMap.get 导致递归重定向
for (Map.Entry<IPatternDetails, ?> e : detailsSlotMap.entrySet()) {
if (Objects.equals(e.getKey(), key)) {
return e.getValue();
}
}
return null;
} catch (Throwable t) {
for (Map.Entry<IPatternDetails, ?> e : detailsSlotMap.entrySet()) {
if (Objects.equals(e.getKey(), key)) {
return e.getValue();
}
}
return null;
}
}
}

View File

@ -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<ItemStack> 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);
}
}

View File

@ -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) {
}
}
}

View File

@ -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) {}
}
}

View File

@ -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();
}
}

View File

@ -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: 从菜单中获取网格实例
*/

View File

@ -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<String> 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);
}
}

View File

@ -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<InternalInventory> 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<InternalInventory> findAllGTMatrixPatternInventories(IGrid grid) {
List<InternalInventory> result = new ArrayList<>();
if (grid == null) return result;
try {
// 获取网络中所有 Pattern Tile
Set<MEMolecularAssemblerIOPartMachine> 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<InternalInventory> 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);
}
}
}
}

View File

@ -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",

View File

@ -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": "存储总线"