装配矩阵上传优化

This commit is contained in:
C-H716 2025-10-27 01:27:46 +08:00
parent 48e8b01384
commit 77edd13054
3 changed files with 111 additions and 188 deletions

View File

@ -2,6 +2,7 @@ package com.extendedae_plus.util;
import net.minecraft.client.Minecraft;
import net.minecraft.network.chat.Component;
import net.minecraft.world.entity.player.Player;
public class GlobalSendMessage {
/**
@ -13,4 +14,10 @@ public class GlobalSendMessage {
player.sendSystemMessage(msg);
}
}
public static void sendPlayerMessage(Player player, Component msg) {
if (player != null) {
player.sendSystemMessage(msg);
}
}
}

View File

@ -13,267 +13,180 @@ import appeng.menu.me.items.PatternEncodingTermMenu;
import appeng.menu.slot.RestrictedInputSlot;
import com.extendedae_plus.content.matrix.UploadCoreBlockEntity;
import com.extendedae_plus.mixin.ae2.accessor.PatternEncodingTermMenuAccessor;
import com.glodblock.github.extendedae.common.tileentities.matrix.TileAssemblerMatrixBase;
import com.glodblock.github.extendedae.common.me.matrix.ClusterAssemblerMatrix;
import com.glodblock.github.extendedae.common.tileentities.matrix.TileAssemblerMatrixPattern;
import net.minecraft.network.chat.Component;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.item.ItemStack;
import net.minecraftforge.common.capabilities.ForgeCapabilities;
import net.minecraftforge.items.IItemHandler;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import static com.extendedae_plus.util.GlobalSendMessage.sendPlayerMessage;
/**
* 与装配矩阵 (assembler matrix) 上传 / 检查 相关的工具方法
* 保留原有逻辑与容错行为
* ExtendedAE 装配矩阵样板上传
* 用于从 AE2 的样板编码终端上传至装配矩阵仅合成样板
*/
public final class MatrixUploadUtil {
private MatrixUploadUtil() {}
/**
* 发送消息给玩家
*
* @param player 玩家
* @param message 消息内容
*/
private static void sendMessage(ServerPlayer player, String message) {
// 静默不再向玩家左下角发送任何提示信息
// 如需恢复取消下面注释即可
// if (player != null) {
// player.sendSystemMessage(Component.literal(message));
// }
// 如果玩家为null静默忽略用于测试环境
}
/**
* AE2 的图样编码终端菜单上传当前已编码图样 ExtendedAE 装配矩阵仅合成图样
* 不会处理处理图样
* AE2 的样板编码终端菜单上传当前已编码合成样板 ExtendedAE 装配矩阵仅合成样板
*
* @param player 服务器玩家
* @param menu PatternEncodingTermMenu
* @return 是否成功插入矩阵
*/
public static boolean uploadFromEncodingMenuToMatrix(ServerPlayer player, PatternEncodingTermMenu menu) {
if (player == null || menu == null) {
return false;
}
public static void uploadFromEncodingMenuToMatrix(ServerPlayer player, PatternEncodingTermMenu menu) {
if (player == null || menu == null) return;
// 读取已编码槽位的物品
RestrictedInputSlot encodedSlot = ((PatternEncodingTermMenuAccessor)menu).eap$getEncodedPatternSlot();
RestrictedInputSlot encodedSlot = ((PatternEncodingTermMenuAccessor) menu).eap$getEncodedPatternSlot();
ItemStack stack = encodedSlot.getItem();
if (stack.isEmpty() || !PatternDetailsHelper.isEncodedPattern(stack)) {
return false;
}
if (stack.isEmpty() || !PatternDetailsHelper.isEncodedPattern(stack)) return;
// 仅允许合成/锻造台/切石机
// 仅允许合成/锻造台/切石机样板
IPatternDetails details = PatternDetailsHelper.decodePattern(stack, player.level());
if (!(details instanceof AECraftingPattern
|| details instanceof AESmithingTablePattern
|| details instanceof AEStonecuttingPattern)) {
return false;
return;
}
// 获取 AE 网络
IGridNode node = menu.getNetworkNode();
if (node == null) {
return false;
}
IGrid grid = node.getGrid();
if (grid == null) {
return false;
}
if (node == null) return;
// 在尝试上传之前检查装配矩阵是否已经存在相同样板物品与NBT完全一致
if (matrixContainsPattern(grid, stack)) {
// 直接提醒并跳过上传并将同等数量的空白样板放回空白样板槽否则退回玩家背包
if (player != null) {
player.sendSystemMessage(Component.literal("ExtendedAE Plus: 装配矩阵已存在相同样板,已跳过上传并返还空白样板"));
}
try {
var accessor = (PatternEncodingTermMenuAccessor) (Object) menu;
var blankSlot = accessor.eap$getBlankPatternSlot();
ItemStack blanks = AEItems.BLANK_PATTERN.stack(stack.getCount());
if (blankSlot != null && blankSlot.mayPlace(blanks)) {
ItemStack remain = blankSlot.safeInsert(blanks);
if (!remain.isEmpty() && player != null) {
player.getInventory().placeItemBackInInventory(remain, false);
}
} else if (player != null) {
player.getInventory().placeItemBackInInventory(blanks, false);
}
} catch (Throwable t) {
if (player != null) {
// 兜底直接还给玩家背包
player.getInventory().placeItemBackInInventory(AEItems.BLANK_PATTERN.stack(stack.getCount()), false);
}
}
// 清空编码样板槽防止再次输出
encodedSlot.set(ItemStack.EMPTY);
return false;
}
IGrid grid = node.getGrid();
if (grid == null) return;
int stackCount = stack.getCount();
ItemStack toInsert = stack.copy();
// 收集所有可用的装配矩阵图样模块内部库存并逐一尝试遵循其过滤规则
List<InternalInventory> inventories = findAllMatrixPatternInventories(grid);
if (!inventories.isEmpty()) {
for (int i = 0; i < inventories.size(); i++) {
var inv = inventories.get(i);
ItemStack toInsert = stack.copy();
ItemStack remain = inv.addItems(toInsert);
if (remain.getCount() < stack.getCount()) {
int inserted = stack.getCount() - remain.getCount();
stack.shrink(inserted);
if (stack.isEmpty()) {
encodedSlot.set(ItemStack.EMPTY);
}
sendMessage(player, "extendedae_plus.upload_to_matrix.success");
return true;
}
}
// 所有内部库存都无法接收 -> 尝试 capability 回退
}
// 回退尝试 Forge 能力可能为聚合图样仓同样遍历所有矩阵
List<IItemHandler> handlers = findAllMatrixPatternHandlers(grid);
if (!handlers.isEmpty()) {
for (int i = 0; i < handlers.size(); i++) {
var cap = handlers.get(i);
ItemStack toInsert = stack.copy();
ItemStack remain = insertIntoAnySlot(cap, toInsert);
if (remain.getCount() < stack.getCount()) {
int inserted = stack.getCount() - remain.getCount();
stack.shrink(inserted);
if (stack.isEmpty()) {
encodedSlot.set(ItemStack.EMPTY);
}
sendMessage(player, "extendedae_plus.upload_to_matrix.success");
return true;
}
// 在尝试上传之前检查装配矩阵是否已经存在相同样板物品与NBT完全一致
if (matrixContainsPattern(inventories, stack)) {
// 直接提醒并跳过上传并将同等数量的空白样板放回空白样板槽否则退回玩家背包
sendPlayerMessage(player, Component.translatable("extendedae_plus.upload_to_matrix.repetition"));
refundBlankPattern(player, menu, stackCount);
encodedSlot.set(ItemStack.EMPTY);
return;
}
// 尝试插入
for (InternalInventory inv : inventories) {
if (inv == null) continue;
ItemStack remain = inv.addItems(toInsert);
if (remain.getCount() < stackCount) {
completeUploadSuccess(player, encodedSlot, stack, remain);
return;
}
}
// 未找到可用矩阵或全部拒收
if (inventories.isEmpty() && handlers.isEmpty()) {
sendMessage(player, "extendedae_plus.upload_to_matrix.fail_no_matrix");
} else {
sendMessage(player, "extendedae_plus.upload_to_matrix.fail_full");
}
return false;
sendPlayerMessage(player,
inventories.isEmpty()
? Component.translatable("extendedae_plus.upload_to_matrix.fail_no_matrix")
: Component.translatable("extendedae_plus.upload_to_matrix.fail_full"));
}
/**
* 在给定 AE Grid 中收集所有已成型且在线的装配矩阵图样模块的用于外部插入的内部库存
* 优先使用 TileAssemblerMatrixPattern#getExposedInventory仅允许插入且已带AE过滤规则
* 在给定 AE Grid 中收集所有已成型且在线的装配矩阵样板核心的用于外部插入的内部库存
*/
private static List<InternalInventory> findAllMatrixPatternInventories(IGrid grid) {
List<InternalInventory> result = new ArrayList<>();
if (grid == null) return result;
try {
var tiles = grid.getMachines(TileAssemblerMatrixPattern.class);
for (TileAssemblerMatrixPattern tile : tiles) {
if (tile != null && tile.isFormed() && tile.getMainNode().isActive() && clusterHasSingleUploadCore(tile)) {
var inv = tile.getExposedInventory();
// 获取网络中所有 Pattern Tile
Set<TileAssemblerMatrixPattern> allTiles = grid.getMachines(TileAssemblerMatrixPattern.class);
// Set 记录已经扫描过的集群避免重复调用 clusterHasSingleUploadCore
Set<ClusterAssemblerMatrix> scannedClusters = new HashSet<>();
for (TileAssemblerMatrixPattern tile : allTiles) {
if (tile == null || !tile.isFormed() || !tile.getMainNode().isActive()) continue;
ClusterAssemblerMatrix cluster = tile.getCluster();
if (cluster == null) continue;
// 如果该集群已经扫描过或者该集群含 UploadCore则处理 tile
if (scannedClusters.contains(cluster) || clusterHasSingleUploadCore(cluster)) {
scannedClusters.add(cluster); // 标记为已扫描
InternalInventory inv = tile.getExposedInventory();
if (inv != null) {
result.add(inv);
}
}
}
} catch (Throwable t) {
}
} catch (Throwable ignored) {}
return result;
}
/**
* 在给定 AE Grid 中收集所有已成型的装配矩阵的聚合图样仓 IItemHandler若可用
* 检查装配矩阵所有已成型矩阵的样板核心中是否已存在与给定样板完全相同的物品含NBT
*/
private static List<IItemHandler> findAllMatrixPatternHandlers(IGrid grid) {
List<IItemHandler> result = new ArrayList<>();
try {
Set<TileAssemblerMatrixBase> matrices = grid.getMachines(TileAssemblerMatrixBase.class);
for (TileAssemblerMatrixBase tile : matrices) {
if (tile != null && tile.isFormed() && clusterHasSingleUploadCore(tile)) {
var capOpt = tile.getCapability(ForgeCapabilities.ITEM_HANDLER, null);
if (capOpt != null) {
var handler = capOpt.orElse(null);
if (handler != null) {
result.add(handler);
}
}
private static boolean matrixContainsPattern(@NotNull List<InternalInventory> inventories, @NotNull ItemStack pattern) {
for (InternalInventory inv : inventories) {
if (inv == null) continue;
for (int i = 0; i < inv.size(); i++) {
ItemStack s = inv.getStackInSlot(i);
if (!s.isEmpty() && ItemStack.isSameItemSameTags(s, pattern)) {
return true;
}
}
} catch (Throwable ignored) {
}
return result;
}
/**
* 检查装配矩阵所有已成型矩阵的图样仓中是否已存在与给定样板完全相同的物品含NBT
*/
private static boolean matrixContainsPattern(IGrid grid, ItemStack pattern) {
if (grid == null || pattern == null || pattern.isEmpty()) return false;
try {
// 先检查提供外部插入视图的内部库存
List<InternalInventory> inventories = findAllMatrixPatternInventories(grid);
for (InternalInventory inv : inventories) {
if (inv == null) continue;
for (int i = 0; i < inv.size(); i++) {
ItemStack s = inv.getStackInSlot(i);
if (!s.isEmpty() && ItemStack.isSameItemSameTags(s, pattern)) {
return true;
}
}
}
} catch (Throwable t) {
}
try {
// 再检查聚合能力视图
List<IItemHandler> handlers = findAllMatrixPatternHandlers(grid);
for (IItemHandler h : handlers) {
if (h == null) continue;
int slots = h.getSlots();
for (int i = 0; i < slots; i++) {
ItemStack s = h.getStackInSlot(i);
if (!s.isEmpty() && ItemStack.isSameItemSameTags(s, pattern)) {
return true;
}
}
}
} catch (Throwable t) {
}
return false;
}
/**
* 判断给定矩阵集群中是否存在装配矩阵上传核心
* 要求至少存在 1 个即可不限制数量
* 传入任意属于该集群的 Tile Pattern/Crafter/Frame
*/
private static boolean clusterHasSingleUploadCore(TileAssemblerMatrixBase any) {
private static boolean clusterHasSingleUploadCore(@NotNull ClusterAssemblerMatrix cluster) {
try {
if (any == null || any.getCluster() == null) return false;
int cores = 0;
var it = any.getCluster().getBlockEntities();
var it = cluster.getBlockEntities();
while (it.hasNext()) {
var te = it.next();
if (te instanceof UploadCoreBlockEntity) {
cores++;
}
if (it.next() instanceof UploadCoreBlockEntity) return true;
}
return cores >= 1; // 至少一个即可
} catch (Throwable t) {
return false;
} catch (Throwable ignored) {}
return false;
}
/**
* 上传成功后处理清空编码槽发送提示
*/
private static void completeUploadSuccess(ServerPlayer player, RestrictedInputSlot encodedSlot, ItemStack stack, ItemStack remain) {
int inserted = stack.getCount() - remain.getCount();
if (inserted > 0) {
stack.shrink(inserted);
if (stack.isEmpty()) encodedSlot.set(ItemStack.EMPTY);
sendPlayerMessage(player, Component.translatable("extendedae_plus.upload_to_matrix.success"));
}
}
/**
* 尝试将整个物品栈插入到 IItemHandler 的任意槽位返回剩余物品
* 当发现重复样板时返还空白样板
*/
private static ItemStack insertIntoAnySlot(IItemHandler handler, ItemStack stack) {
ItemStack remaining = stack.copy();
if (handler == null || remaining.isEmpty()) return remaining;
for (int i = 0; i < handler.getSlots(); i++) {
remaining = handler.insertItem(i, remaining, false);
if (remaining.isEmpty()) break;
private static void refundBlankPattern(ServerPlayer player, PatternEncodingTermMenu menu, int count) {
try {
var accessor = (PatternEncodingTermMenuAccessor) menu;
var blankSlot = accessor.eap$getBlankPatternSlot();
ItemStack blanks = AEItems.BLANK_PATTERN.stack(count);
if (blankSlot != null && blankSlot.mayPlace(blanks)) {
ItemStack remain = blankSlot.safeInsert(blanks);
if (!remain.isEmpty() && player != null) {
player.getInventory().placeItemBackInInventory(remain, false);
}
} else if (player != null) {
player.getInventory().placeItemBackInInventory(blanks, false);
}
} catch (Throwable t) {
if (player != null) {
player.getInventory().placeItemBackInInventory(AEItems.BLANK_PATTERN.stack(count), false);
}
}
return remaining;
}
}

View File

@ -49,6 +49,9 @@
"extendedae_plus.upload_to_matrix.fail_not_crafting": "仅支持上传合成样板,处理样板将被忽略",
"extendedae_plus.upload_to_matrix.fail_no_matrix": "未在当前网络中找到已成型的装配矩阵",
"extendedae_plus.upload_to_matrix.fail_full": "装配矩阵的样板仓已满或无法插入",
"extendedae_plus.upload_to_matrix.repetition": "装配矩阵已存在相同样板,已跳过上传并返还空白样板",
"extendedae_plus.screen.reload_mapping": "重载映射",
"extendedae_plus.screen.reload_mapping_success": "重载映射成功",
"extendedae_plus.screen.reload_mapping_fail": "重载映射失败: %s",