舍弃按钮,改用点击编码按键自动上传

This commit is contained in:
GaLicn 2025-08-14 01:38:14 +08:00
parent ad8a7ac930
commit 48bd05576c
5 changed files with 79 additions and 163 deletions

View File

@ -3,7 +3,7 @@ org.gradle.jvmargs=-Xmx1G
loom.platform = forge
# Mod properties
mod_version = 1.2.0-a
mod_version = 1.2.1
maven_group = com.extendedae_plus
archives_name = extendedae_plus

View File

@ -1,14 +1,18 @@
package com.extendedae_plus.mixin;
import appeng.menu.me.items.PatternEncodingTermMenu;
import com.extendedae_plus.util.ExtendedAEPatternUploadUtil;
import appeng.menu.slot.RestrictedInputSlot;
import appeng.api.crafting.PatternDetailsHelper;
import appeng.parts.encoding.EncodingMode;
import com.glodblock.github.glodium.network.packet.sync.IActionHolder;
import com.glodblock.github.glodium.network.packet.sync.Paras;
import com.extendedae_plus.util.ExtendedAEPatternUploadUtil;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.entity.player.Player;
import org.jetbrains.annotations.NotNull;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique;
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.callback.CallbackInfo;
@ -29,39 +33,47 @@ public abstract class ContainerPatternEncodingTermMenuMixin implements IActionHo
@Unique
private Player epp$player;
@Shadow(remap = false)
private RestrictedInputSlot encodedPatternSlot;
@Unique
private void epp$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)) {
System.out.println("[EAE+][Server] Auto-upload crafting pattern after encode.");
ExtendedAEPatternUploadUtil.uploadFromEncodingMenuToMatrix(sp, menu);
} else {
// 槽位可能尚未同步到位继续下一 tick 重试
if (attemptsLeft > 0) {
epp$scheduleUploadWithRetry(sp, menu, attemptsLeft - 1);
} else {
System.out.println("[EAE+][Server] Auto-upload aborted: encoded slot still empty or not encoded after retries.");
}
}
} catch (Throwable t) {
System.out.println("[EAE+][Server] Auto-upload after encode failed: " + t);
t.printStackTrace();
}
});
}
// 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 epp$ctorA(int id, net.minecraft.world.entity.player.Inventory ip, appeng.helpers.IPatternTerminalMenuHost host, CallbackInfo ci) {
this.epp$player = ip.player;
// 注册动作无参由服务端直接读 encoded 槽位
System.out.println("[EAE+][Server] Register action 'upload_to_matrix' for PatternEncodingTermMenu ctorA");
this.actions.put("upload_to_matrix", p -> {
try {
var sp = (ServerPlayer) this.epp$player;
System.out.println("[EAE+][Server] Handle action 'upload_to_matrix' from " + sp.getGameProfile().getName());
ExtendedAEPatternUploadUtil.uploadFromEncodingMenuToMatrix(sp, (PatternEncodingTermMenu) (Object) this);
} catch (Throwable t) {
System.out.println("[EAE+][Server] Exception in 'upload_to_matrix': " + t);
t.printStackTrace();
}
});
// 不再注册任何上传相关动作
}
// AE2 另一个构造PatternEncodingTermMenu(MenuType, int, Inventory, IPatternTerminalMenuHost, boolean)
@Inject(method = "<init>(Lnet/minecraft/world/inventory/MenuType;ILnet/minecraft/world/entity/player/Inventory;Lappeng/helpers/IPatternTerminalMenuHost;Z)V", at = @At("TAIL"), remap = false)
private void epp$ctorB(net.minecraft.world.inventory.MenuType<?> menuType, int id, net.minecraft.world.entity.player.Inventory ip, appeng.helpers.IPatternTerminalMenuHost host, boolean bindInventory, CallbackInfo ci) {
this.epp$player = ip.player;
System.out.println("[EAE+][Server] Register action 'upload_to_matrix' for PatternEncodingTermMenu ctorB");
this.actions.put("upload_to_matrix", p -> {
try {
var sp = (ServerPlayer) this.epp$player;
System.out.println("[EAE+][Server] Handle action 'upload_to_matrix' from " + sp.getGameProfile().getName());
ExtendedAEPatternUploadUtil.uploadFromEncodingMenuToMatrix(sp, (PatternEncodingTermMenu) (Object) this);
} catch (Throwable t) {
System.out.println("[EAE+][Server] Exception in 'upload_to_matrix': " + t);
t.printStackTrace();
}
});
// 不再注册任何上传相关动作
}
@NotNull
@ -69,4 +81,33 @@ public abstract class ContainerPatternEncodingTermMenuMixin implements IActionHo
public Map<String, Consumer<Paras>> getActionMap() {
return this.actions;
}
// 服务器端 encode() 执行完毕后如果已编码槽位存在样板且当前为合成模式则上传到装配矩阵
@Inject(method = "encode", at = @At("TAIL"), remap = false)
private void epp$serverUploadAfterEncode(CallbackInfo ci) {
try {
if (!(this.epp$player instanceof ServerPlayer sp)) {
return; // 仅服务器执行
}
var menu = (PatternEncodingTermMenu) (Object) this;
if (menu.getMode() != EncodingMode.CRAFTING) {
return; // 只处理合成样板
}
if (this.encodedPatternSlot == null) {
return;
}
var stack = this.encodedPatternSlot.getItem();
if (stack == null || stack.isEmpty()) {
return; // 没有编码样板
}
if (!PatternDetailsHelper.isEncodedPattern(stack)) {
return; // 不是编码样板
}
// 为兼容整合包中可能出现的延后写槽位/同步增加多次重试 5 每次间隔 1 tick
epp$scheduleUploadWithRetry(sp, menu, 5);
} catch (Throwable t) {
System.out.println("[EAE+][Server] epp$serverUploadAfterEncode error: " + t);
t.printStackTrace();
}
}
}

