fix:修复制作界面跳转样板可能打开未活跃供应器问题

This commit is contained in:
C-H716 2025-09-02 19:24:04 +08:00
parent 1ca8c8c48e
commit 8fec6fff87
2 changed files with 84 additions and 117 deletions

View File

@ -13,7 +13,6 @@ import appeng.menu.me.crafting.CraftingCPUMenu;
import appeng.parts.AEBasePart;
import com.extendedae_plus.mixin.ae2.accessor.PatternProviderLogicAccessor;
import com.extendedae_plus.util.PatternProviderDataUtil;
import com.mojang.logging.LogUtils;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.server.level.ServerPlayer;
import net.minecraftforge.network.NetworkDirection;
@ -49,11 +48,8 @@ public class CraftingMonitorOpenProviderC2SPacket {
ServerPlayer player = context.getSender();
if (player == null) return;
LogUtils.getLogger().info("EAP[S]: recv CraftingMonitorOpenProviderC2SPacket key={} from {}", msg.what, player.getGameProfile().getName());
// 必须在 CraftingCPU 界面内
if (!(player.containerMenu instanceof CraftingCPUMenu menu)) {
LogUtils.getLogger().info("EAP[S]: not in CraftingCPUMenu, abort");
return;
}
@ -64,19 +60,16 @@ public class CraftingMonitorOpenProviderC2SPacket {
grid = host.getActionableNode().getGrid();
}
if (grid == null) {
LogUtils.getLogger().info("EAP[S]: grid is null, abort");
return;
}
var cs = grid.getCraftingService();
if (!(cs instanceof CraftingService craftingService)) {
LogUtils.getLogger().info("EAP[S]: craftingService is null/unsupported, abort");
return;
}
// 1) 根据 AEKey 找到可能的样板pattern
Collection<IPatternDetails> patterns = craftingService.getCraftingFor(msg.what);
LogUtils.getLogger().info("EAP[S]: patterns found={} for key={}", patterns.size(), msg.what);
if (patterns.isEmpty()) {
return;
}
@ -91,14 +84,35 @@ public class CraftingMonitorOpenProviderC2SPacket {
if (host == null) continue;
var pbe = host.getBlockEntity();
if (pbe == null) continue;
// 在服务端上下文中执行pbe 仅用于构造菜单定位器
// 跳过未连接到网格或不活跃的 provider例如缺少频道/通道
try {
var providerGrid = ppl.getGrid();
if (providerGrid == null || !providerGrid.equals(grid)) {
continue;
}
// 如果 provider 自身对外提供的通道/频道信息不可用或不活跃跳过
try {
// 尝试通过 provider 的主节点判断是否有 channel
var mainNodeField = ppl.getClass().getDeclaredField("mainNode");
mainNodeField.setAccessible(true);
var mainNode = mainNodeField.get(ppl);
if (mainNode == null) continue;
var getChannelsMethod = mainNode.getClass().getMethod("getChannels");
Object channels = null;
channels = getChannelsMethod.invoke(mainNode);
if (channels instanceof java.util.Collection) {
if (((java.util.Collection<?>) channels).isEmpty()) continue;
}
} catch (Exception ignored) {
// 无法判断 channel 时继续不因反射失败而阻止正常 provider
}
} catch (Exception e) {
continue;
}
// 直接打开供应器自身的 UI调用 Host 默认方法
try {
// 告知目标玩家客户端高亮该 AEKey避免全局服务端状态污染
AEKey key = pattern.getOutputs()[0].what();
ModNetwork.CHANNEL.sendTo(new SetPatternHighlightS2CPacket(key, true), player.connection.connection, NetworkDirection.PLAY_TO_CLIENT);
// 部件与方块实体分别选择定位器并打开界面
if (host instanceof AEBasePart part) {
host.openMenu(player, MenuLocators.forPart(part));
@ -106,18 +120,8 @@ public class CraftingMonitorOpenProviderC2SPacket {
host.openMenu(player, MenuLocators.forBlockEntity(pbe));
}
// 先在该 provider 中定位 pattern 的槽位索引以便计算页码
int foundSlot = -1;
var list = PatternProviderDataUtil.getAllPatternData(ppl);
for (var pd : list) {
if (pd != null && pd.getPatternDetails() != null
&& pd.getPatternDetails().getDefinition().equals(pattern.getDefinition())) {
foundSlot = pd.getSlotIndex();
break;
}
}
// 先在该 provider 中定位 pattern 的槽位索引以便计算页码尽量早退出按槽位逐个解码
int foundSlot = findSlotIndexInProvider(ppl, pattern);
if (foundSlot >= 0) {
int pageId = foundSlot / 36;
if (pageId > 0) {
@ -125,19 +129,36 @@ public class CraftingMonitorOpenProviderC2SPacket {
ModNetwork.CHANNEL.sendTo(new SetProviderPageS2CPacket(pageId), player.connection.connection, NetworkDirection.PLAY_TO_CLIENT);
}
}
context.setPacketHandled(true);
// 最后发送高亮包保证界面已打开
if (pattern.getOutputs() != null && pattern.getOutputs().length > 0 && pattern.getOutputs()[0] != null) {
AEKey key = pattern.getOutputs()[0].what();
ModNetwork.CHANNEL.sendTo(new SetPatternHighlightS2CPacket(key, true), player.connection.connection, NetworkDirection.PLAY_TO_CLIENT);
}
return;
} catch (Throwable t) {
LogUtils.getLogger().error("EAP[S]: open provider UI failed at {}", pbe.getBlockPos(), t);
} catch (Exception ignored) {
}
}
}
}
LogUtils.getLogger().info("EAP[S]: no provider UI opened for key={}", msg.what);
});
context.setPacketHandled(true);
}
private static int findSlotIndexInProvider(PatternProviderLogic ppl, IPatternDetails pattern) {
try {
// 通过逐槽位解码并在找到匹配定义时立即返回索引避免分配大量对象
var list = PatternProviderDataUtil.getAllPatternData(ppl);
for (var pd : list) {
if (pd != null && pd.getPatternDetails() != null && pd.getPatternDetails().getDefinition().equals(pattern.getDefinition())) {
return pd.getSlotIndex();
}
}
} catch (Exception ignored) {
}
return -1;
}
}

