使远程打开UI支持mek的menu, 删除了自动上传非处理样板的逻辑

This commit is contained in:
XianYuFish 2025-09-28 19:27:10 +08:00 committed by XianYuFish001
parent e80ed5a55c
commit 65389c4180
15 changed files with 71 additions and 129 deletions

View File

@ -2,7 +2,7 @@
_这个分支是粗浅的EMI适配,技术力不足,请见谅_
### 相较于主分支(1.4.0)的改动
- 删除了JEI的适配
- 暂无 _(大概?)_
### 增加/更改的QoL功能
- 在EMI配方/配方树界面快速拉起样板编写&上传

View File

@ -33,14 +33,6 @@ public class ExtendedAEPlus {
modEventBus.addListener(ModNetwork::registerPayloadHandlers);
modEventBus.addListener(ModCapabilities::onRegisterCapabilities);
/*
TODO
将这literal地狱烧成灰(bushi
反正就是引入dataGeneration, 多写点translatable
顺便不小心露出我无敌的component工具类(
算了算了,我真不敢动aaa
*/
ModBlocks.BLOCKS.register(modEventBus);
ModItems.ITEMS.register(modEventBus);
ModBlockEntities.BLOCK_ENTITY_TYPES.register(modEventBus);

View File

@ -7,7 +7,9 @@ public class NewIcon {
@SuppressWarnings("all")
// 贴图当前存放于 assets/extendedae_plus/textures/gui/nicons.png
// MODID (extendedaeplus) 不同因此这里直接指定贴图所在命名空间
private static final ResourceLocation TEXTURE = ResourceLocation.fromNamespaceAndPath("extendedae_plus", "textures/gui/nicons.png");
// 真不同吗🧐
private static final ResourceLocation TEXTURE =
ResourceLocation.fromNamespaceAndPath(ExtendedAEPlus.MODID, "textures/gui/nicons.png");

View File

@ -15,7 +15,7 @@ public class ModNetwork {
registrar.playToClient(SetPatternHighlightS2CPacket.TYPE, SetPatternHighlightS2CPacket.STREAM_CODEC, SetPatternHighlightS2CPacket::handle);
registrar.playToClient(AdvancedBlockingSyncS2CPacket.TYPE, AdvancedBlockingSyncS2CPacket.STREAM_CODEC, AdvancedBlockingSyncS2CPacket::handle);
registrar.playToClient(ProvidersListS2CPacket.TYPE, ProvidersListS2CPacket.STREAM_CODEC, ProvidersListS2CPacket::handle);
registrar.playToServer(RequestProvidersListC2SPacket.TYPE, RequestProvidersListC2SPacket.STREAM_CODEC, RequestProvidersListC2SPacket::handle);
registrar.playToServer(RequestUploadingC2SPacket.TYPE, RequestUploadingC2SPacket.STREAM_CODEC, RequestUploadingC2SPacket::handle);
registrar.playToClient(SetProviderPageS2CPacket.TYPE, SetProviderPageS2CPacket.STREAM_CODEC, SetProviderPageS2CPacket::handle);
registrar.playToServer(GlobalToggleProviderModesC2SPacket.TYPE, GlobalToggleProviderModesC2SPacket.STREAM_CODEC, GlobalToggleProviderModesC2SPacket::handle);
registrar.playToServer(CraftingMonitorJumpC2SPacket.TYPE, CraftingMonitorJumpC2SPacket.STREAM_CODEC, CraftingMonitorJumpC2SPacket::handle);

View File

@ -1,20 +0,0 @@
package com.extendedae_plus.mixin;
import net.minecraft.world.inventory.AbstractContainerMenu;
import org.spongepowered.asm.mixin.Mixin;
@Mixin(AbstractContainerMenu.class)
public abstract class MixinAbstractContainerMenu {
// @Inject(method = "stillValid(Lnet/minecraft/world/inventory/ContainerLevelAccess;Lnet/minecraft/world/entity/player/Player;Lnet/minecraft/world/level/block/Block;)Z",
// at = @At("HEAD"), cancellable = true)
// private static void eaep$cancelCheck(ContainerLevelAccess access, Player player, Block targetBlock, CallbackInfoReturnable<Boolean> cir) {
// cir.setReturnValue(access.evaluate(((level, pos) -> {
// for (Direction direction : Direction.values()) {
// if (level.getBlockState(pos.relative(direction)).getBlock()
// instanceof PatternProviderBlock)
// return true;
// }
// return level.getBlockState(pos).is(targetBlock) && player.canInteractWithBlock(pos, 4.0F);
// }), true));
// }
}

View File

@ -10,7 +10,7 @@ import com.extendedae_plus.config.ModConfig;
import com.extendedae_plus.mixin.accessor.AbstractContainerScreenAccessor;
import com.extendedae_plus.mixin.accessor.ScreenAccessor;
import com.extendedae_plus.mixin.ae2.accessor.AEBaseScreenAccessor;
import com.extendedae_plus.network.RequestProvidersListC2SPacket;
import com.extendedae_plus.network.RequestUploadingC2SPacket;
import net.minecraft.client.gui.GuiGraphics;
import net.minecraft.client.gui.components.Tooltip;
import net.minecraft.client.renderer.Rect2i;
@ -41,7 +41,7 @@ public abstract class PatternEncodingTermScreenMixin {
// 复用已存在的按钮实例避免重复创建
if (eap$uploadBtn == null) {
eap$uploadBtn = new IconButton(btn -> PacketDistributor
.sendToServer(RequestProvidersListC2SPacket.INSTANCE)) {
.sendToServer(RequestUploadingC2SPacket.INSTANCE)) {
private final float eap$scale = 0.75f; // 12x12
@Override

View File

@ -1,20 +1,14 @@
package com.extendedae_plus.mixin.ae2.menu;
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.util.ExtendedAEPatternUploadUtil;
import com.glodblock.github.glodium.network.packet.sync.ActionMap;
import com.glodblock.github.glodium.network.packet.sync.IActionHolder;
import net.minecraft.core.component.DataComponents;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.component.CustomData;
import org.jetbrains.annotations.NotNull;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
@ -34,30 +28,6 @@ public abstract class ContainerPatternEncodingTermMenuMixin implements IActionHo
@Unique
private Player epp$player;
@Shadow(remap = false)
private RestrictedInputSlot encodedPatternSlot;
@Unique
private void eap$scheduleUploadWithRetry(ServerPlayer sp, PatternEncodingTermMenu menu, int attemptsLeft) {
sp.server.execute(() -> {
try {
if (attemptsLeft < 0) {
return;
}
var stack = this.encodedPatternSlot != null ? this.encodedPatternSlot.getItem() : net.minecraft.world.item.ItemStack.EMPTY;
if (stack != null && !stack.isEmpty() && PatternDetailsHelper.isEncodedPattern(stack)) {
ExtendedAEPatternUploadUtil.uploadFromEncodingMenuToMatrix(sp, menu);
} else {
// 槽位可能尚未同步到位继续下一 tick 重试
if (attemptsLeft > 0) {
eap$scheduleUploadWithRetry(sp, menu, attemptsLeft - 1);
}
}
} catch (Throwable ignored) {
}
});
}
// AE2 终端主构造PatternEncodingTermMenu(int id, Inventory ip, IPatternTerminalMenuHost host)
@Inject(method = "<init>(ILnet/minecraft/world/entity/player/Inventory;Lappeng/helpers/IPatternTerminalMenuHost;)V", at = @At("TAIL"), remap = false)
private void eap$ctorA(int id, net.minecraft.world.entity.player.Inventory ip, appeng.helpers.IPatternTerminalMenuHost host, CallbackInfo ci) {
@ -78,40 +48,6 @@ public abstract class ContainerPatternEncodingTermMenuMixin implements IActionHo
return this.eap$actions;
}
// 服务器端 encode() 执行完毕后如果已编码槽位存在样板且当前为合成模式则上传到装配矩阵
@Inject(method = "encode", at = @At("TAIL"), remap = false)
private void eap$serverUploadAfterEncode(CallbackInfo ci) {
try {
if (!(this.epp$player instanceof ServerPlayer sp)) {
return; // 仅服务器执行
}
var menu = (PatternEncodingTermMenu) (Object) this;
if (menu.getMode() != EncodingMode.CRAFTING
&& menu.getMode() != EncodingMode.SMITHING_TABLE
&& menu.getMode() != EncodingMode.STONECUTTING) {
return; // 只处理合成/锻造台/切石机样板
}
if (this.encodedPatternSlot == null) {
return;
}
var stack = this.encodedPatternSlot.getItem();
if (stack == null || stack.isEmpty()) {
return; // 没有编码样板
}
if (!PatternDetailsHelper.isEncodedPattern(stack)) {
return; // 不是编码样板
}
// 为避免与 AE2 后续同步竞争切到下一 tick 执行
sp.server.execute(() -> {
try {
ExtendedAEPatternUploadUtil.uploadFromEncodingMenuToMatrix(sp, menu);
} catch (Throwable ignored) {
}
});
} catch (Throwable ignored) {
}
}
// 服务器端在构造样板返回前插入编码玩家的名称
@Inject(method = "encodePattern", at = @At("TAIL"), remap = false, cancellable = true)
private void eap$writeEncodePlayerToPattern(CallbackInfoReturnable<ItemStack> cir) {

View File

@ -56,7 +56,7 @@ public abstract class MEStorageMenuMixin {
placeholder = serverValue;
}
// 使用辅助方法统一进行受检的泛型转换后再注册
eap$registerSettingCompat(client, setting, placeholder);
eap$registerSettingCompact(client, setting, placeholder);
} catch (Throwable ignore) {
// 防御不让异常影响主流程
}
@ -88,7 +88,7 @@ public abstract class MEStorageMenuMixin {
@Unique
@SuppressWarnings({"unchecked", "rawtypes"})
private static <T extends Enum<T>> void eap$registerSettingCompat(
private static <T extends Enum<T>> void eap$registerSettingCompact(
IConfigManager client, Setting<?> setting, Object value) {
// 前置校验仅处理枚举类型的设置值
if (!(value instanceof Enum<?>)) {

View File

@ -1,5 +1,6 @@
package com.extendedae_plus.mixin.ae2.menu;
import appeng.api.crafting.PatternDetailsHelper;
import appeng.api.inventories.InternalInventory;
import appeng.api.networking.energy.IEnergySource;
import appeng.api.stacks.AEItemKey;
@ -10,13 +11,16 @@ import appeng.core.definitions.AEItems;
import appeng.helpers.IPatternTerminalMenuHost;
import appeng.menu.me.items.PatternEncodingTermMenu;
import appeng.menu.slot.RestrictedInputSlot;
import appeng.parts.encoding.EncodingMode;
import com.extendedae_plus.client.PatternEncodingTermMenuMixinHelper;
import com.extendedae_plus.config.ModConfig;
import com.extendedae_plus.mixin.ae2.accessor.MEStorageMenuAccessor;
import com.extendedae_plus.network.C2SPacketEncodeFinished;
import com.extendedae_plus.util.ExtendedAEPatternUploadUtil;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.entity.player.Inventory;
import net.minecraft.world.inventory.MenuType;
import net.minecraft.world.item.ItemStack;
import net.neoforged.neoforge.network.PacketDistributor;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
@ -26,6 +30,8 @@ import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import java.util.Objects;
@Mixin(PatternEncodingTermMenu.class)
public abstract class PatternEncodingTermMenuMixin implements PatternEncodingTermMenuMixinHelper {
// 防止重复执行
@ -33,6 +39,9 @@ public abstract class PatternEncodingTermMenuMixin implements PatternEncodingTer
private boolean eap$blankAutoFilled = false;
@Shadow @Final
private RestrictedInputSlot blankPatternSlot;
@Shadow
@Final
private RestrictedInputSlot encodedPatternSlot;
@Unique
public boolean eaep$isCtrlPressed = false;
@ -163,9 +172,18 @@ public abstract class PatternEncodingTermMenuMixin implements PatternEncodingTer
@Inject(method = "encode", at = @At("TAIL"))
private void eaep$onEncode(CallbackInfo ci) {
if (ModConfig.INDEPENDENT_UPLOADING_BUTTON.getAsBoolean()) return;
var self = (PatternEncodingTermMenu) (Object) this;
if (self.isClientSide()) return;
if (!eaep$isCtrlPressed) return;
eaep$isCtrlPressed = false;
var self = (PatternEncodingTermMenu) (Object) this;
PacketDistributor.sendToPlayer((ServerPlayer) self.getPlayer(), C2SPacketEncodeFinished.INSTANCE);
ItemStack pattern = this.encodedPatternSlot.getItem();
if (pattern == null || !PatternDetailsHelper.isEncodedPattern(pattern)) return;
Objects.requireNonNull(self.getPlayer().getServer()).execute(() -> {
try {
if (self.getMode() == EncodingMode.PROCESSING)
PacketDistributor.sendToPlayer((ServerPlayer) self.getPlayer(), C2SPacketEncodeFinished.INSTANCE);
else ExtendedAEPatternUploadUtil.uploadFromEncodingMenuToMatrix((ServerPlayer) self.getPlayer(), self);
} catch (Throwable ignored) {}
});
}
}

View File

@ -4,7 +4,7 @@ import appeng.integration.modules.emi.EmiStackHelper;
import appeng.menu.me.items.PatternEncodingTermMenu;
import com.extendedae_plus.integration.RecipeViewer.emi.HandlerBoMRecipes;
import com.extendedae_plus.integration.RecipeViewer.emi.PatternFillingHelper;
import com.extendedae_plus.network.RequestProvidersListC2SPacket;
import com.extendedae_plus.network.RequestUploadingC2SPacket;
import com.extendedae_plus.util.ExtendedAEPatternUploadUtil;
import dev.emi.emi.api.stack.EmiStack;
import dev.emi.emi.bom.BoM;
@ -87,7 +87,7 @@ public abstract class MixinBoMScreen {
ExtendedAEPatternUploadUtil.addLastProcessingNameList(
node.recipe.getId().toString().split("/")[0]);
PacketDistributor.sendToServer(RequestProvidersListC2SPacket.INSTANCE);
PacketDistributor.sendToServer(RequestUploadingC2SPacket.INSTANCE);
}
Minecraft.getInstance().player.playSound(SoundEvents.UI_BUTTON_CLICK.value());
}

View File

@ -22,6 +22,6 @@ public record C2SPacketEncodeFinished() implements CustomPacketPayload {
public static final StreamCodec<RegistryFriendlyByteBuf, C2SPacketEncodeFinished> STREAM_CODEC = StreamCodec.unit(INSTANCE);
public static void handle(final C2SPacketEncodeFinished packet, final IPayloadContext context) {
if (context.player().level().isClientSide) PacketDistributor.sendToServer(RequestProvidersListC2SPacket.INSTANCE);
if (context.player().level().isClientSide) PacketDistributor.sendToServer(RequestUploadingC2SPacket.INSTANCE);
}
}

View File

@ -28,6 +28,8 @@ import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.Vec3;
import net.neoforged.neoforge.network.handling.IPayloadContext;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
@ -87,6 +89,7 @@ public record CraftingMonitorJumpC2SPacket(AEKey what) implements CustomPacketPa
List<Direction> delayedBlocks = new ArrayList<>();
// 尝试对邻居打开 GUI优先通过 MenuProvider
// 这功能真是太有效了, 是个大点的mod就不用menuProvider😅
for (Direction dir : host.getTargets()) {
BlockPos targetPos = pbe.getBlockPos().relative(dir);
@ -104,14 +107,25 @@ public record CraftingMonitorJumpC2SPacket(AEKey what) implements CustomPacketPa
}
// awc这AE怎么这么坏啊😭😭😭
if (!(tbe instanceof AEBaseBlockEntity)) continue;
switch (tbe) {
case InterfaceBlockEntity ignored -> delayedBlocks.addFirst(dir);
case PatternProviderBlockEntity ignored -> delayedBlocks.addLast(dir);
case CableBusBlockEntity bus when bus.getCableBus().getPart(dir.getOpposite()) != null ->
delayedBlocks.addFirst(dir);
default -> tState.useWithoutItem(level, player,
new BlockHitResult(player.position(), dir.getOpposite(), targetPos, false));
if (tbe instanceof AEBaseBlockEntity) {
switch (tbe) {
case InterfaceBlockEntity ignored -> delayedBlocks.addFirst(dir);
case PatternProviderBlockEntity ignored -> delayedBlocks.addLast(dir);
case CableBusBlockEntity bus when bus.getCableBus().getPart(dir.getOpposite()) != null ->
delayedBlocks.addFirst(dir);
default -> tState.useWithoutItem(level, player,
new BlockHitResult(player.position(), dir.getOpposite(), targetPos, false));
}
}
if (tbe == null) continue;
String blockEntityClassName = tbe.getClass().getName().toLowerCase();
if (blockEntityClassName.contains("mekanism") && blockEntityClassName.contains("tile")) {
try {
Method methodOpenGui = tbe.getClass().getMethod("openGui", Player.class);
methodOpenGui.invoke(tbe, player);
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException ignored) {}
return;
}
}
if (delayedBlocks.isEmpty()) return;

View File

@ -3,6 +3,7 @@ package com.extendedae_plus.network;
import appeng.helpers.patternprovider.PatternContainer;
import appeng.menu.implementations.PatternAccessTermMenu;
import appeng.menu.me.items.PatternEncodingTermMenu;
import appeng.parts.encoding.EncodingMode;
import com.extendedae_plus.ExtendedAEPlus;
import com.extendedae_plus.util.ExtendedAEPatternUploadUtil;
import net.minecraft.network.FriendlyByteBuf;
@ -18,27 +19,34 @@ import java.util.List;
/**
* C2S: 请求当前终端可见的样板供应器列表用于弹窗选择
*/
public class RequestProvidersListC2SPacket implements CustomPacketPayload {
public static final Type<RequestProvidersListC2SPacket> TYPE = new Type<>(
ResourceLocation.fromNamespaceAndPath(ExtendedAEPlus.MODID, "request_providers_list"));
public class RequestUploadingC2SPacket implements CustomPacketPayload {
public static final Type<RequestUploadingC2SPacket> TYPE = new Type<>(
ResourceLocation.fromNamespaceAndPath(ExtendedAEPlus.MODID, "request_uploading"));
public static final RequestProvidersListC2SPacket INSTANCE = new RequestProvidersListC2SPacket();
public static final RequestUploadingC2SPacket INSTANCE = new RequestUploadingC2SPacket();
public static final StreamCodec<FriendlyByteBuf, RequestProvidersListC2SPacket> STREAM_CODEC =
public static final StreamCodec<FriendlyByteBuf, RequestUploadingC2SPacket> STREAM_CODEC =
StreamCodec.unit(INSTANCE);
public RequestProvidersListC2SPacket() {}
public RequestUploadingC2SPacket() {}
@Override
public Type<? extends CustomPacketPayload> type() {
return TYPE;
}
public static void handle(final RequestProvidersListC2SPacket msg, final IPayloadContext ctx) {
public static void handle(final RequestUploadingC2SPacket msg, final IPayloadContext ctx) {
ctx.enqueueWork(() -> {
if (!(ctx.player() instanceof ServerPlayer player)) return;
if (!(player.containerMenu instanceof PatternEncodingTermMenu encMenu)) return;
if (encMenu.getMode() != EncodingMode.PROCESSING) {
try {
ExtendedAEPatternUploadUtil.uploadFromEncodingMenuToMatrix(player, encMenu);
} catch (Throwable ignored) {}
return;
}
// 优先若玩家也打开了样板访问终端则用 byId 方式精确服务器ID
PatternAccessTermMenu accessMenu = ExtendedAEPatternUploadUtil.getPatternAccessMenu(player);
if (accessMenu != null) {

View File

@ -767,10 +767,7 @@ public class ExtendedAEPatternUploadUtil {
try {
// 通过反射访问byId字段ExtendedAE继承了这个字段
Field byIdField = findByIdField(menu.getClass());
if (byIdField == null) {
System.err.println("ExtendedAE Plus: 无法找到byId字段");
return null;
}
if (byIdField == null) return null;
byIdField.setAccessible(true);
@ -784,16 +781,12 @@ public class ExtendedAEPatternUploadUtil {
// 从ContainerTracker中获取PatternContainer
Field containerField = findContainerField(containerTracker.getClass());
if (containerField == null) {
System.err.println("ExtendedAE Plus: 无法找到container字段");
return null;
}
if (containerField == null) return null;
containerField.setAccessible(true);
return (PatternContainer) containerField.get(containerTracker);
} catch (Exception e) {
System.err.println("ExtendedAE Plus: 无法获取PatternContainer错误: " + e.getMessage());
return null;
}
}

View File

@ -3,7 +3,6 @@
"package": "com.extendedae_plus.mixin",
"compatibilityLevel": "JAVA_21",
"mixins": [
"MixinAbstractContainerMenu",
"MixinPlayer",
"ae2.AEProcessingPatternMixin",
"ae2.CraftingCPUClusterMixin",