View File

@ -1,145 +1,9 @@
package com.extendedae_plus.mixin;
import appeng.client.gui.me.items.PatternEncodingTermScreen;
import appeng.client.gui.widgets.IconButton;
import appeng.client.gui.Icon;
import appeng.client.gui.style.ScreenStyle;
import appeng.menu.me.items.PatternEncodingTermMenu;
import appeng.parts.encoding.EncodingMode;
import appeng.menu.SlotSemantics;
import net.minecraft.client.gui.components.Tooltip;
import net.minecraft.network.chat.Component;
import net.minecraft.world.entity.player.Inventory;
import net.minecraft.world.inventory.Slot;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import com.glodblock.github.extendedae.network.EPPNetworkHandler;
import com.glodblock.github.glodium.network.packet.CGenericPacket;
import com.extendedae_plus.mixin.accessor.ScreenAccessor;
import com.extendedae_plus.mixin.accessor.AEBaseScreenAccessor;
/**
* AE2 PatternEncodingTermScreen 界面中给编码按钮旁添加一个上传到矩阵按钮
* 客户端点击后使用 ExtendedAE 的网络通道发送通用数据包到服务端由容器 Mixin 处理
*/
// 保留空的 Mixin 外壳不再注入任何上传到矩阵相关逻辑或按钮
@Mixin(PatternEncodingTermScreen.class)
public abstract class GuiPatternEncodingTermMixin<C extends PatternEncodingTermMenu> {
@Unique
private IconButton epp$uploadButton;
@Unique
private boolean epp$addedWidget = false;
@Unique
private boolean epp$usingToolbar = false;
// 在构造函数尾部创建按钮实例并加入普通部件列表
@Inject(method = "<init>", at = @At("TAIL"), remap = false)
private void epp$onInit(C menu, Inventory playerInventory, Component title, ScreenStyle style, CallbackInfo ci) {
// 选择一个合适的图标占位AE2 暂无显式上传图标这里先用 PATTERN_ACCESS_SHOW
this.epp$uploadButton = new IconButton(b -> {
// 直接发送无参动作服务端容器会根据当前 encoded 槽位处理
System.out.println("[EAE+][Client] Click upload button -> send CGenericPacket('upload_to_matrix')");
try {
var mc = net.minecraft.client.Minecraft.getInstance();
var cm = mc.player != null ? mc.player.containerMenu : null;
System.out.println("[EAE+][Client] Current container: " + (cm == null ? "null" : cm.getClass().getName()));
if (cm instanceof com.glodblock.github.glodium.network.packet.sync.IActionHolder ah) {
var keys = ah.getActionMap().keySet();
System.out.println("[EAE+][Client] IActionHolder detected. actions=" + keys);
} else {
System.out.println("[EAE+][Client] Current container is NOT IActionHolder");
}
} catch (Throwable t) {
System.out.println("[EAE+][Client] Inspect container failed: " + t);
}
EPPNetworkHandler.INSTANCE.sendToServer(new CGenericPacket("upload_to_matrix"));
}) {
@Override
protected Icon getIcon() {
return Icon.PATTERN_ACCESS_SHOW;
}
};
this.epp$uploadButton.setTooltip(Tooltip.create(Component.translatable("extendedae_plus.upload_to_matrix")));
System.out.println("[EAE+][Client] Created upload button in PatternEncodingTermScreen ctor tail");
// 注意此处不立即添加到 Screen等到 updateBeforeRender 首帧再添加避免在构造期调用导致异常
this.epp$uploadButton.visible = false; // 初始隐藏等待定位
}
// 在每帧渲染前定位按钮到已编码样板槽位的左侧
@Inject(method = "updateBeforeRender", at = @At("TAIL"), remap = false)
private void epp$positionUploadButton(CallbackInfo ci) {
try {
if (this.epp$uploadButton == null) {
return;
}
// 首帧尝试将按钮加入到 Screen 的可渲染部件中
if (!this.epp$addedWidget) {
try {
((ScreenAccessor)(Object)this).epp$invokeAddRenderableWidget(this.epp$uploadButton);
this.epp$addedWidget = true;
this.epp$usingToolbar = false;
System.out.println("[EAE+][Client] Added upload button as normal widget in first updateBeforeRender");
} catch (Throwable t) {
System.out.println("[EAE+][Client] addRenderableWidget failed, fallback to left toolbar: " + t);
try {
((AEBaseScreenAccessor)(Object)this).epp$addToLeftToolbar(this.epp$uploadButton);
this.epp$addedWidget = true;
this.epp$usingToolbar = true;
this.epp$uploadButton.visible = true;
System.out.println("[EAE+][Client] Fallback added to left toolbar");
} catch (Throwable t2) {
System.out.println("[EAE+][Client] Fallback addToLeftToolbar also failed: " + t2);
// 放弃添加避免死循环
this.epp$addedWidget = true;
}
}
}
// 仅在合成模式下显示按钮与上传逻辑一致
var self = (PatternEncodingTermScreen<?>) (Object) this;
var menu = (PatternEncodingTermMenu) self.getMenu();
if (menu == null) {
return;
}
boolean craftingMode = menu.getMode() == EncodingMode.CRAFTING;
Slot encoded = null;
for (Slot s : menu.slots) {
var sem = menu.getSlotSemantic(s);
if (sem == SlotSemantics.ENCODED_PATTERN) {
encoded = s;
break;
}
}
// 如果退回到左侧工具栏显示则不做定位
if (this.epp$usingToolbar) {
return;
}
if (craftingMode && encoded != null) {
int slotX = self.getGuiLeft() + encoded.x;
int slotY = self.getGuiTop() + encoded.y;
int bw = this.epp$uploadButton.getWidth();
int bh = this.epp$uploadButton.getHeight();
int gap = 3;
this.epp$uploadButton.setX(slotX - gap - bw);
this.epp$uploadButton.setY(slotY + (18 - bh) / 2);
this.epp$uploadButton.visible = true;
// 可选当槽位有编码图样时启用
this.epp$uploadButton.active = !encoded.getItem().isEmpty();
} else {
this.epp$uploadButton.visible = false;
}
} catch (Throwable t) {
System.out.println("[EAE+][Client] updateBeforeRender positioning failed: " + t);
}
}
public abstract class GuiPatternEncodingTermMixin {
}

View File

@ -0,0 +1,10 @@
package com.extendedae_plus.mixin.accessor;
/**
* 客户端在点击编写样板时记录一个待上传标记
* 当客户端检测到已编码样板槽位被服务器回传填充后再发送上传请求
*/
public interface IClientEncodeUploadMarker {
boolean epp$getClientUploadAfterEncode();
void epp$setClientUploadAfterEncode(boolean v);
}

View File

@ -19,6 +19,7 @@
"ContainerUWirelessExPatternTerminalMixin",
"TileExPatternProviderMixin",
"PartExPatternProviderMixin",
"PatternEncodingTermMenuMixin",
"ContainerPatternEncodingTermMenuMixin",
"accessor.MEStorageMenuAccessor",
"accessor.PatternEncodingTermMenuAccessor"