View File

@ -6,8 +6,11 @@ import appeng.api.inventories.InternalInventory;
import appeng.api.stacks.AEKey;
import appeng.api.stacks.GenericStack;
import appeng.helpers.patternprovider.PatternProviderLogic;
import com.extendedae_plus.mixin.ae2.accessor.PatternProviderLogicAccessor;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Level;
import appeng.helpers.patternprovider.PatternProviderLogicHost;
import net.minecraft.world.level.block.entity.BlockEntity;
import java.util.ArrayList;
import java.util.HashMap;
@ -230,26 +233,8 @@ public class PatternProviderDataUtil {
if (patternInventory == null) {
return patternDataList;
}
// 通过反射安全地访问host字段获取Level
Level level = null;
try {
var hostField = patternProvider.getClass().getDeclaredField("host");
hostField.setAccessible(true);
var host = hostField.get(patternProvider);
if (host != null) {
var getBlockEntityMethod = host.getClass().getMethod("getBlockEntity");
var blockEntity = getBlockEntityMethod.invoke(host);
if (blockEntity != null) {
var getLevelMethod = blockEntity.getClass().getMethod("getLevel");
level = (Level) getLevelMethod.invoke(blockEntity);
}
}
} catch (Exception e) {
// 如果反射失败返回空列表
return patternDataList;
}
// 获取 Level使用 mixin accessor 替代反射
Level level = getPatternProviderLevel(patternProvider);
if (level == null) {
return patternDataList;
}
@ -353,24 +338,7 @@ public class PatternProviderDataUtil {
return null;
}
// 通过反射安全地访问host字段获取Level
Level level = null;
try {
var hostField = patternProvider.getClass().getDeclaredField("host");
hostField.setAccessible(true);
var host = hostField.get(patternProvider);
if (host != null) {
var getBlockEntityMethod = host.getClass().getMethod("getBlockEntity");
var blockEntity = getBlockEntityMethod.invoke(host);
if (blockEntity != null) {
var getLevelMethod = blockEntity.getClass().getMethod("getLevel");
level = (Level) getLevelMethod.invoke(blockEntity);
}
}
} catch (Exception e) {
return null;
}
Level level = getPatternProviderLevel(patternProvider);
if (level == null) {
return null;
}
@ -862,50 +830,30 @@ public class PatternProviderDataUtil {
// 1. 设置物品到库存
patternInventory.setItemDirect(slot, newPattern);
// 2. 标记数据为脏数据确保保存到磁盘
// 2. 标记数据为脏数据确保保存到磁盘尝试使用 mixin accessor 替代反射
try {
// 通过反射获取host并标记为脏数据
var hostField = patternProvider.getClass().getDeclaredField("host");
hostField.setAccessible(true);
var host = hostField.get(patternProvider);
if (host != null) {
// 获取BlockEntity并标记为脏数据
var getBlockEntityMethod = host.getClass().getMethod("getBlockEntity");
var blockEntity = getBlockEntityMethod.invoke(host);
if (blockEntity != null) {
// 调用setChanged()方法标记为脏数据
var setChangedMethod = blockEntity.getClass().getMethod("setChanged");
setChangedMethod.invoke(blockEntity);
// 尝试触发网络同步
try {
var levelField = blockEntity.getClass().getSuperclass().getDeclaredField("level");
levelField.setAccessible(true);
Level level = (Level) levelField.get(blockEntity);
if (level != null && !level.isClientSide()) {
// 服务器端强制同步到客户端
var getBlockPosMethod = blockEntity.getClass().getMethod("getBlockPos");
var blockPos = getBlockPosMethod.invoke(blockEntity);
if (blockPos != null) {
// 通知客户端方块状态变更
var getBlockStateMethod = blockEntity.getClass().getMethod("getBlockState");
var blockState = getBlockStateMethod.invoke(blockEntity);
level.sendBlockUpdated((net.minecraft.core.BlockPos) blockPos,
(net.minecraft.world.level.block.state.BlockState) blockState,
(net.minecraft.world.level.block.state.BlockState) blockState, 3);
}
if (patternProvider instanceof PatternProviderLogicAccessor accessor) {
var host = accessor.eap$host();
if (host != null) {
BlockEntity be = host.getBlockEntity();
if (be != null) {
try {
be.setChanged();
} catch (Exception ignored) {
}
try {
Level level = be.getLevel();
if (level != null && !level.isClientSide()) {
var pos = be.getBlockPos();
var state = be.getBlockState();
level.sendBlockUpdated(pos, state, state, 3);
}
} catch (Exception ignored) {
}
} catch (Exception syncException) {
// 网络同步失败不影响主要功能
}
}
}
} catch (Exception e) {
// 如果反射失败使用备用方案
} catch (Exception ignored) {
}
// 3. 强制更新样板缓存
@ -920,20 +868,18 @@ public class PatternProviderDataUtil {
* ExtendedAE风格安全获取样板供应器的Level对象
*/
private static Level getPatternProviderLevel(PatternProviderLogic patternProvider) {
if (patternProvider == null) return null;
try {
var hostField = patternProvider.getClass().getDeclaredField("host");
hostField.setAccessible(true);
var host = hostField.get(patternProvider);
if (host != null) {
var getBlockEntityMethod = host.getClass().getMethod("getBlockEntity");
var blockEntity = getBlockEntityMethod.invoke(host);
if (blockEntity != null) {
var getLevelMethod = blockEntity.getClass().getMethod("getLevel");
return (Level) getLevelMethod.invoke(blockEntity);
if (patternProvider instanceof PatternProviderLogicAccessor accessor) {
var host = accessor.eap$host();
if (host != null) {
BlockEntity be = host.getBlockEntity();
if (be != null) {
return be.getLevel();
}
}
}
} catch (Exception e) {
// 静默处理异常返回null让调用者处理
} catch (Exception ignored) {
}
return null;
}