diff --git a/gradle.properties b/gradle.properties index 3446d39..5799383 100644 --- a/gradle.properties +++ b/gradle.properties @@ -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 diff --git a/src/main/java/com/extendedae_plus/mixin/ContainerPatternEncodingTermMenuMixin.java b/src/main/java/com/extendedae_plus/mixin/ContainerPatternEncodingTermMenuMixin.java index 5913315..ce90a4a 100644 --- a/src/main/java/com/extendedae_plus/mixin/ContainerPatternEncodingTermMenuMixin.java +++ b/src/main/java/com/extendedae_plus/mixin/ContainerPatternEncodingTermMenuMixin.java @@ -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 = "(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 = "(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> 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(); + } + } } diff --git a/src/main/java/com/extendedae_plus/mixin/GuiPatternEncodingTermMixin.java b/src/main/java/com/extendedae_plus/mixin/GuiPatternEncodingTermMixin.java index a0e7b46..4ee72aa 100644 --- a/src/main/java/com/extendedae_plus/mixin/GuiPatternEncodingTermMixin.java +++ b/src/main/java/com/extendedae_plus/mixin/GuiPatternEncodingTermMixin.java @@ -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 { - - @Unique - private IconButton epp$uploadButton; - @Unique - private boolean epp$addedWidget = false; - @Unique - private boolean epp$usingToolbar = false; - - // 在构造函数尾部创建按钮实例并加入普通部件列表 - @Inject(method = "", 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 { } diff --git a/src/main/java/com/extendedae_plus/mixin/accessor/IClientEncodeUploadMarker.java b/src/main/java/com/extendedae_plus/mixin/accessor/IClientEncodeUploadMarker.java new file mode 100644 index 0000000..69ad5a2 --- /dev/null +++ b/src/main/java/com/extendedae_plus/mixin/accessor/IClientEncodeUploadMarker.java @@ -0,0 +1,10 @@ +package com.extendedae_plus.mixin.accessor; + +/** + * 客户端在点击“编写样板”时记录一个待上传标记, + * 当客户端检测到已编码样板槽位被服务器回传填充后,再发送上传请求。 + */ +public interface IClientEncodeUploadMarker { + boolean epp$getClientUploadAfterEncode(); + void epp$setClientUploadAfterEncode(boolean v); +} diff --git a/src/main/resources/extendedae_plus.mixins.json b/src/main/resources/extendedae_plus.mixins.json index 4d2a794..645e73a 100644 --- a/src/main/resources/extendedae_plus.mixins.json +++ b/src/main/resources/extendedae_plus.mixins.json @@ -19,6 +19,7 @@ "ContainerUWirelessExPatternTerminalMixin", "TileExPatternProviderMixin", "PartExPatternProviderMixin", + "PatternEncodingTermMenuMixin", "ContainerPatternEncodingTermMenuMixin", "accessor.MEStorageMenuAccessor", "accessor.PatternEncodingTermMenuAccessor"