diff --git a/src/main/java/com/extendedae_plus/init/ModNetwork.java b/src/main/java/com/extendedae_plus/init/ModNetwork.java index 5dae0a4..dd5560d 100644 --- a/src/main/java/com/extendedae_plus/init/ModNetwork.java +++ b/src/main/java/com/extendedae_plus/init/ModNetwork.java @@ -202,6 +202,12 @@ public final class ModNetwork { .decoder(CancelPendingPatternC2SPacket::decode) .consumerNetworkThread(CancelPendingPatternC2SPacket::handle) .add(); + + CHANNEL.messageBuilder(ReturnLastPatternC2SPacket.class,nextId(), NetworkDirection.PLAY_TO_SERVER) + .encoder(ReturnLastPatternC2SPacket::encode) + .decoder(ReturnLastPatternC2SPacket::decode) + .consumerNetworkThread(ReturnLastPatternC2SPacket::handle) + .add(); } private static int nextId() { return id++; } diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/client/gui/PatternEncodingTermScreenMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/client/gui/PatternEncodingTermScreenMixin.java index e4f44d8..c8f6bf0 100644 --- a/src/main/java/com/extendedae_plus/mixin/ae2/client/gui/PatternEncodingTermScreenMixin.java +++ b/src/main/java/com/extendedae_plus/mixin/ae2/client/gui/PatternEncodingTermScreenMixin.java @@ -12,6 +12,7 @@ import com.extendedae_plus.mixin.ae2.accessor.AEBaseScreenAccessor; import com.extendedae_plus.mixin.minecraft.accessor.AbstractContainerScreenAccessor; import com.extendedae_plus.mixin.minecraft.accessor.ScreenAccessor; import com.extendedae_plus.network.provider.RequestProvidersListC2SPacket; +import com.extendedae_plus.network.provider.ReturnLastPatternC2SPacket; import com.extendedae_plus.network.upload.EncodeWithShiftFlagC2SPacket; import com.mojang.blaze3d.systems.RenderSystem; import net.minecraft.client.gui.GuiGraphics; @@ -66,9 +67,13 @@ public abstract class PatternEncodingTermScreenMixin { @Unique private IconButton createUploadButton() { - IconButton btn = new IconButton(button -> - ModNetwork.CHANNEL.sendToServer(new RequestProvidersListC2SPacket()) - ) { + IconButton btn = new IconButton(button -> { + if (Screen.hasShiftDown()) { + ModNetwork.CHANNEL.sendToServer(new ReturnLastPatternC2SPacket()); + } else { + ModNetwork.CHANNEL.sendToServer(new RequestProvidersListC2SPacket()); + } + }) { private final float eap$scale = 0.75f; @Override @@ -99,7 +104,19 @@ public abstract class PatternEncodingTermScreenMixin { if (!this.isDisableBackground()) { Icon.TOOLBAR_BUTTON_BACKGROUND.getBlitter().dest(0, 0).blit(guiGraphics); } - blitter.dest(0, 0).blit(guiGraphics); + + if (Screen.hasShiftDown()) { + pose.pushPose(); + // Rotate around the center of the 16x16 icon + pose.translate(8.0f, 8.0f, 0.0f); + pose.mulPose(com.mojang.math.Axis.ZP.rotationDegrees(180.0f)); + pose.translate(-8.0f, -8.0f, 0.0f); + blitter.dest(0, 0).blit(guiGraphics); + pose.popPose(); + } else { + blitter.dest(0, 0).blit(guiGraphics); + } + pose.popPose(); RenderSystem.enableDepthTest(); @@ -110,6 +127,16 @@ public abstract class PatternEncodingTermScreenMixin { return new Rect2i(getX(), getY(), Math.round(16 * eap$scale), Math.round(16 * eap$scale)); } + @Override + public void render(GuiGraphics p_281670_, int p_282682_, int p_281714_, float p_282542_) { + if (Screen.hasShiftDown()) { + this.setTooltip(Tooltip.create(Component.translatable("extendedae_plus.button.return_last_pattern"))); + } else { + this.setTooltip(Tooltip.create(Component.translatable("extendedae_plus.button.choose_provider"))); + } + super.render(p_281670_, p_282682_, p_281714_, p_282542_); + } + @Override protected Icon getIcon() { return Icon.ARROW_UP; diff --git a/src/main/java/com/extendedae_plus/network/provider/ReturnLastPatternC2SPacket.java b/src/main/java/com/extendedae_plus/network/provider/ReturnLastPatternC2SPacket.java new file mode 100644 index 0000000..37942fd --- /dev/null +++ b/src/main/java/com/extendedae_plus/network/provider/ReturnLastPatternC2SPacket.java @@ -0,0 +1,138 @@ +package com.extendedae_plus.network.provider; + +import appeng.api.crafting.PatternDetailsHelper; +import appeng.api.inventories.InternalInventory; +import appeng.api.networking.IGrid; +import appeng.api.networking.IGridNode; +import appeng.helpers.patternprovider.PatternContainer; +import appeng.menu.implementations.PatternAccessTermMenu; +import appeng.menu.me.items.PatternEncodingTermMenu; +import com.extendedae_plus.util.PatternTerminalUtil; +import com.extendedae_plus.util.uploadPattern.ProviderUploadUtil; + +import com.glodblock.github.extendedae.common.tileentities.matrix.TileAssemblerMatrixPattern; +import com.extendedae_plus.content.matrix.PatternCorePlusBlockEntity; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.item.ItemStack; +import net.minecraftforge.network.NetworkEvent; + +import java.util.List; +import java.util.Set; +import java.util.function.Supplier; + +/** + * C2S: Retrieved the last uploaded pattern from the recently used provider. + */ +public class ReturnLastPatternC2SPacket { + public ReturnLastPatternC2SPacket() {} + + public static void encode(ReturnLastPatternC2SPacket msg, FriendlyByteBuf buf) {} + + public static ReturnLastPatternC2SPacket decode(FriendlyByteBuf buf) { + return new ReturnLastPatternC2SPacket(); + } + + public static void handle(ReturnLastPatternC2SPacket msg, Supplier ctxSupplier) { + var ctx = ctxSupplier.get(); + ctx.enqueueWork(() -> { + ServerPlayer player = ctx.getSender(); + if (player == null) return; + + long lastProviderId = ProviderUploadUtil.getLastUploadedProviderId(player); + if (lastProviderId == Long.MIN_VALUE) { + return; // Nothing to return + } + + // Special ID for Assembly Matrix + if (lastProviderId == -999999L) { + if (player.containerMenu instanceof PatternEncodingTermMenu encMenu) { + IGridNode node = encMenu.getNetworkNode(); + if (node != null && node.getGrid() != null) { + IGrid grid = node.getGrid(); + // Get all matrix pattern inventories + try { + Set allTiles = grid.getMachines(TileAssemblerMatrixPattern.class); + for (TileAssemblerMatrixPattern tile : allTiles) { + if (tile != null && tile.isFormed() && tile.getMainNode().isActive()) { + InternalInventory inv = tile.getTerminalPatternInventory(); + if (inv != null && returnPatternFromInventory(player, inv)) { + return; + } + } + } + + Set myAllTiles = grid.getMachines(PatternCorePlusBlockEntity.class); + for (PatternCorePlusBlockEntity tile : myAllTiles) { + if (tile != null && tile.isFormed() && tile.getMainNode().isActive()) { + InternalInventory inv = tile.getTerminalPatternInventory(); + if (inv != null && returnPatternFromInventory(player, inv)) { + return; + } + } + } + } catch (Exception ignored) {} + } + } + return; + } + + PatternContainer targetContainer = null; + + // Try to resolve the container using encoding menu + if (player.containerMenu instanceof PatternEncodingTermMenu encMenu) { + if (lastProviderId >= 0) { + PatternAccessTermMenu accessMenu = PatternTerminalUtil.getPatternAccessMenu(player); + if (accessMenu != null) { + targetContainer = PatternTerminalUtil.getPatternContainerById(accessMenu, lastProviderId); + } + } else { + int index = (int) (-1L - lastProviderId); + List list = PatternTerminalUtil.listAvailableProvidersFromGrid(encMenu); + if (index >= 0 && index < list.size()) { + targetContainer = list.get(index); + } + } + } else { + // Try to resolve from player network if not in encoding menu (e.g. pending ctrl+q state) + if (lastProviderId >= 0) { + // It's harder without access terminal menu, but technically we could search the grid + return; + } else { + int index = (int) (-1L - lastProviderId); + List list = ProviderUploadUtil.listAvailableProvidersFromPlayerNetwork(player); + if (index >= 0 && index < list.size()) { + targetContainer = list.get(index); + } + } + } + + if (targetContainer == null || !targetContainer.isVisibleInTerminal()) { + return; + } + + InternalInventory inv = targetContainer.getTerminalPatternInventory(); + if (inv != null && inv.size() > 0) { + returnPatternFromInventory(player, inv); + } + }); + ctx.setPacketHandled(true); + } + + private static boolean returnPatternFromInventory(ServerPlayer player, InternalInventory inv) { + // Find the highest slot with an encoded pattern + for (int slot = inv.size() - 1; slot >= 0; slot--) { + ItemStack stack = inv.getStackInSlot(slot); + if (!stack.isEmpty() && PatternDetailsHelper.isEncodedPattern(stack)) { + ItemStack extracted = inv.extractItem(slot, 1, false); + if (!extracted.isEmpty()) { + if (!player.getInventory().add(extracted)) { + player.drop(extracted, false); + } + return true; // Found and returned + } + } + } + return false; + } +} diff --git a/src/main/java/com/extendedae_plus/util/uploadPattern/MatrixUploadUtil.java b/src/main/java/com/extendedae_plus/util/uploadPattern/MatrixUploadUtil.java index 8a0fc48..2dc54e1 100644 --- a/src/main/java/com/extendedae_plus/util/uploadPattern/MatrixUploadUtil.java +++ b/src/main/java/com/extendedae_plus/util/uploadPattern/MatrixUploadUtil.java @@ -132,6 +132,7 @@ public final class MatrixUploadUtil { if (remain.getCount() < pattern.getCount()) { // 上传成功 sendPlayerMessage(player, Component.translatable("extendedae_plus.upload_to_matrix.success")); + player.getPersistentData().putLong("eap_last_uploaded_provider_id", -999999L); return true; } } @@ -146,7 +147,7 @@ public final class MatrixUploadUtil { /** * 在给定 AE Grid 中收集所有已成型且在线的装配矩阵“样板核心”的用于外部插入的内部库存 */ - private static List findAllMatrixPatternInventories(IGrid grid) { + public static List findAllMatrixPatternInventories(IGrid grid) { List result = new ArrayList<>(); if (grid == null) return result; @@ -242,6 +243,7 @@ public final class MatrixUploadUtil { stack.shrink(inserted); if (stack.isEmpty()) encodedSlot.set(ItemStack.EMPTY); sendPlayerMessage(player, Component.translatable("extendedae_plus.upload_to_matrix.success")); + player.getPersistentData().putLong("eap_last_uploaded_provider_id", -999999L); } } diff --git a/src/main/java/com/extendedae_plus/util/uploadPattern/ProviderUploadUtil.java b/src/main/java/com/extendedae_plus/util/uploadPattern/ProviderUploadUtil.java index 31c20f9..521592f 100644 --- a/src/main/java/com/extendedae_plus/util/uploadPattern/ProviderUploadUtil.java +++ b/src/main/java/com/extendedae_plus/util/uploadPattern/ProviderUploadUtil.java @@ -35,6 +35,7 @@ import java.util.UUID; public final class ProviderUploadUtil { private static final String PENDING_DATA_KEY = "eap_ctrlq_pending_provider_upload_id"; private static final String PENDING_STACK_KEY = "eap_ctrlq_pending_provider_upload_stack"; + private static final String LAST_UPLOADED_PROVIDER_KEY = "eap_last_uploaded_provider_id"; private ProviderUploadUtil() {} @@ -114,6 +115,8 @@ public final class ProviderUploadUtil { player.getInventory().setItem(playerSlotIndex, ItemStack.EMPTY); } + player.getPersistentData().putLong(LAST_UPLOADED_PROVIDER_KEY, providerId); + String terminalType = PatternTerminalUtil.isExtendedAETerminal(player) ? "扩展样板管理终端" : "样板访问终端"; sendMessage(player, "ExtendedAE Plus: 通过" + terminalType + "成功上传 " + insertedCount + " 个样板"); return true; @@ -175,6 +178,7 @@ public final class ProviderUploadUtil { } else { encodedSlot.set(stack); } + player.getPersistentData().putLong(LAST_UPLOADED_PROVIDER_KEY, id); return true; } } @@ -227,6 +231,15 @@ public final class ProviderUploadUtil { } else { encodedSlot.set(stack); } + + // For index-based, we can store the negative index to let the return logic resolve it + player.getPersistentData().putLong(LAST_UPLOADED_PROVIDER_KEY, index == 0 && c == list.get(0) ? (-1L - index) : -1000000L); // store specifically if logic allows, but to be sure let's store the index format + // actually we know `c` might be from tryList, let's find the true index of `c` in `list` + int trueIndex = list.indexOf(c); + if (trueIndex >= 0) { + player.getPersistentData().putLong(LAST_UPLOADED_PROVIDER_KEY, -1L - trueIndex); + } + return true; } } @@ -278,6 +291,8 @@ public final class ProviderUploadUtil { } else { player.getPersistentData().put(PENDING_STACK_KEY, remain.save(new CompoundTag())); } + + player.getPersistentData().putLong(LAST_UPLOADED_PROVIDER_KEY, providerId); return true; } @@ -407,6 +422,15 @@ public final class ProviderUploadUtil { return null; } + public static long getLastUploadedProviderId(ServerPlayer player) { + if (player == null) return Long.MIN_VALUE; + CompoundTag data = player.getPersistentData(); + if (data.contains(LAST_UPLOADED_PROVIDER_KEY)) { + return data.getLong(LAST_UPLOADED_PROVIDER_KEY); + } + return Long.MIN_VALUE; + } + /** * ExtendedAE兼容的样板过滤器 * 使用AE2的PatternDetailsHelper进行样板验证 diff --git a/src/main/resources/assets/extendedae_plus/lang/en_us.json b/src/main/resources/assets/extendedae_plus/lang/en_us.json index c24cd62..b3dc01a 100644 --- a/src/main/resources/assets/extendedae_plus/lang/en_us.json +++ b/src/main/resources/assets/extendedae_plus/lang/en_us.json @@ -142,6 +142,7 @@ "extendedae_plus.screen.open_provider_ui": "Open the target container UI of this provider", "extendedae_plus.button.choose_provider": "Upload Pattern", + "extendedae_plus.button.return_last_pattern": "Return Last Uploaded Pattern", "extendedae_plus.pattern.hovertext.player": "Encoded by %s", "item.extendedae_plus.entity_speed_ticker": "Entity Speed Ticker", diff --git a/src/main/resources/assets/extendedae_plus/lang/zh_cn.json b/src/main/resources/assets/extendedae_plus/lang/zh_cn.json index 7ede5bf..f7f619d 100644 --- a/src/main/resources/assets/extendedae_plus/lang/zh_cn.json +++ b/src/main/resources/assets/extendedae_plus/lang/zh_cn.json @@ -141,6 +141,7 @@ "extendedae_plus.screen.open_provider_ui": "打开该供应器目标容器的界面", "extendedae_plus.button.choose_provider":"上传样板", + "extendedae_plus.button.return_last_pattern":"取回上一个上传样板", "extendedae_plus.pattern.hovertext.player": "由 %s 编码", "item.extendedae_plus.entity_speed_ticker": "实体加速器", diff --git a/src/main/resources/assets/extendedae_plus/lang/zh_tw.json b/src/main/resources/assets/extendedae_plus/lang/zh_tw.json index 4f8acba..e6cdf7a 100644 --- a/src/main/resources/assets/extendedae_plus/lang/zh_tw.json +++ b/src/main/resources/assets/extendedae_plus/lang/zh_tw.json @@ -140,6 +140,7 @@ "extendedae_plus.screen.open_provider_ui": "開啟該供應器目標容器的介面", "extendedae_plus.button.choose_provider": "上傳樣板", + "extendedae_plus.button.return_last_pattern": "取回上一個上傳樣板", "extendedae_plus.pattern.hovertext.player": "由 %s 編碼", "item.extendedae_plus.entity_speed_ticker": "實體加速器",