ME接口中按钮实现点击逻辑
This commit is contained in:
parent
224fa2a37b
commit
b3e129e03c
|
|
@ -97,6 +97,12 @@ public class ModNetwork {
|
|||
.consumerNetworkThread(ToggleEntityTickerC2SPacket::handle)
|
||||
.add();
|
||||
|
||||
CHANNEL.messageBuilder(InterfaceAdjustConfigAmountC2SPacket.class, nextId(), NetworkDirection.PLAY_TO_SERVER)
|
||||
.encoder(InterfaceAdjustConfigAmountC2SPacket::encode)
|
||||
.decoder(InterfaceAdjustConfigAmountC2SPacket::decode)
|
||||
.consumerNetworkThread(InterfaceAdjustConfigAmountC2SPacket::handle)
|
||||
.add();
|
||||
|
||||
CHANNEL.messageBuilder(AdvancedBlockingSyncS2CPacket.class, nextId(), NetworkDirection.PLAY_TO_CLIENT)
|
||||
.encoder(AdvancedBlockingSyncS2CPacket::encode)
|
||||
.decoder(AdvancedBlockingSyncS2CPacket::decode)
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package com.extendedae_plus.mixin.accessor;
|
|||
|
||||
import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen;
|
||||
import net.minecraft.world.inventory.AbstractContainerMenu;
|
||||
import net.minecraft.world.inventory.Slot;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.gen.Accessor;
|
||||
|
||||
|
|
@ -11,4 +12,5 @@ public interface AbstractContainerScreenAccessor<T extends AbstractContainerMenu
|
|||
@Accessor("topPos") int eap$getTopPos();
|
||||
@Accessor("imageWidth") int eap$getImageWidth();
|
||||
@Accessor("imageHeight") int eap$getImageHeight();
|
||||
@Accessor("hoveredSlot") Slot eap$getHoveredSlot();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,10 @@ import appeng.client.gui.AEBaseScreen;
|
|||
import appeng.client.gui.implementations.InterfaceScreen;
|
||||
import appeng.menu.AEBaseMenu;
|
||||
import com.extendedae_plus.NewIcon;
|
||||
import com.extendedae_plus.init.ModNetwork;
|
||||
import com.extendedae_plus.network.InterfaceAdjustConfigAmountC2SPacket;
|
||||
import appeng.menu.SlotSemantics;
|
||||
import net.minecraft.world.inventory.Slot;
|
||||
import com.extendedae_plus.mixin.accessor.AbstractContainerScreenAccessor;
|
||||
import com.extendedae_plus.mixin.accessor.ScreenAccessor;
|
||||
import com.glodblock.github.extendedae.client.button.ActionEPPButton;
|
||||
|
|
@ -34,6 +38,9 @@ public abstract class InterfaceScreenMixin<T extends AEBaseMenu> {
|
|||
@Unique private int eap$lastTopPos = -1;
|
||||
@Unique private int eap$lastImageWidth = -1;
|
||||
@Unique private int eap$lastImageHeight = -1;
|
||||
// 记录最近一次在 CONFIG 槽位区域内悬停到的配置槽索引,
|
||||
// 以便当鼠标移到按钮上导致 hoveredSlot 为空时仍能进行操作。
|
||||
@Unique private int eap$lastConfigIndex = -1;
|
||||
|
||||
@Inject(method = "init", at = @At("TAIL"))
|
||||
private void eap$addScaleButtons(CallbackInfo ci) {
|
||||
|
|
@ -45,42 +52,42 @@ public abstract class InterfaceScreenMixin<T extends AEBaseMenu> {
|
|||
// 避免重复创建
|
||||
if (eap$x2Button == null) {
|
||||
eap$x2Button = new ActionEPPButton((b) -> {
|
||||
// 点击逻辑留空
|
||||
eap$sendAdjustForAllConfigs(false, 2);
|
||||
}, NewIcon.MULTIPLY2);
|
||||
eap$x2Button.setTooltip(Tooltip.create(Component.translatable("extendedae_plus.button.multiply2")));
|
||||
eap$x2Button.setVisibility(true);
|
||||
}
|
||||
if (eap$divideBy2Button == null) {
|
||||
eap$divideBy2Button = new ActionEPPButton((b) -> {
|
||||
// 点击逻辑留空
|
||||
eap$sendAdjustForAllConfigs(true, 2);
|
||||
}, NewIcon.DIVIDE2);
|
||||
eap$divideBy2Button.setTooltip(Tooltip.create(Component.translatable("extendedae_plus.button.divide2")));
|
||||
eap$divideBy2Button.setVisibility(true);
|
||||
}
|
||||
if (eap$x5Button == null) {
|
||||
eap$x5Button = new ActionEPPButton((b) -> {
|
||||
// 点击逻辑留空
|
||||
eap$sendAdjustForAllConfigs(false, 5);
|
||||
}, NewIcon.MULTIPLY5);
|
||||
eap$x5Button.setTooltip(Tooltip.create(Component.translatable("extendedae_plus.button.multiply5")));
|
||||
eap$x5Button.setVisibility(true);
|
||||
}
|
||||
if (eap$divideBy5Button == null) {
|
||||
eap$divideBy5Button = new ActionEPPButton((b) -> {
|
||||
// 点击逻辑留空
|
||||
eap$sendAdjustForAllConfigs(true, 5);
|
||||
}, NewIcon.DIVIDE5);
|
||||
eap$divideBy5Button.setTooltip(Tooltip.create(Component.translatable("extendedae_plus.button.divide5")));
|
||||
eap$divideBy5Button.setVisibility(true);
|
||||
}
|
||||
if (eap$x10Button == null) {
|
||||
eap$x10Button = new ActionEPPButton((b) -> {
|
||||
// 点击逻辑留空
|
||||
eap$sendAdjustForAllConfigs(false, 10);
|
||||
}, NewIcon.MULTIPLY10);
|
||||
eap$x10Button.setTooltip(Tooltip.create(Component.translatable("extendedae_plus.button.multiply10")));
|
||||
eap$x10Button.setVisibility(true);
|
||||
}
|
||||
if (eap$divideBy10Button == null) {
|
||||
eap$divideBy10Button = new ActionEPPButton((b) -> {
|
||||
// 点击逻辑留空
|
||||
eap$sendAdjustForAllConfigs(true, 10);
|
||||
}, NewIcon.DIVIDE10);
|
||||
eap$divideBy10Button.setTooltip(Tooltip.create(Component.translatable("extendedae_plus.button.divide10")));
|
||||
eap$divideBy10Button.setVisibility(true);
|
||||
|
|
@ -156,6 +163,78 @@ public abstract class InterfaceScreenMixin<T extends AEBaseMenu> {
|
|||
eap$relayoutButtons();
|
||||
try { LogUtils.getLogger().info("[EAP][InterfaceMixin] relayout due to bounds change: left={}, top={}, w={}, h={}", curLeft, curTop, curImgW, curImgH); } catch (Throwable ignored) {}
|
||||
}
|
||||
// 每帧根据 hoveredSlot 刷新最近一次的配置槽索引
|
||||
eap$updateLastConfigFromHover();
|
||||
}
|
||||
|
||||
@Unique
|
||||
private void eap$sendAdjustForHoveredConfig(boolean divide, int factor) {
|
||||
try {
|
||||
// 仅在 InterfaceScreen 中生效
|
||||
if (!(((Object) this) instanceof InterfaceScreen)) {
|
||||
return;
|
||||
}
|
||||
// 获取悬停槽位
|
||||
Slot hovered = ((AbstractContainerScreenAccessor<?>) (Object) this).eap$getHoveredSlot();
|
||||
// 获取菜单与配置槽列表
|
||||
var screen = (AEBaseScreen<?>) (Object) this;
|
||||
var menu = screen.getMenu();
|
||||
if (!(menu instanceof appeng.menu.implementations.InterfaceMenu interfaceMenu)) {
|
||||
return;
|
||||
}
|
||||
var configSlots = interfaceMenu.getSlots(SlotSemantics.CONFIG);
|
||||
if (configSlots == null || configSlots.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 优先根据 hoveredSlot 解析索引;若 hovered 为空,则回退使用最近一次的配置槽索引
|
||||
Integer slotFieldObj = null;
|
||||
if (hovered != null) {
|
||||
for (var s : configSlots) {
|
||||
if (s == hovered) {
|
||||
// 反射读取 AppEngSlot#slot
|
||||
try {
|
||||
var f = s.getClass().getDeclaredField("slot");
|
||||
f.setAccessible(true);
|
||||
Object v = f.get(s);
|
||||
if (v instanceof Integer i) {
|
||||
slotFieldObj = i;
|
||||
}
|
||||
} catch (Throwable ignored) {}
|
||||
if (slotFieldObj == null) {
|
||||
// 回退:使用列表位置当作索引
|
||||
slotFieldObj = configSlots.indexOf(s);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
int slotField = -1;
|
||||
if (slotFieldObj != null) {
|
||||
slotField = slotFieldObj;
|
||||
} else if (eap$lastConfigIndex >= 0 && eap$lastConfigIndex < configSlots.size()) {
|
||||
slotField = eap$lastConfigIndex;
|
||||
try { LogUtils.getLogger().info("[EAP][InterfaceMixin] Using last hovered config index: {}", slotField); } catch (Throwable ignored) {}
|
||||
}
|
||||
if (slotField < 0) {
|
||||
try { LogUtils.getLogger().info("[EAP][InterfaceMixin] No hovered slot and no last config index; ignoring adjust."); } catch (Throwable ignored) {}
|
||||
return;
|
||||
}
|
||||
|
||||
// 发送到服务端
|
||||
ModNetwork.CHANNEL.sendToServer(new InterfaceAdjustConfigAmountC2SPacket(slotField, divide, factor));
|
||||
} catch (Throwable ignored) {}
|
||||
}
|
||||
|
||||
@Unique
|
||||
private void eap$sendAdjustForAllConfigs(boolean divide, int factor) {
|
||||
try {
|
||||
if (!(((Object) this) instanceof InterfaceScreen)) {
|
||||
return;
|
||||
}
|
||||
// 直接发送 -1 表示对所有 CONFIG 槽生效
|
||||
ModNetwork.CHANNEL.sendToServer(new InterfaceAdjustConfigAmountC2SPacket(-1, divide, factor));
|
||||
} catch (Throwable ignored) {}
|
||||
}
|
||||
|
||||
@Unique
|
||||
|
|
@ -176,4 +255,50 @@ public abstract class InterfaceScreenMixin<T extends AEBaseMenu> {
|
|||
if (eap$x10Button != null) { eap$x10Button.setX(bx); eap$x10Button.setY(by + spacing * 5); }
|
||||
} catch (Throwable ignored) {}
|
||||
}
|
||||
|
||||
@Unique
|
||||
private void eap$updateLastConfigFromHover() {
|
||||
try {
|
||||
if (!(((Object) this) instanceof InterfaceScreen)) {
|
||||
return;
|
||||
}
|
||||
Slot hovered = ((AbstractContainerScreenAccessor<?>) (Object) this).eap$getHoveredSlot();
|
||||
if (hovered == null) {
|
||||
return;
|
||||
}
|
||||
var screen = (AEBaseScreen<?>) (Object) this;
|
||||
var menu = screen.getMenu();
|
||||
if (!(menu instanceof appeng.menu.implementations.InterfaceMenu interfaceMenu)) {
|
||||
return;
|
||||
}
|
||||
var configSlots = interfaceMenu.getSlots(SlotSemantics.CONFIG);
|
||||
if (configSlots == null || configSlots.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
// 在 CONFIG 槽列表中定位 hovered 对应的索引
|
||||
Integer idx = null;
|
||||
for (var s : configSlots) {
|
||||
if (s == hovered) {
|
||||
try {
|
||||
var f = s.getClass().getDeclaredField("slot");
|
||||
f.setAccessible(true);
|
||||
Object v = f.get(s);
|
||||
if (v instanceof Integer i) {
|
||||
idx = i;
|
||||
}
|
||||
} catch (Throwable ignored) {}
|
||||
if (idx == null) {
|
||||
idx = configSlots.indexOf(s);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (idx != null && idx >= 0) {
|
||||
if (eap$lastConfigIndex != idx) {
|
||||
eap$lastConfigIndex = idx;
|
||||
try { LogUtils.getLogger().info("[EAP][InterfaceMixin] lastConfigIndex updated: {}", eap$lastConfigIndex); } catch (Throwable ignored) {}
|
||||
}
|
||||
}
|
||||
} catch (Throwable ignored) {}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,116 @@
|
|||
package com.extendedae_plus.network;
|
||||
|
||||
import appeng.menu.implementations.InterfaceMenu;
|
||||
import appeng.menu.SlotSemantics;
|
||||
import appeng.api.stacks.GenericStack;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraftforge.network.NetworkEvent;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* C2S:调整 ME 接口配置槽位(标记物品)的数量。
|
||||
* 支持按因子倍增或整除,且保持最小值为 1。
|
||||
*/
|
||||
public class InterfaceAdjustConfigAmountC2SPacket {
|
||||
private final int slotIndex; // 配置槽位索引(ConfigInventory 索引)
|
||||
private final boolean divide; // true 表示做除法,否则做乘法
|
||||
private final int factor; // 因子:2/5/10
|
||||
|
||||
public InterfaceAdjustConfigAmountC2SPacket(int slotIndex, boolean divide, int factor) {
|
||||
this.slotIndex = slotIndex;
|
||||
this.divide = divide;
|
||||
this.factor = factor;
|
||||
}
|
||||
|
||||
public static void encode(InterfaceAdjustConfigAmountC2SPacket msg, FriendlyByteBuf buf) {
|
||||
buf.writeVarInt(msg.slotIndex);
|
||||
buf.writeBoolean(msg.divide);
|
||||
buf.writeVarInt(msg.factor);
|
||||
}
|
||||
|
||||
public static InterfaceAdjustConfigAmountC2SPacket decode(FriendlyByteBuf buf) {
|
||||
int slot = buf.readVarInt();
|
||||
boolean div = buf.readBoolean();
|
||||
int factor = buf.readVarInt();
|
||||
return new InterfaceAdjustConfigAmountC2SPacket(slot, div, factor);
|
||||
|
||||
}
|
||||
|
||||
public static void handle(InterfaceAdjustConfigAmountC2SPacket msg, Supplier<NetworkEvent.Context> ctxSupplier) {
|
||||
var ctx = ctxSupplier.get();
|
||||
ctx.enqueueWork(() -> {
|
||||
ServerPlayer player = ctx.getSender();
|
||||
if (player == null) return;
|
||||
if (!(player.containerMenu instanceof InterfaceMenu menu)) return;
|
||||
|
||||
try {
|
||||
var logic = menu.getHost().getInterfaceLogic();
|
||||
var config = logic.getConfig();
|
||||
if (msg.slotIndex == -1) {
|
||||
// 对所有 CONFIG 槽位生效
|
||||
var configSlots = menu.getSlots(SlotSemantics.CONFIG);
|
||||
if (configSlots == null || configSlots.isEmpty()) return;
|
||||
for (var s : configSlots) {
|
||||
int idx = -1;
|
||||
try {
|
||||
var f = s.getClass().getDeclaredField("slot");
|
||||
f.setAccessible(true);
|
||||
Object v = f.get(s);
|
||||
if (v instanceof Integer i) idx = i;
|
||||
} catch (Throwable ignored) {}
|
||||
if (idx < 0) {
|
||||
idx = configSlots.indexOf(s);
|
||||
}
|
||||
|
||||
var st = config.getStack(idx);
|
||||
if (st == null) continue;
|
||||
|
||||
long current = st.amount();
|
||||
int factor = Math.max(1, msg.factor);
|
||||
long next;
|
||||
if (msg.divide) {
|
||||
if (factor <= 1) continue;
|
||||
if (current % factor != 0) continue;
|
||||
next = current / factor;
|
||||
if (next < 1) next = 1;
|
||||
} else {
|
||||
if (factor <= 1) continue;
|
||||
next = current * factor;
|
||||
if (next < 1) next = 1;
|
||||
}
|
||||
|
||||
GenericStack newStack = new GenericStack(st.what(), next);
|
||||
config.setStack(idx, newStack);
|
||||
}
|
||||
} else {
|
||||
var stack = config.getStack(msg.slotIndex);
|
||||
if (stack == null) return; // 槽位无标记
|
||||
|
||||
long current = stack.amount();
|
||||
int factor = Math.max(1, msg.factor);
|
||||
long next;
|
||||
if (msg.divide) {
|
||||
// 只能整除,且至少为 1
|
||||
if (factor <= 1) return;
|
||||
if (current % factor != 0) return; // 不能整除则跳过
|
||||
next = current / factor;
|
||||
if (next < 1) next = 1;
|
||||
} else {
|
||||
// 倍增,至少为 1
|
||||
if (factor <= 1) return;
|
||||
next = current * factor;
|
||||
if (next < 1) next = 1;
|
||||
}
|
||||
|
||||
// 应用
|
||||
GenericStack newStack = new GenericStack(stack.what(), next);
|
||||
config.setStack(msg.slotIndex, newStack);
|
||||
}
|
||||
// 不需要显式保存:InterfaceLogic.config 的变更监听器会触发 host.saveChanges() 与计划更新
|
||||
} catch (Throwable ignored) {}
|
||||
});
|
||||
ctx.setPacketHandled(true);
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user