增加合成监控界面shift左键打开机器ui
This commit is contained in:
parent
3540c52676
commit
a75d7fbc5c
|
|
@ -21,7 +21,7 @@ import java.util.List;
|
|||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* 参照 MAE2 的 DynamicCraftingCubeModelProvider,实现形成态光照模型。
|
||||
* 形成态光照模型。
|
||||
*/
|
||||
public class EPlusCraftingCubeModelProvider
|
||||
extends AbstractCraftingUnitModelProvider<EPlusCraftingUnitType> {
|
||||
|
|
@ -29,13 +29,13 @@ public class EPlusCraftingCubeModelProvider
|
|||
public static final ChunkRenderTypeSet CUTOUT = ChunkRenderTypeSet.of(RenderType.cutout());
|
||||
private static final List<Material> MATERIALS = new ArrayList<>();
|
||||
|
||||
// 与 MAE2 一致:将环形边框与基础发光底图放在本模组命名空间
|
||||
//将环形边框与基础发光底图放在本模组命名空间
|
||||
protected static final Material RING_CORNER = texture(ExtendedAEPlus.MODID, "ring_corner");
|
||||
protected static final Material RING_SIDE_HOR = texture(ExtendedAEPlus.MODID, "ring_side_hor");
|
||||
protected static final Material RING_SIDE_VER = texture(ExtendedAEPlus.MODID, "ring_side_ver");
|
||||
protected static final Material LIGHT_BASE = texture(ExtendedAEPlus.MODID, "light_base");
|
||||
|
||||
// 我们自己的亮面贴图(formed 时使用)
|
||||
// 亮面贴图(formed 时使用)
|
||||
protected static final Material ACCELERATOR_4X_LIGHT = texture(ExtendedAEPlus.MODID,
|
||||
"4x_accelerator_light");
|
||||
protected static final Material ACCELERATOR_16X_LIGHT = texture(ExtendedAEPlus.MODID,
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import appeng.menu.slot.AppEngSlot;
|
|||
import com.extendedae_plus.api.ExPatternPageAccessor;
|
||||
import com.extendedae_plus.network.CraftingMonitorJumpC2SPacket;
|
||||
import com.extendedae_plus.network.ModNetwork;
|
||||
import com.extendedae_plus.network.CraftingMonitorOpenProviderC2SPacket;
|
||||
import com.extendedae_plus.util.GuiUtil;
|
||||
import com.glodblock.github.extendedae.client.gui.GuiExPatternProvider;
|
||||
import com.mojang.logging.LogUtils;
|
||||
|
|
@ -79,6 +80,40 @@ public abstract class AEBaseScreenMixin {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 在 AEBaseScreen 的 mouseClicked 入口拦截 CraftingCPUScreen 的 Shift+右键,
|
||||
* 读取鼠标下的 AEKey 并发送 CraftingMonitorOpenProviderC2SPacket(打开样板供应器UI)。
|
||||
*/
|
||||
@Inject(method = "mouseClicked", at = @At("HEAD"), cancellable = true)
|
||||
private void eap$craftingCpuShiftRightClick(double mouseX, double mouseY, int button, CallbackInfoReturnable<Boolean> cir) {
|
||||
// 仅处理 CraftingCPUScreen 实例
|
||||
Object self = this;
|
||||
if (!(self instanceof CraftingCPUScreen<?> screen)) {
|
||||
return;
|
||||
}
|
||||
// 仅在 Shift + 右键 时触发
|
||||
if (button != 1 || !net.minecraft.client.gui.screens.Screen.hasShiftDown()) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
StackWithBounds hovered = screen.getStackUnderMouse(mouseX, mouseY);
|
||||
if (hovered == null || hovered.stack() == null) {
|
||||
return;
|
||||
}
|
||||
AEKey key = hovered.stack().what();
|
||||
if (key == null) {
|
||||
return;
|
||||
}
|
||||
// Debug: 标记一次发送(打开供应器UI)
|
||||
try {
|
||||
LogUtils.getLogger().info("EAP: Send CraftingMonitorOpenProviderC2SPacket: {}", key);
|
||||
} catch (Throwable ignored2) {}
|
||||
ModNetwork.CHANNEL.sendToServer(new CraftingMonitorOpenProviderC2SPacket(key));
|
||||
cir.setReturnValue(true);
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
@Unique
|
||||
private static int eap$getIntField(Object self, String name, int def) {
|
||||
Class<?> c = self.getClass();
|
||||
|
|
|
|||
|
|
@ -1,12 +0,0 @@
|
|||
package com.extendedae_plus.mixin.ae2.accessor;
|
||||
|
||||
import appeng.api.networking.IGrid;
|
||||
import appeng.menu.me.crafting.CraftingCPUMenu;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.gen.Accessor;
|
||||
|
||||
@Mixin(CraftingCPUMenu.class)
|
||||
public interface CraftingCPUMenuAccessor {
|
||||
@Accessor("grid")
|
||||
IGrid getGrid();
|
||||
}
|
||||
|
|
@ -8,9 +8,7 @@ import appeng.api.stacks.AEKey;
|
|||
import appeng.helpers.patternprovider.PatternProviderLogic;
|
||||
import appeng.helpers.patternprovider.PatternProviderLogicHost;
|
||||
import appeng.me.service.CraftingService;
|
||||
import appeng.menu.me.crafting.CraftingCPUMenu;
|
||||
import com.extendedae_plus.mixin.ae2.accessor.PatternProviderLogicAccessor;
|
||||
import com.extendedae_plus.mixin.ae2.accessor.CraftingCPUMenuAccessor;
|
||||
import com.mojang.logging.LogUtils;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
|
|
@ -59,12 +57,17 @@ public class CraftingMonitorJumpC2SPacket {
|
|||
LogUtils.getLogger().info("EAP[S]: recv CraftingMonitorJumpC2SPacket key={} from {}", msg.what, player.getGameProfile().getName());
|
||||
|
||||
// 必须在 CraftingCPU 界面内
|
||||
if (!(player.containerMenu instanceof CraftingCPUMenu menu)) {
|
||||
if (!(player.containerMenu instanceof appeng.menu.me.crafting.CraftingCPUMenu menu)) {
|
||||
LogUtils.getLogger().info("EAP[S]: not in CraftingCPUMenu, abort");
|
||||
return;
|
||||
}
|
||||
// 直接通过 accessor 从菜单获取 Grid,避免对方块实体/level 的依赖
|
||||
IGrid grid = ((CraftingCPUMenuAccessor) menu).getGrid();
|
||||
|
||||
// 通过菜单 target(可能是 BlockEntity/Part/ItemHost)按 IActionHost 获取 Grid
|
||||
IGrid grid = null;
|
||||
Object target = ((appeng.menu.AEBaseMenu) menu).getTarget();
|
||||
if (target instanceof IActionHost host && host.getActionableNode() != null) {
|
||||
grid = host.getActionableNode().getGrid();
|
||||
}
|
||||
if (grid == null) {
|
||||
LogUtils.getLogger().info("EAP[S]: grid is null, abort");
|
||||
return;
|
||||
|
|
@ -97,8 +100,7 @@ public class CraftingMonitorJumpC2SPacket {
|
|||
PatternProviderLogicHost host = ((PatternProviderLogicAccessor) ppl).eap$host();
|
||||
if (host == null) continue;
|
||||
var pbe = host.getBlockEntity();
|
||||
var level = pbe.getLevel();
|
||||
if (!(level instanceof ServerLevel serverLevel)) continue;
|
||||
ServerLevel serverLevel = player.serverLevel();
|
||||
|
||||
// 尝试对邻居打开 GUI(复用 OpenProviderUiC2SPacket 的策略)
|
||||
for (Direction dir : host.getTargets()) {
|
||||
|
|
@ -120,29 +122,26 @@ public class CraftingMonitorJumpC2SPacket {
|
|||
}
|
||||
}
|
||||
|
||||
// 兜底:若无 MenuProvider,模拟徒手右键一次(优先有方块实体的面)
|
||||
boolean anyHandEmpty = player.getMainHandItem().isEmpty() || player.getOffhandItem().isEmpty();
|
||||
if (anyHandEmpty) {
|
||||
InteractionHand hand = player.getMainHandItem().isEmpty() ? InteractionHand.MAIN_HAND : InteractionHand.OFF_HAND;
|
||||
Direction chosen = null;
|
||||
// 兜底:若无 MenuProvider,始终模拟一次右键(优先有方块实体的一面)
|
||||
InteractionHand hand = player.getMainHandItem().isEmpty() ? InteractionHand.MAIN_HAND : InteractionHand.MAIN_HAND;
|
||||
Direction chosen = null;
|
||||
for (Direction d : host.getTargets()) {
|
||||
if (serverLevel.getBlockEntity(pbe.getBlockPos().relative(d)) != null) { chosen = d; break; }
|
||||
}
|
||||
if (chosen == null) {
|
||||
for (Direction d : host.getTargets()) {
|
||||
if (serverLevel.getBlockEntity(pbe.getBlockPos().relative(d)) != null) { chosen = d; break; }
|
||||
if (!serverLevel.getBlockState(pbe.getBlockPos().relative(d)).isAir()) { chosen = d; break; }
|
||||
}
|
||||
if (chosen == null) {
|
||||
for (Direction d : host.getTargets()) {
|
||||
if (!serverLevel.getBlockState(pbe.getBlockPos().relative(d)).isAir()) { chosen = d; break; }
|
||||
}
|
||||
}
|
||||
if (chosen != null) {
|
||||
BlockPos targetPos = pbe.getBlockPos().relative(chosen);
|
||||
var state2 = serverLevel.getBlockState(targetPos);
|
||||
var hit = new BlockHitResult(Vec3.atCenterOf(targetPos), chosen.getOpposite(), targetPos, false);
|
||||
InteractionResult r = state2.use(serverLevel, player, hand, hit);
|
||||
if (r.consumesAction()) {
|
||||
LogUtils.getLogger().info("EAP[S]: opened via simulated use at {} ({}), result={}", targetPos, chosen, r);
|
||||
context.setPacketHandled(true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (chosen != null) {
|
||||
BlockPos targetPos = pbe.getBlockPos().relative(chosen);
|
||||
var state2 = serverLevel.getBlockState(targetPos);
|
||||
var hit = new BlockHitResult(Vec3.atCenterOf(targetPos), chosen.getOpposite(), targetPos, false);
|
||||
InteractionResult r = state2.use(serverLevel, player, hand, hit);
|
||||
LogUtils.getLogger().info("EAP[S]: simulated use on {}, face={}, result={}", targetPos, chosen, r);
|
||||
if (r.consumesAction()) {
|
||||
context.setPacketHandled(true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,115 @@
|
|||
package com.extendedae_plus.network;
|
||||
|
||||
import appeng.api.crafting.IPatternDetails;
|
||||
import appeng.api.networking.IGrid;
|
||||
import appeng.api.networking.security.IActionHost;
|
||||
import appeng.api.stacks.AEKey;
|
||||
import appeng.helpers.patternprovider.PatternProviderLogic;
|
||||
import appeng.helpers.patternprovider.PatternProviderLogicHost;
|
||||
import appeng.me.service.CraftingService;
|
||||
import appeng.menu.AEBaseMenu;
|
||||
import appeng.menu.me.crafting.CraftingCPUMenu;
|
||||
import appeng.menu.locator.MenuLocators;
|
||||
import com.extendedae_plus.mixin.ae2.accessor.PatternProviderLogicAccessor;
|
||||
import com.mojang.logging.LogUtils;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraftforge.network.NetworkEvent;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* 客户端从 CraftingCPUScreen 发送:鼠标下条目对应的 AEKey。
|
||||
* 服务端在当前打开的 CraftingCPUMenu 所属网络中,定位匹配该 AEKey 的样板供应器,
|
||||
* 打开该供应器自身的 UI(不是目标机器的 UI)。
|
||||
*/
|
||||
public class CraftingMonitorOpenProviderC2SPacket {
|
||||
private final AEKey what;
|
||||
|
||||
public CraftingMonitorOpenProviderC2SPacket(AEKey what) {
|
||||
this.what = what;
|
||||
}
|
||||
|
||||
public static void encode(CraftingMonitorOpenProviderC2SPacket msg, FriendlyByteBuf buf) {
|
||||
AEKey.writeKey(buf, msg.what);
|
||||
}
|
||||
|
||||
public static CraftingMonitorOpenProviderC2SPacket decode(FriendlyByteBuf buf) {
|
||||
AEKey key = AEKey.readKey(buf);
|
||||
return new CraftingMonitorOpenProviderC2SPacket(key);
|
||||
}
|
||||
|
||||
public static void handle(CraftingMonitorOpenProviderC2SPacket msg, Supplier<NetworkEvent.Context> ctx) {
|
||||
NetworkEvent.Context context = ctx.get();
|
||||
context.enqueueWork(() -> {
|
||||
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;
|
||||
}
|
||||
|
||||
// 通过菜单的 target(可能是 BlockEntity/Part/ItemHost),按 IActionHost 获取 Grid
|
||||
IGrid grid = null;
|
||||
Object target = ((AEBaseMenu) menu).getTarget();
|
||||
if (target instanceof IActionHost host && host.getActionableNode() != null) {
|
||||
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;
|
||||
}
|
||||
|
||||
// 2) 遍历提供该样板的 Provider,定位 PatternProviderLogic
|
||||
for (var pattern : patterns) {
|
||||
var providers = craftingService.getProviders(pattern);
|
||||
for (var provider : providers) {
|
||||
if (provider instanceof PatternProviderLogic ppl) {
|
||||
// accessor 获取 host
|
||||
PatternProviderLogicHost host = ((PatternProviderLogicAccessor) ppl).eap$host();
|
||||
if (host == null) continue;
|
||||
var pbe = host.getBlockEntity();
|
||||
if (pbe == null) continue;
|
||||
// 在服务端上下文中执行,pbe 仅用于构造菜单定位器
|
||||
|
||||
// 直接打开供应器自身的 UI(调用 Host 默认方法)
|
||||
try {
|
||||
// 部件与方块实体分别选择定位器
|
||||
if (host instanceof appeng.parts.AEBasePart part) {
|
||||
host.openMenu(player, MenuLocators.forPart(part));
|
||||
} else {
|
||||
host.openMenu(player, MenuLocators.forBlockEntity(pbe));
|
||||
}
|
||||
context.setPacketHandled(true);
|
||||
return;
|
||||
} catch (Throwable t) {
|
||||
LogUtils.getLogger().error("EAP[S]: open provider UI failed at {}", pbe.getBlockPos(), t);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LogUtils.getLogger().info("EAP[S]: no provider UI opened for key={}", msg.what);
|
||||
});
|
||||
context.setPacketHandled(true);
|
||||
}
|
||||
}
|
||||
|
|
@ -77,6 +77,12 @@ public class ModNetwork {
|
|||
.decoder(CraftingMonitorJumpC2SPacket::decode)
|
||||
.consumerNetworkThread(CraftingMonitorJumpC2SPacket::handle)
|
||||
.add();
|
||||
|
||||
CHANNEL.messageBuilder(CraftingMonitorOpenProviderC2SPacket.class, nextId(), NetworkDirection.PLAY_TO_SERVER)
|
||||
.encoder(CraftingMonitorOpenProviderC2SPacket::encode)
|
||||
.decoder(CraftingMonitorOpenProviderC2SPacket::decode)
|
||||
.consumerNetworkThread(CraftingMonitorOpenProviderC2SPacket::handle)
|
||||
.add();
|
||||
}
|
||||
|
||||
private static int nextId() { return id++; }
|
||||
|
|
|
|||
|
|
@ -31,7 +31,6 @@
|
|||
"ae2.PatternEncodingTermMenuMixin",
|
||||
"ae2.PatternProviderLogicAdvancedMixin",
|
||||
"ae2.PatternProviderMenuAdvancedMixin",
|
||||
"ae2.accessor.CraftingCPUMenuAccessor",
|
||||
"ae2.accessor.MEStorageMenuAccessor",
|
||||
"ae2.accessor.PatternEncodingTermMenuAccessor",
|
||||
"ae2.accessor.PatternProviderLogicAccessor",
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user