1.搭建gtl环境,其中resourcefullib不能换。

2.兼容147版本eae的高亮聊天文本提示
3.修复扩展样板管理终端UI按钮在eae147版本失效问题
This commit is contained in:
Savitor 2025-10-21 02:23:32 +08:00
parent bb6704d830
commit a3bd163cb6
14 changed files with 269 additions and 269 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,9 +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}"
modCompileOnly files('libs/gtlcore-1.2.2.3-fix6.jar')
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}"
@ -97,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}"
@ -106,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
@ -126,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 {

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

@ -67,10 +67,14 @@ public abstract class GuiExPatternTerminalMixin extends AEBaseScreen<AEBaseMenu>
@Unique
private boolean eap$debugLoggedOnce = false;
@Shadow(remap = false) private AETextField searchOutField;
@Shadow(remap = false) private AETextField searchInField;
@Shadow(remap = false) private Set<ItemStack> matchedStack;
@Shadow(remap = false) private Set<PatternContainerRecord> matchedProvider;
@Shadow(remap = false)
private AETextField searchOutField;
@Shadow(remap = false)
private AETextField searchInField;
@Shadow(remap = false)
private Set<ItemStack> matchedStack;
@Shadow(remap = false)
private Set<PatternContainerRecord> matchedProvider;
public GuiExPatternTerminalMixin(AEBaseMenu menu, Inventory playerInventory, Component title, ScreenStyle style) {
super(menu, playerInventory, title, style);
@ -137,13 +141,15 @@ public abstract class GuiExPatternTerminalMixin extends AEBaseScreen<AEBaseMenu>
private void eap$quickUploadPattern(int playerSlotIndex) {
if (this.minecraft.player != null) {
// 获取要上传的物品
ItemStack itemToUpload = this.minecraft.player.getInventory().getItem(playerSlotIndex);
ItemStack itemToUpload = this.minecraft.player.getInventory()
.getItem(playerSlotIndex);
if (!itemToUpload.isEmpty() && PatternDetailsHelper.isEncodedPattern(itemToUpload)) {
// 通过反射调用 ExtendedAE 的网络发送软依赖
try {
Class<?> EPPNetworkHandlerClass = Class.forName("com.glodblock.github.extendedae.network.EPPNetworkHandler");
Object handlerInstance = EPPNetworkHandlerClass.getField("INSTANCE").get(null);
Object handlerInstance = EPPNetworkHandlerClass.getField("INSTANCE")
.get(null);
Class<?> packetClass = Class.forName("com.glodblock.github.glodium.network.packet.CGenericPacket");
Constructor<?> constructor = packetClass.getConstructor(String.class, Object[].class);
@ -193,17 +199,23 @@ public abstract class GuiExPatternTerminalMixin extends AEBaseScreen<AEBaseMenu>
Object byGroup = byGroupField.get(this); // HashMultimap<PatternContainerGroup, PatternContainerRecord>
Object headerRow = rows.get(rowIndex);
var groupField = headerRow.getClass().getDeclaredField("group");
var groupField = headerRow.getClass()
.getDeclaredField("group");
groupField.setAccessible(true);
Object group = groupField.get(headerRow);
// 调用 byGroup.get(group)再取第一个元素
Collection<?> containers = (Collection<?>) byGroup.getClass().getMethod("get", Object.class).invoke(byGroup, group);
Collection<?> containers = (Collection<?>) byGroup.getClass()
.getMethod("get", Object.class)
.invoke(byGroup, group);
if (containers == null || containers.isEmpty()) {
return;
}
Object firstRecord = containers.iterator().next(); // PatternContainerRecord
long serverId = (long) firstRecord.getClass().getMethod("getServerId").invoke(firstRecord);
Object firstRecord = containers.iterator()
.next(); // PatternContainerRecord
long serverId = (long) firstRecord.getClass()
.getMethod("getServerId")
.invoke(firstRecord);
// 通过 infoMap 获取位置信息
var infoMapField = cls.getDeclaredField("infoMap");
@ -220,13 +232,28 @@ public abstract class GuiExPatternTerminalMixin extends AEBaseScreen<AEBaseMenu>
}
// PatternProviderInfo record: pos(), face(), playerWorld()
Object pos = info.getClass().getMethod("pos").invoke(info);
Object face = info.getClass().getMethod("face").invoke(info); // 可能为 null方块型供应器
Object playerWorld = info.getClass().getMethod("playerWorld").invoke(info);
Object pos = info.getClass()
.getMethod("pos")
.invoke(info);
Object face = info.getClass()
.getMethod("face")
.invoke(info); // 可能为 null方块型供应器
Object playerWorld;
try {
playerWorld = info.getClass()
.getMethod("playerWorld")
.invoke(info);
} catch (Throwable t) {
playerWorld = info.getClass()
.getMethod("world")
.invoke(info);
}
// 避免对 MC 类进行反射使用强制类型转换后直接调用方法 Forge 运行时重映射保证
long posLong = ((BlockPos) pos).asLong();
String dimStr = ((ResourceKey<Level>) playerWorld).location().toString();
String dimStr = ((ResourceKey<Level>) playerWorld).location()
.toString();
int faceOrd = -1;
if (face != null) {
faceOrd = ((Direction) face).ordinal();
@ -271,11 +298,14 @@ public abstract class GuiExPatternTerminalMixin extends AEBaseScreen<AEBaseMenu>
Method refreshMethod = null;
try {
// 先尝试在当前类中查找
refreshMethod = this.getClass().getDeclaredMethod("refreshList");
refreshMethod = this.getClass()
.getDeclaredMethod("refreshList");
} catch (NoSuchMethodException e1) {
// 如果当前类没有尝试在父类中查找
try {
refreshMethod = this.getClass().getSuperclass().getDeclaredMethod("refreshList");
refreshMethod = this.getClass()
.getSuperclass()
.getDeclaredMethod("refreshList");
} catch (NoSuchMethodException e2) {
throw e2;
}
@ -309,17 +339,21 @@ public abstract class GuiExPatternTerminalMixin extends AEBaseScreen<AEBaseMenu>
private void eap$onResize(Minecraft mc, int width, int height, CallbackInfo ci) {
try {
// 移除并清理按钮避免旧位置残留
this.eap$openUIButtons.values().forEach(this::removeWidget);
this.eap$openUIButtons.values()
.forEach(this::removeWidget);
this.eap$openUIButtons.clear();
// 重置一次滚动条避免可见行/偏移在缩放后与 UI 尺寸不一致
try {
Method resetScrollbarMethod = null;
try {
resetScrollbarMethod = this.getClass().getDeclaredMethod("resetScrollbar");
resetScrollbarMethod = this.getClass()
.getDeclaredMethod("resetScrollbar");
} catch (NoSuchMethodException e1) {
try {
resetScrollbarMethod = this.getClass().getSuperclass().getDeclaredMethod("resetScrollbar");
resetScrollbarMethod = this.getClass()
.getSuperclass()
.getDeclaredMethod("resetScrollbar");
} catch (NoSuchMethodException e2) {
resetScrollbarMethod = null;
}
@ -335,10 +369,13 @@ public abstract class GuiExPatternTerminalMixin extends AEBaseScreen<AEBaseMenu>
try {
Method refreshMethod = null;
try {
refreshMethod = this.getClass().getDeclaredMethod("refreshList");
refreshMethod = this.getClass()
.getDeclaredMethod("refreshList");
} catch (NoSuchMethodException e1) {
try {
refreshMethod = this.getClass().getSuperclass().getDeclaredMethod("refreshList");
refreshMethod = this.getClass()
.getSuperclass()
.getDeclaredMethod("refreshList");
} catch (NoSuchMethodException e2) {
refreshMethod = null;
}
@ -359,7 +396,8 @@ public abstract class GuiExPatternTerminalMixin extends AEBaseScreen<AEBaseMenu>
@Inject(method = "init", at = @At("TAIL"), remap = false, require = 0)
private void eap$onInit(CallbackInfo ci) {
// 清理旧的打开UI按钮
this.eap$openUIButtons.values().forEach(this::removeWidget);
this.eap$openUIButtons.values()
.forEach(this::removeWidget);
this.eap$openUIButtons.clear();
}
@ -372,7 +410,8 @@ public abstract class GuiExPatternTerminalMixin extends AEBaseScreen<AEBaseMenu>
)));
}
// 清理旧的打开UI按钮
this.eap$openUIButtons.values().forEach(this::removeWidget);
this.eap$openUIButtons.values()
.forEach(this::removeWidget);
this.eap$openUIButtons.clear();
}
@ -386,11 +425,14 @@ public abstract class GuiExPatternTerminalMixin extends AEBaseScreen<AEBaseMenu>
java.lang.reflect.Field rowsField = null;
try {
// 先尝试在当前类中查找
rowsField = this.getClass().getDeclaredField("rows");
rowsField = this.getClass()
.getDeclaredField("rows");
} catch (NoSuchFieldException e1) {
// 如果当前类没有尝试在父类中查找
try {
rowsField = this.getClass().getSuperclass().getDeclaredField("rows");
rowsField = this.getClass()
.getSuperclass()
.getDeclaredField("rows");
} catch (NoSuchFieldException e2) {
throw e2;
}
@ -402,11 +444,14 @@ public abstract class GuiExPatternTerminalMixin extends AEBaseScreen<AEBaseMenu>
java.lang.reflect.Field highlightBtnsField = null;
try {
// 先尝试在当前类中查找
highlightBtnsField = this.getClass().getDeclaredField("highlightBtns");
highlightBtnsField = this.getClass()
.getDeclaredField("highlightBtns");
} catch (NoSuchFieldException e1) {
// 如果当前类没有尝试在父类中查找
try {
highlightBtnsField = this.getClass().getSuperclass().getDeclaredField("highlightBtns");
highlightBtnsField = this.getClass()
.getSuperclass()
.getDeclaredField("highlightBtns");
} catch (NoSuchFieldException e2) {
throw e2;
}
@ -422,7 +467,8 @@ public abstract class GuiExPatternTerminalMixin extends AEBaseScreen<AEBaseMenu>
// 移除所有SlotsRow只保留GroupHeaderRow同时重新映射高亮按钮索引
for (int i = 0; i < rows.size(); i++) {
Object row = rows.get(i);
String className = row.getClass().getSimpleName();
String className = row.getClass()
.getSimpleName();
if (className.equals("GroupHeaderRow")) {
// 保留GroupHeaderRow并重新映射对应的高亮按钮
@ -459,11 +505,14 @@ public abstract class GuiExPatternTerminalMixin extends AEBaseScreen<AEBaseMenu>
Method resetScrollbarMethod = null;
try {
// 先尝试在当前类中查找
resetScrollbarMethod = this.getClass().getDeclaredMethod("resetScrollbar");
resetScrollbarMethod = this.getClass()
.getDeclaredMethod("resetScrollbar");
} catch (NoSuchMethodException e1) {
// 如果当前类没有尝试在父类中查找
try {
resetScrollbarMethod = this.getClass().getSuperclass().getDeclaredMethod("resetScrollbar");
resetScrollbarMethod = this.getClass()
.getSuperclass()
.getDeclaredMethod("resetScrollbar");
} catch (NoSuchMethodException e2) {
throw e2;
}
@ -485,7 +534,8 @@ public abstract class GuiExPatternTerminalMixin extends AEBaseScreen<AEBaseMenu>
// 使用 Accessor 获取必要的字段避免反射失败
GuiExPatternTerminalAccessor acc = (GuiExPatternTerminalAccessor) this;
java.util.ArrayList<?> rows = acc.getRows();
int currentScroll = acc.getScrollbar().getCurrentScroll();
int currentScroll = acc.getScrollbar()
.getCurrentScroll();
// 直接引用目标类以获取其静态常量
Class<?> cls = GuiExPatternTerminal.class;
@ -511,7 +561,9 @@ public abstract class GuiExPatternTerminalMixin extends AEBaseScreen<AEBaseMenu>
continue;
}
Object row = rows.get(rowIndex);
if (!row.getClass().getSimpleName().equals("GroupHeaderRow")) {
if (!row.getClass()
.getSimpleName()
.equals("GroupHeaderRow")) {
continue;
}
@ -522,8 +574,10 @@ public abstract class GuiExPatternTerminalMixin extends AEBaseScreen<AEBaseMenu>
Button btn = eap$openUIButtons.get(rowIndex);
if (btn == null) {
btn = Button.builder(Component.literal("UI"), (b) -> {
eap$tryOpenProviderUI(rowIndex);
}).size(14, 12).build();
eap$tryOpenProviderUI(rowIndex);
})
.size(14, 12)
.build();
btn.setTooltip(Tooltip.create(Component.literal("打开该供应器目标容器的界面")));
eap$openUIButtons.put(rowIndex, btn);
this.addRenderableWidget(btn);
@ -538,8 +592,10 @@ public abstract class GuiExPatternTerminalMixin extends AEBaseScreen<AEBaseMenu>
// 原有的搜索高亮逻辑
// 仅当任一搜索框非空时绘制叠加层与原版行为保持一致
boolean searchActive = (this.searchOutField != null && !this.searchOutField.getValue().isEmpty())
|| (this.searchInField != null && !this.searchInField.getValue().isEmpty());
boolean searchActive = (this.searchOutField != null && !this.searchOutField.getValue()
.isEmpty())
|| (this.searchInField != null && !this.searchInField.getValue()
.isEmpty());
if (!searchActive) {
return;
}

View File

@ -1,207 +0,0 @@
package com.extendedae_plus.network;
import appeng.api.crafting.IPatternDetails;
import appeng.api.networking.IGrid;
import appeng.api.networking.security.IActionHost;
import appeng.api.stacks.AEKey;
import appeng.helpers.patternprovider.PatternProviderLogic;
import appeng.helpers.patternprovider.PatternProviderLogicHost;
import appeng.me.service.CraftingService;
import appeng.menu.AEBaseMenu;
import appeng.menu.locator.MenuLocators;
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.provider.SetProviderPageS2CPacket;
import com.extendedae_plus.util.PatternProviderDataUtil;
import com.glodblock.github.extendedae.util.FCClientUtil;
import com.glodblock.github.glodium.util.GlodUtil;
import com.gregtechceu.gtceu.api.gui.factory.MachineUIFactory;
import com.gregtechceu.gtceu.api.machine.MetaMachine;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.network.chat.Component;
import net.minecraft.resources.ResourceKey;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.level.Level;
import net.minecraft.world.phys.AABB;
import net.minecraftforge.network.NetworkDirection;
import net.minecraftforge.network.NetworkEvent;
import java.util.Collection;
import java.util.Objects;
import java.util.function.Supplier;
import static com.glodblock.github.extendedae.client.render.EAEHighlightHandler.highlight;
/**
* 客户端从 CraftingCPUScreen 发送鼠标下条目对应的 AEKey
* 服务端在当前打开的 CraftingCPUMenu 所属网络中定位匹配该 AEKey 的样板供应器
* 打开该供应器自身的 UI不是目标机器的 UI
*/
public class CraftingMonitorOpenProviderC2SPacket {
private final AEKey what;
public CraftingMonitorOpenProviderC2SPacket(AEKey what) {
this.what = what;
}
public static void encode(CraftingMonitorOpenProviderC2SPacket msg, FriendlyByteBuf buf) {
AEKey.writeKey(buf, msg.what);
}
public static CraftingMonitorOpenProviderC2SPacket decode(FriendlyByteBuf buf) {
AEKey key = AEKey.readKey(buf);
return new CraftingMonitorOpenProviderC2SPacket(key);
}
public static void handle(CraftingMonitorOpenProviderC2SPacket msg, Supplier<NetworkEvent.Context> ctx) {
NetworkEvent.Context context = ctx.get();
context.enqueueWork(() -> {
ServerPlayer player = context.getSender();
if (player == null) return;
// 必须在 CraftingCPU 界面内
if (!(player.containerMenu instanceof CraftingCPUMenu menu)) {
return;
}
// 通过菜单的 target可能是 BlockEntity/Part/ItemHost IActionHost 获取 Grid
IGrid grid = null;
Object target = ((AEBaseMenu) menu).getTarget();
if (target instanceof IActionHost host && host.getActionableNode() != null) {
grid = host.getActionableNode().getGrid();
}
if (grid == null) {
return;
}
var cs = grid.getCraftingService();
if (!(cs instanceof CraftingService craftingService)) {
return;
}
// 1) 根据 AEKey 找到可能的样板pattern
Collection<IPatternDetails> patterns = craftingService.getCraftingFor(msg.what);
if (patterns.isEmpty()) {
return;
}
// 2) 遍历提供该样板的 Provider定位 PatternProviderLogic
for (var pattern : patterns) {
var providers = craftingService.getProviders(pattern);
for (var provider : providers) {
if (provider instanceof PatternProviderLogic ppl) {
// accessor 获取 host
PatternProviderLogicHost host = ((PatternProviderLogicAccessor) ppl).eap$host();
if (host == null) continue;
var pbe = host.getBlockEntity();
if (pbe == null) continue;
// 跳过未连接到网格或不活跃的 provider使用 util 判断并传入当前 grid
if (!PatternProviderDataUtil.isProviderAvailable(ppl, grid)) continue;
// 直接打开供应器自身的 UI调用 Host 默认方法
try {
// 部件与方块实体分别选择定位器并打开界面
if (host instanceof AEBasePart part) {
host.openMenu(player, MenuLocators.forPart(part));
highlightWithMessage(pbe.getBlockPos(), part.getSide(), Objects.requireNonNull(pbe.getLevel()).dimension(), 1.0, player);
} else {
host.openMenu(player, MenuLocators.forBlockEntity(pbe));
highlightWithMessage(pbe.getBlockPos(), null, Objects.requireNonNull(pbe.getLevel()).dimension(), 1.0, player);
}
// 先在该 provider 中定位 pattern 的槽位索引以便计算页码尽量早退出按槽位逐个解码
int foundSlot = PatternProviderDataUtil.findSlotForPattern(ppl, pattern.getDefinition());
if (foundSlot >= 0) {
int pageId = foundSlot / 36;
if (pageId > 0) {
// 发送 S2C 包通知客户端切换到指定页客户端会写入 mixin 字段并重排槽位
ModNetwork.CHANNEL.sendTo(new SetProviderPageS2CPacket(pageId), player.connection.connection, NetworkDirection.PLAY_TO_CLIENT);
}
}
// 最后发送高亮包保证界面已打开
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);
}
return;
} catch (Exception ignored) {
}
} else if (provider instanceof com.gregtechceu.gtceu.integration.ae2.machine.MEPatternBufferPartMachine machine) {
// 处理 MEPatternBufferPartMachine打开其界面并高亮位置尽量反射获取槽位页码
try {
BlockPos pos = machine.getPos();
Level level = machine.getLevel();
if (pos == null || level == null) continue;
if (!level.isClientSide) { // 确保在服务器端执行
MachineUIFactory.INSTANCE.openUI(MetaMachine.getMachine(level, pos), player);
}
// 最后发送高亮包保证界面已打开
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);
}
highlightWithMessage(pos, null, level.dimension(), 1.0, player);
} catch (Exception ignored) {
}
} else if (provider instanceof org.gtlcore.gtlcore.common.machine.multiblock.part.ae.MEPatternBufferPartMachine machine) {
// 处理 MEPatternBufferPartMachine打开其界面并高亮位置尽量反射获取槽位页码
try {
BlockPos pos = machine.getPos();
Level level = machine.getLevel();
if (pos == null || level == null) continue;
if (!level.isClientSide) { // 确保在服务器端执行
MachineUIFactory.INSTANCE.openUI(MetaMachine.getMachine(level, pos), player);
}
// 最后发送高亮包保证界面已打开
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);
}
highlightWithMessage(pos, null, level.dimension(), 1.0, player);
} catch (Exception ignored) {
}
}
}
}
});
context.setPacketHandled(true);
}
private static void highlightWithMessage(BlockPos pos, Direction face, ResourceKey<Level> dim, double multiplier, Player player) {
if (pos == null || dim == null) {
return;
}
long endTime = System.currentTimeMillis() + (long) (6000 * GlodUtil.clamp(multiplier, 1, 30));
if (face == null) {
highlight(pos, dim, endTime);
} else {
var origin = new AABB(2 / 16D, 2 / 16D, 0, 14 / 16D, 14 / 16D, 2 / 16D).move(pos);
var center = new AABB(pos).getCenter();
switch (face) {
case WEST -> origin = FCClientUtil.rotor(origin, center, Direction.Axis.Y, (float) (Math.PI / 2));
case SOUTH -> origin = FCClientUtil.rotor(origin, center, Direction.Axis.Y, (float) Math.PI);
case EAST -> origin = FCClientUtil.rotor(origin, center, Direction.Axis.Y, (float) (-Math.PI / 2));
case UP -> origin = FCClientUtil.rotor(origin, center, Direction.Axis.X, (float) (-Math.PI / 2));
case DOWN -> origin = FCClientUtil.rotor(origin, center, Direction.Axis.X, (float) (Math.PI / 2));
}
highlight(pos, face, dim, endTime, origin);
}
if (player != null) {
player.displayClientMessage(Component.translatable("chat.ex_pattern_access_terminal.pos", pos.toShortString(), dim.location().getPath()), false);
}
}
}

View File

@ -14,11 +14,18 @@ import com.extendedae_plus.mixin.ae2.accessor.PatternProviderLogicAccessor;
import com.extendedae_plus.network.SetBlockHighlightS2CPacket;
import com.extendedae_plus.network.SetPatternHighlightS2CPacket;
import com.extendedae_plus.network.provider.SetProviderPageS2CPacket;
import com.extendedae_plus.util.CompareModVersionUtil;
import com.extendedae_plus.util.PatternProviderDataUtil;
import com.glodblock.github.extendedae.client.render.EAEHighlightHandler;
import com.glodblock.github.extendedae.util.MessageUtil;
import com.glodblock.github.glodium.util.GlodUtil;
import com.gregtechceu.gtceu.api.gui.factory.MachineUIFactory;
import com.gregtechceu.gtceu.api.machine.MetaMachine;
import net.minecraft.core.BlockPos;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.network.chat.Component;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.level.Level;
import net.minecraftforge.network.NetworkDirection;
import net.minecraftforge.network.NetworkEvent;
@ -70,11 +77,22 @@ public class CraftingMonitorOpenProviderC2SPacket {
// 遍历所有样板找到第一个可用 Provider 并打开 UI
for (var pattern : patterns) {
var provider = PatternLocator.findValidProvider(craftingService, pattern, grid);
if (provider == null) continue;
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);
} catch (Exception ignored) {}
} catch (Exception ignored) {
}
}
});
context.setPacketHandled(true);
@ -82,21 +100,73 @@ 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: 从菜单中获取网格实例
*/
public static final class GridHelper {
private GridHelper() {}
private GridHelper() {
}
/**
* 获取菜单对应的 Grid
*
* @param menu 当前 AEBaseMenu
* @return Grid null
*/
public static IGrid getGridFromMenu(appeng.menu.AEBaseMenu menu) {
Object target = menu.getTarget();
if (target instanceof IActionHost host && host.getActionableNode() != null) {
return host.getActionableNode().getGrid();
return host.getActionableNode()
.getGrid();
}
return null;
}
@ -106,13 +176,15 @@ public class CraftingMonitorOpenProviderC2SPacket {
* PatternLocator: 根据样板定位可用的 Provider
*/
public static final class PatternLocator {
private PatternLocator() {}
private PatternLocator() {
}
/**
* 查找提供指定样板的可用 Provider
* @param cs CraftingService
*
* @param cs CraftingService
* @param pattern 样板
* @param grid 当前 Grid
* @param grid 当前 Grid
* @return 第一个可用的 PatternProviderLogic null
*/
public static PatternProviderLogic findValidProvider(CraftingService cs, IPatternDetails pattern, IGrid grid) {
@ -133,7 +205,8 @@ public class CraftingMonitorOpenProviderC2SPacket {
* ProviderUIHelper: 打开 Provider UI 并发送客户端反馈
*/
public static final class ProviderUIHelper {
private ProviderUIHelper() {}
private ProviderUIHelper() {
}
/**
* 打开 Provider UI
@ -143,8 +216,8 @@ public class CraftingMonitorOpenProviderC2SPacket {
* 4. 发送 Pattern 输出高亮包
*
* @param provider PatternProviderLogic 实例
* @param pattern 样板
* @param player 玩家
* @param pattern 样板
* @param player 玩家
*/
public static void openProviderUI(PatternProviderLogic provider, IPatternDetails pattern, ServerPlayer player) {
var host = ((PatternProviderLogicAccessor) provider).eap$host();
@ -160,7 +233,9 @@ public class CraftingMonitorOpenProviderC2SPacket {
new SetBlockHighlightS2CPacket(
pbe.getBlockPos(),
isPart ? ((AEBasePart) host).getSide() : null,
pbe.getLevel().dimension().location(),
pbe.getLevel()
.dimension()
.location(),
(long) (6000 * GlodUtil.clamp(1.0, 1, 30))
),
player.connection.connection,
@ -168,15 +243,30 @@ public class CraftingMonitorOpenProviderC2SPacket {
);
// 聊天提示
player.displayClientMessage(
Component.translatable(
"chat.ex_pattern_access_terminal.pos",
pbe.getBlockPos().toShortString(),
pbe.getLevel().dimension().location().getPath()
),
false
);
if (CompareModVersionUtil.compareTo("expatternprovider", "1.4.7")) {
player.displayClientMessage(
MessageUtil.createEnhancedHighlightMessage(
player,
pbe.getBlockPos(),
pbe.getLevel()
.dimension(),
"chat.ex_pattern_access_terminal.pos"),
false
);
} else {
player.displayClientMessage(
Component.translatable(
"chat.ex_pattern_access_terminal.pos",
pbe.getBlockPos()
.toShortString(),
pbe.getLevel()
.dimension()
.location()
.getPath()
),
false
);
}
// 页码同步
int slot = PatternProviderDataUtil.findSlotForPattern(provider, pattern.getDefinition());
if (slot >= 0) {

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