Merge remote-tracking branch 'origin/master' into feature/pattern_display
# Conflicts: # src/main/java/com/extendedae_plus/util/ExtendedAELogger.java
This commit is contained in:
commit
b8da83b902
|
|
@ -16,6 +16,15 @@ loom {
|
|||
forge {
|
||||
mixinConfig 'extendedae_plus.mixins.json'
|
||||
}
|
||||
|
||||
runs {
|
||||
client1 {
|
||||
client()
|
||||
name "Client 1"
|
||||
runDir "run/client1"
|
||||
programArgs "--username", "Player1"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
repositories {
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ org.gradle.jvmargs=-Xmx1G
|
|||
loom.platform = forge
|
||||
|
||||
# Mod properties
|
||||
mod_version = 1.3.2-beta
|
||||
mod_version = 1.3.2-fix1
|
||||
maven_group = com.extendedae_plus
|
||||
archives_name = extendedae_plus
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,11 @@
|
|||
package com.extendedae_plus.api;
|
||||
|
||||
/**
|
||||
* 由 {@code GuiExPatternProviderMixin} 实现,用于从通用的 Screen Mixin 中更新按钮布局。
|
||||
*/
|
||||
public interface ExPatternButtonsAccessor {
|
||||
/**
|
||||
* 在每帧调用以维护扩展样板供应器右侧按钮的可见性、重注册(窗口尺寸变化)与定位。
|
||||
*/
|
||||
void eap$updateButtonsLayout();
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
package com.extendedae_plus.api;
|
||||
|
||||
/**
|
||||
* 由 GuiExPatternProviderMixin 实现,用于在客户端侧提供当前页号,避免反射读取 AE2 内部字段失败。
|
||||
*/
|
||||
public interface ExPatternPageAccessor {
|
||||
int eap$getCurrentPage();
|
||||
}
|
||||
|
|
@ -0,0 +1,176 @@
|
|||
package com.extendedae_plus.mixin.ae2;
|
||||
|
||||
import appeng.client.gui.AEBaseScreen;
|
||||
import appeng.client.gui.TextOverride;
|
||||
import appeng.client.Point;
|
||||
import appeng.client.gui.style.PaletteColor;
|
||||
import appeng.client.gui.style.ScreenStyle;
|
||||
import appeng.client.gui.style.Text;
|
||||
import appeng.client.gui.style.TextAlignment;
|
||||
import com.extendedae_plus.api.ExPatternPageAccessor;
|
||||
import com.glodblock.github.extendedae.client.gui.GuiExPatternProvider;
|
||||
import net.minecraft.client.gui.Font;
|
||||
import net.minecraft.client.gui.GuiGraphics;
|
||||
import net.minecraft.client.renderer.Rect2i;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.network.chat.contents.TranslatableContents;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
@Mixin(AEBaseScreen.class)
|
||||
public abstract class AEBaseScreenMixin {
|
||||
|
||||
@Unique
|
||||
private ScreenStyle eap$getStyle(Object self) {
|
||||
try {
|
||||
var f = self.getClass().getDeclaredField("style");
|
||||
f.setAccessible(true);
|
||||
Object v = f.get(self);
|
||||
if (v instanceof ScreenStyle s) return s;
|
||||
} catch (Throwable ignored) {}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Unique
|
||||
private static int eap$getIntField(Object self, String name, int def) {
|
||||
Class<?> c = self.getClass();
|
||||
while (c != null && c != Object.class) {
|
||||
try {
|
||||
var f = c.getDeclaredField(name);
|
||||
f.setAccessible(true);
|
||||
Object v = f.get(self);
|
||||
if (v instanceof Integer i) return i;
|
||||
} catch (Throwable ignored) {}
|
||||
c = c.getSuperclass();
|
||||
}
|
||||
return def;
|
||||
}
|
||||
|
||||
@Unique
|
||||
private static Font eap$getFont(Object self) {
|
||||
Class<?> c = self.getClass();
|
||||
while (c != null && c != Object.class) {
|
||||
try {
|
||||
var f = c.getDeclaredField("font");
|
||||
f.setAccessible(true);
|
||||
Object v = f.get(self);
|
||||
if (v instanceof Font font) return font;
|
||||
} catch (Throwable ignored) {}
|
||||
c = c.getSuperclass();
|
||||
}
|
||||
return net.minecraft.client.Minecraft.getInstance().font;
|
||||
}
|
||||
|
||||
// 在 AEBaseScreen.drawText 完成某个文本绘制后,若该文本为“样板”标签,则紧接着绘制页码。
|
||||
@Inject(method = "drawText", at = @At("TAIL"), remap = false)
|
||||
private void eap$appendPageAfterPatternsLabel(GuiGraphics guiGraphics,
|
||||
Text text,
|
||||
@Nullable TextOverride override,
|
||||
CallbackInfo ci) {
|
||||
Object self = this;
|
||||
if (!(self instanceof GuiExPatternProvider)) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// 解析最终用于显示的标签内容
|
||||
Component content = text.getText();
|
||||
if (override != null && override.getContent() != null) {
|
||||
content = override.getContent().copy().withStyle(content.getStyle());
|
||||
}
|
||||
|
||||
// 计算“样板”文本起点与宽度,按对齐方式与缩放修正 x/y
|
||||
int imageWidth = eap$getIntField(self, "imageWidth", 0);
|
||||
int imageHeight = eap$getIntField(self, "imageHeight", 0);
|
||||
Rect2i bounds = new Rect2i(0, 0, imageWidth, imageHeight);
|
||||
Point pos = text.getPosition().resolve(bounds);
|
||||
|
||||
float scale = text.getScale();
|
||||
|
||||
Font font = eap$getFont(self);
|
||||
// 只关心第一行(标题类文本无换行或 maxWidth<=0)
|
||||
var contentLine = (text.getMaxWidth() <= 0)
|
||||
? content.getVisualOrderText()
|
||||
: font.split(content, text.getMaxWidth()).get(0);
|
||||
int lineWidth = font.width(contentLine);
|
||||
|
||||
int x = pos.getX();
|
||||
int y = pos.getY();
|
||||
// 对齐修正
|
||||
var align = text.getAlign();
|
||||
if (align == TextAlignment.CENTER) {
|
||||
int textPx = Math.round(lineWidth * scale);
|
||||
x -= textPx / 2;
|
||||
} else if (align == TextAlignment.RIGHT) {
|
||||
int textPx = Math.round(lineWidth * scale);
|
||||
x -= textPx;
|
||||
}
|
||||
|
||||
// 判断是否为“样板”组标题(多语言兼容且避免标题)
|
||||
boolean isPatterns = false;
|
||||
// 1) 基于翻译键
|
||||
var contents = content.getContents();
|
||||
if (contents instanceof TranslatableContents tc) {
|
||||
String key = tc.getKey();
|
||||
if (key != null && key.endsWith(".patterns")) {
|
||||
isPatterns = true;
|
||||
}
|
||||
}
|
||||
// 2) 基于已知本地化键的字符串解析
|
||||
if (!isPatterns) {
|
||||
String label = content.getString();
|
||||
if (label != null) {
|
||||
if (label.equals(Component.translatable("gui.pattern_provider.patterns").getString())) isPatterns = true;
|
||||
else if (label.equals(Component.translatable("gui.extendedae.patterns").getString())) isPatterns = true;
|
||||
else if (label.equals(Component.translatable("gui.ae2.patterns").getString())) isPatterns = true;
|
||||
}
|
||||
}
|
||||
// 3) 容错:中文“样板”且在标题下方(放宽到 y>=14)或文本正好等于“样板”
|
||||
if (!isPatterns) {
|
||||
String s = content.getString();
|
||||
if (s != null && ("样板".equals(s) || (s.contains("样板") && y >= 14))) {
|
||||
isPatterns = true;
|
||||
}
|
||||
}
|
||||
if (!isPatterns) return;
|
||||
|
||||
int cur = 1;
|
||||
int max = 1;
|
||||
if (self instanceof ExPatternPageAccessor accessor) {
|
||||
cur = Math.max(0, accessor.eap$getCurrentPage()) + 1;
|
||||
}
|
||||
try {
|
||||
var fMax = self.getClass().getDeclaredField("eap$maxPageLocal");
|
||||
fMax.setAccessible(true);
|
||||
Object v = fMax.get(self);
|
||||
if (v instanceof Integer i) {
|
||||
max = Math.max(1, i);
|
||||
}
|
||||
} catch (Throwable ignored) {}
|
||||
|
||||
String pageText = "第"+cur+"页" + "/" + max + "页";
|
||||
|
||||
ScreenStyle style = eap$getStyle(self);
|
||||
int color = 0xFFFFFFFF;
|
||||
if (style != null) {
|
||||
try {
|
||||
color = style.getColor(PaletteColor.DEFAULT_TEXT_COLOR).toARGB();
|
||||
} catch (Throwable ignored) {}
|
||||
}
|
||||
int padding = 4;
|
||||
if (scale == 1.0f) {
|
||||
guiGraphics.drawString(font, pageText, x + lineWidth + padding, y, color, false);
|
||||
} else {
|
||||
guiGraphics.pose().pushPose();
|
||||
guiGraphics.pose().translate(x, y, 1);
|
||||
guiGraphics.pose().scale(scale, scale, 1);
|
||||
guiGraphics.drawString(font, pageText, lineWidth + padding, 0, color, false);
|
||||
guiGraphics.pose().popPose();
|
||||
}
|
||||
} catch (Throwable ignored) {}
|
||||
}
|
||||
}
|
||||
|
|
@ -2,6 +2,7 @@ package com.extendedae_plus.mixin.ae2;
|
|||
|
||||
import appeng.helpers.patternprovider.PatternProviderLogic;
|
||||
import appeng.helpers.patternprovider.PatternProviderLogicHost;
|
||||
import appeng.menu.AEBaseMenu;
|
||||
import appeng.menu.guisync.GuiSync;
|
||||
import appeng.menu.implementations.PatternProviderMenu;
|
||||
import com.extendedae_plus.api.AdvancedBlockingHolder;
|
||||
|
|
@ -20,9 +21,6 @@ import static com.extendedae_plus.util.ExtendedAELogger.LOGGER;
|
|||
|
||||
@Mixin(PatternProviderMenu.class)
|
||||
public abstract class PatternProviderMenuAdvancedMixin implements PatternProviderMenuAdvancedSync {
|
||||
@Shadow
|
||||
protected abstract boolean isServerSide();
|
||||
|
||||
@Shadow
|
||||
protected PatternProviderLogic logic;
|
||||
|
||||
|
|
@ -33,18 +31,16 @@ public abstract class PatternProviderMenuAdvancedMixin implements PatternProvide
|
|||
|
||||
@Inject(method = "broadcastChanges", at = @At("HEAD"))
|
||||
private void eap$syncAdvancedBlocking(CallbackInfo ci) {
|
||||
if (this.isServerSide()) {
|
||||
// 避免@Shadow父类方法,改用公共API:AEBaseMenu#isClientSide()
|
||||
if (!((AEBaseMenu) (Object) this).isClientSide()) {
|
||||
var l = this.logic;
|
||||
if (l instanceof AdvancedBlockingHolder holder) {
|
||||
this.eap$AdvancedBlocking = holder.eap$getAdvancedBlocking();
|
||||
LOGGER.debug("[EAP] Menu broadcastChanges HEAD: eap$AdvancedBlocking={}", this.eap$AdvancedBlocking);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(method = "broadcastChanges", at = @At("TAIL"))
|
||||
private void eap$syncAdvancedBlockingTail(CallbackInfo ci) {
|
||||
}
|
||||
|
||||
// 构造器尾注入(public ctor)
|
||||
@Inject(method = "<init>(ILnet/minecraft/world/entity/player/Inventory;Lappeng/helpers/patternprovider/PatternProviderLogicHost;)V", at = @At("TAIL"))
|
||||
private void eap$initAdvancedSync_Public(int id, Inventory playerInventory, PatternProviderLogicHost host, CallbackInfo ci) {
|
||||
|
|
|
|||
|
|
@ -4,14 +4,15 @@ import appeng.client.gui.AEBaseScreen;
|
|||
import appeng.client.gui.Icon;
|
||||
import appeng.client.gui.implementations.PatternProviderScreen;
|
||||
import appeng.client.gui.style.ScreenStyle;
|
||||
import appeng.client.gui.widgets.ToggleButton;
|
||||
import appeng.client.gui.widgets.SettingToggleButton;
|
||||
import appeng.api.config.YesNo;
|
||||
import appeng.api.config.Settings;
|
||||
import appeng.menu.implementations.PatternProviderMenu;
|
||||
import com.extendedae_plus.api.PatternProviderMenuAdvancedSync;
|
||||
import com.extendedae_plus.client.ClientAdvancedBlockingState;
|
||||
import com.extendedae_plus.mixin.ae2.accessor.PatternProviderLogicAccessor;
|
||||
import com.extendedae_plus.mixin.ae2.accessor.PatternProviderMenuAdvancedAccessor;
|
||||
import com.extendedae_plus.network.ModNetwork;
|
||||
import com.extendedae_plus.network.ToggleAdvancedBlockingC2SPacket;
|
||||
import com.extendedae_plus.api.ExPatternButtonsAccessor;
|
||||
import com.glodblock.github.extendedae.client.gui.GuiExPatternProvider;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.world.entity.player.Inventory;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
|
|
@ -21,122 +22,88 @@ import org.spongepowered.asm.mixin.injection.Inject;
|
|||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
import static com.extendedae_plus.util.ExtendedAELogger.LOGGER;
|
||||
|
||||
/**
|
||||
* 为 AE2 原版样板供应器界面添加“高级阻挡模式”按钮(仅客户端UI反馈)。
|
||||
* 为 AE2 原版样板供应器界面添加“高级阻挡模式”按钮。
|
||||
* - 位于左侧工具栏
|
||||
* - 点击后切换图标(YES/NO)并切换 tooltip 提示
|
||||
* - 当前不做任何网络/服务端逻辑
|
||||
* - 点击仅发送 C2S 切换请求;状态由 AE2 @GuiSync 回传决定
|
||||
*/
|
||||
@Mixin(PatternProviderScreen.class)
|
||||
public abstract class PatternProviderScreenMixin<C extends PatternProviderMenu> extends AEBaseScreen<C> {
|
||||
|
||||
@Unique
|
||||
private ToggleButton eap$AdvancedBlockingToggle;
|
||||
private SettingToggleButton<YesNo> eap$AdvancedBlockingToggle;
|
||||
|
||||
@Unique
|
||||
private boolean eap$AdvancedBlockingEnabled = false;
|
||||
|
||||
@Unique
|
||||
private String eap$ProviderKey = null;
|
||||
|
||||
public PatternProviderScreenMixin(C menu, Inventory playerInventory, Component title, ScreenStyle style) {
|
||||
super(menu, playerInventory, title, style);
|
||||
}
|
||||
|
||||
@Inject(method = "<init>", at = @At("RETURN"))
|
||||
private void eap$initAdvancedBlocking(C menu, Inventory playerInventory, Component title, ScreenStyle style, CallbackInfo ci) {
|
||||
// 计算供应器唯一键:维度ID + 方块坐标
|
||||
// 使用 @GuiSync 初始化
|
||||
try {
|
||||
var logic = ((PatternProviderMenuAdvancedAccessor) menu).eap$logic();
|
||||
var host = ((PatternProviderLogicAccessor) logic).eap$host();
|
||||
var be = host.getBlockEntity();
|
||||
var level = be.getLevel();
|
||||
String dimId = level.dimension().location().toString();
|
||||
long posLong = be.getBlockPos().asLong();
|
||||
this.eap$ProviderKey = ClientAdvancedBlockingState.key(dimId, posLong);
|
||||
if (menu instanceof PatternProviderMenuAdvancedSync sync) {
|
||||
this.eap$AdvancedBlockingEnabled = sync.eap$getAdvancedBlockingSynced();
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
LOGGER.error("Error initializing advanced sync", t);
|
||||
}
|
||||
|
||||
// 优先使用该供应器最近一次 S2C 状态;否则回退读取 @GuiSync 初始化
|
||||
if (this.eap$ProviderKey != null && ClientAdvancedBlockingState.has(this.eap$ProviderKey)) {
|
||||
this.eap$AdvancedBlockingEnabled = ClientAdvancedBlockingState.get(this.eap$ProviderKey);
|
||||
} else if (menu instanceof PatternProviderMenuAdvancedSync sync) {
|
||||
this.eap$AdvancedBlockingEnabled = sync.eap$getAdvancedBlockingSynced();
|
||||
}
|
||||
// 使用 ToggleButton 以便在 YES/NO 图标与提示之间动态切换
|
||||
this.eap$AdvancedBlockingToggle = new ToggleButton(
|
||||
Icon.BLOCKING_MODE_YES,
|
||||
Icon.BLOCKING_MODE_NO,
|
||||
// 提示文本:名称与说明
|
||||
Component.literal("高级阻挡模式"),
|
||||
Component.literal("高级阻挡模式:当开启时,执行更严格的阻挡判定"),
|
||||
(state) -> {
|
||||
// 客户端立即反馈:切换图标/提示
|
||||
this.eap$AdvancedBlockingEnabled = state;
|
||||
this.eap$AdvancedBlockingToggle.setState(state);
|
||||
// 发送 C2S 切换请求
|
||||
// 使用 SettingToggleButton<YesNo> 的外观(原版图标),但自定义悬停描述为“智能阻挡”
|
||||
this.eap$AdvancedBlockingToggle = new SettingToggleButton<>(
|
||||
Settings.BLOCKING_MODE,
|
||||
this.eap$AdvancedBlockingEnabled ? YesNo.YES : YesNo.NO,
|
||||
(btn, backwards) -> {
|
||||
// 不做本地切换,点击仅发送自定义C2S,显示由@GuiSync回传
|
||||
LOGGER.debug("[EAP] Click advanced blocking toggle: send C2S");
|
||||
ModNetwork.CHANNEL.sendToServer(new ToggleAdvancedBlockingC2SPacket());
|
||||
// 可根据状态调整提示文本(演示性:开启/关闭不同第二行)
|
||||
if (state) {
|
||||
this.eap$AdvancedBlockingToggle.setTooltipOn(java.util.List.of(
|
||||
Component.literal("高级阻挡模式"),
|
||||
Component.literal("高级阻挡模式:已开启")));
|
||||
this.eap$AdvancedBlockingToggle.setTooltipOff(java.util.List.of(
|
||||
Component.literal("高级阻挡模式"),
|
||||
Component.literal("高级阻挡模式:已开启")));
|
||||
} else {
|
||||
this.eap$AdvancedBlockingToggle.setTooltipOn(java.util.List.of(
|
||||
Component.literal("高级阻挡模式"),
|
||||
Component.literal("高级阻挡模式:已关闭")));
|
||||
this.eap$AdvancedBlockingToggle.setTooltipOff(java.util.List.of(
|
||||
Component.literal("高级阻挡模式"),
|
||||
Component.literal("高级阻挡模式:已关闭")));
|
||||
}
|
||||
}
|
||||
);
|
||||
this.eap$AdvancedBlockingToggle.setState(this.eap$AdvancedBlockingEnabled);
|
||||
// 初始 tooltip
|
||||
this.eap$AdvancedBlockingToggle.setTooltipOn(java.util.List.of(
|
||||
Component.literal("高级阻挡模式"),
|
||||
Component.literal(this.eap$AdvancedBlockingEnabled ? "高级阻挡模式:已开启" : "高级阻挡模式:已关闭")
|
||||
));
|
||||
this.eap$AdvancedBlockingToggle.setTooltipOff(java.util.List.of(
|
||||
Component.literal("高级阻挡模式"),
|
||||
Component.literal(this.eap$AdvancedBlockingEnabled ? "高级阻挡模式:已开启" : "高级阻挡模式:已关闭")
|
||||
));
|
||||
) {
|
||||
@Override
|
||||
public java.util.List<net.minecraft.network.chat.Component> getTooltipMessage() {
|
||||
boolean enabled = eap$AdvancedBlockingEnabled;
|
||||
var title = net.minecraft.network.chat.Component.literal("智能阻挡");
|
||||
var line = enabled
|
||||
? net.minecraft.network.chat.Component.literal("已启用:YES")
|
||||
: net.minecraft.network.chat.Component.literal("已禁用:NO");
|
||||
return java.util.List.of(title, line);
|
||||
}
|
||||
};
|
||||
// 初始化后立刻对齐当前@GuiSync状态,避免首帧显示不一致
|
||||
LOGGER.debug("[EAP] Screen init: initial synced={} -> set button", this.eap$AdvancedBlockingEnabled);
|
||||
this.eap$AdvancedBlockingToggle.set(this.eap$AdvancedBlockingEnabled ? YesNo.YES : YesNo.NO);
|
||||
|
||||
this.addToLeftToolbar(this.eap$AdvancedBlockingToggle);
|
||||
}
|
||||
|
||||
// 每帧刷新:从菜单同步布尔值,保持按钮状态一致
|
||||
@Inject(method = "updateBeforeRender", at = @At("TAIL"), remap = false)
|
||||
// 每帧刷新:仅从菜单(@GuiSync)同步布尔值,保持按钮状态一致
|
||||
@Inject(method = "updateBeforeRender", at = @At("HEAD"), remap = false)
|
||||
private void eap$updateAdvancedBlocking(CallbackInfo ci) {
|
||||
// 打印一条轻量 tick 日志以确认该方法被调用(频繁输出可在验证后移除)
|
||||
// System.out.println("[EPP][CLIENT] updateBeforeRender tick, local=" + this.eppAdvancedBlockingEnabled);
|
||||
|
||||
if (this.eap$AdvancedBlockingToggle == null) return;
|
||||
|
||||
boolean desired = this.eap$AdvancedBlockingEnabled;
|
||||
// 优先使用该供应器最近一次 S2C 值
|
||||
if (this.eap$ProviderKey != null && ClientAdvancedBlockingState.has(this.eap$ProviderKey)) {
|
||||
desired = ClientAdvancedBlockingState.get(this.eap$ProviderKey);
|
||||
} else if (this.menu instanceof PatternProviderMenuAdvancedSync sync) {
|
||||
if (this.menu instanceof PatternProviderMenuAdvancedSync sync) {
|
||||
desired = sync.eap$getAdvancedBlockingSynced();
|
||||
}
|
||||
|
||||
if (desired != this.eap$AdvancedBlockingEnabled) {
|
||||
this.eap$AdvancedBlockingEnabled = desired;
|
||||
this.eap$AdvancedBlockingToggle.setState(desired);
|
||||
// 同步 tooltip 二行提示
|
||||
this.eap$AdvancedBlockingToggle.setTooltipOn(java.util.List.of(
|
||||
Component.literal("高级阻挡模式"),
|
||||
Component.literal(desired ? "高级阻挡模式:已开启" : "高级阻挡模式:已关闭")
|
||||
));
|
||||
this.eap$AdvancedBlockingToggle.setTooltipOff(java.util.List.of(
|
||||
Component.literal("高级阻挡模式"),
|
||||
Component.literal(desired ? "高级阻挡模式:已开启" : "高级阻挡模式:已关闭")
|
||||
));
|
||||
// 与AE2一致:每帧无条件对齐按钮状态至@GuiSync(使用YesNo以获得原版图标与提示)
|
||||
LOGGER.debug("[EAP] updateBeforeRender tick: desired={}", desired);
|
||||
if (this.eap$AdvancedBlockingEnabled != desired) {
|
||||
LOGGER.debug("[EAP] updateBeforeRender: desired changed {} -> {}", this.eap$AdvancedBlockingEnabled, desired);
|
||||
}
|
||||
this.eap$AdvancedBlockingEnabled = desired;
|
||||
this.eap$AdvancedBlockingToggle.set(desired ? YesNo.YES : YesNo.NO);
|
||||
|
||||
// 如果当前屏幕是 ExtendedAE 的 GuiExPatternProvider,则委托布局更新到 accessor
|
||||
if ((Object) this instanceof GuiExPatternProvider) {
|
||||
try {
|
||||
((ExPatternButtonsAccessor) this).eap$updateButtonsLayout();
|
||||
} catch (Throwable t) {
|
||||
LOGGER.debug("[EAP] updateButtonsLayout skipped: {}", t.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import org.spongepowered.asm.mixin.Unique;
|
|||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
@Mixin(SlotGridLayout.class)
|
||||
public abstract class SlotGridLayoutMixin {
|
||||
|
|
@ -27,18 +28,54 @@ public abstract class SlotGridLayoutMixin {
|
|||
return;
|
||||
}
|
||||
|
||||
// 计算当前页码
|
||||
int currentPage = semanticIdx / SLOTS_PER_PAGE;
|
||||
|
||||
// 计算在当前页中的位置
|
||||
// 读取实际当前页码:优先从 GUI accessor,其次反射容器,失败则为 0
|
||||
int currentPage = 0;
|
||||
try {
|
||||
if (screen instanceof com.extendedae_plus.api.ExPatternPageAccessor accessor) {
|
||||
currentPage = accessor.eap$getCurrentPage();
|
||||
} else {
|
||||
var menu = ((com.glodblock.github.extendedae.client.gui.GuiExPatternProvider) screen).getMenu();
|
||||
Field fieldPage = eap$findFieldRecursive(menu.getClass(), "page");
|
||||
if (fieldPage != null) {
|
||||
fieldPage.setAccessible(true);
|
||||
currentPage = (Integer) fieldPage.get(menu);
|
||||
}
|
||||
}
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
|
||||
// 该槽位属于第几页
|
||||
int slotPage = semanticIdx / SLOTS_PER_PAGE;
|
||||
if (slotPage != currentPage) {
|
||||
// 非当前页:将其移出视野,避免渲染与鼠标命中
|
||||
cir.setReturnValue(new Point(-10000, -10000));
|
||||
cir.cancel();
|
||||
return;
|
||||
}
|
||||
|
||||
// 当前页中的位置(0..35)
|
||||
int slotInPage = semanticIdx % SLOTS_PER_PAGE;
|
||||
int row = slotInPage / 9; // 0-3
|
||||
int col = slotInPage % 9; // 0-8
|
||||
|
||||
|
||||
// 计算目标位置(始终在前4行)
|
||||
int targetX = x + col * 18;
|
||||
int targetY = y + row * 18;
|
||||
|
||||
|
||||
cir.setReturnValue(new Point(targetX, targetY));
|
||||
cir.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
@Unique
|
||||
private static Field eap$findFieldRecursive(Class<?> cls, String name) {
|
||||
Class<?> c = cls;
|
||||
while (c != null && c != Object.class) {
|
||||
try {
|
||||
return c.getDeclaredField(name);
|
||||
} catch (NoSuchFieldException ignored) {}
|
||||
c = c.getSuperclass();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -7,12 +7,12 @@ import appeng.client.gui.style.ScreenStyle;
|
|||
import appeng.menu.SlotSemantics;
|
||||
import com.extendedae_plus.NewIcon;
|
||||
import com.glodblock.github.extendedae.client.button.ActionEPPButton;
|
||||
import com.extendedae_plus.api.ExPatternButtonsAccessor;
|
||||
import com.extendedae_plus.config.ModConfigs;
|
||||
import com.glodblock.github.extendedae.client.gui.GuiExPatternProvider;
|
||||
import com.glodblock.github.extendedae.container.ContainerExPatternProvider;
|
||||
import com.glodblock.github.extendedae.network.EPPNetworkHandler;
|
||||
import com.glodblock.github.glodium.network.packet.CGenericPacket;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.Font;
|
||||
import net.minecraft.client.gui.GuiGraphics;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.world.entity.player.Inventory;
|
||||
|
|
@ -25,9 +25,10 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
|||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.List;
|
||||
import static com.extendedae_plus.util.ExtendedAELogger.LOGGER;
|
||||
|
||||
@Mixin(GuiExPatternProvider.class)
|
||||
public abstract class GuiExPatternProviderMixin extends PatternProviderScreen<ContainerExPatternProvider> {
|
||||
public abstract class GuiExPatternProviderMixin extends PatternProviderScreen<ContainerExPatternProvider> implements ExPatternButtonsAccessor, com.extendedae_plus.api.ExPatternPageAccessor {
|
||||
|
||||
@Unique
|
||||
ScreenStyle eap$screenStyle;
|
||||
|
|
@ -41,224 +42,68 @@ public abstract class GuiExPatternProviderMixin extends PatternProviderScreen<Co
|
|||
@Unique
|
||||
private static final int SLOTS_PER_PAGE = 36; // 每页显示36个槽位
|
||||
|
||||
@Unique
|
||||
private int eap$currentPage = 0;
|
||||
|
||||
@Unique
|
||||
private int eap$maxPageLocal = 1;
|
||||
|
||||
public GuiExPatternProviderMixin(ContainerExPatternProvider menu, Inventory playerInventory, Component title, ScreenStyle style) {
|
||||
super(menu, playerInventory, title, style);
|
||||
}
|
||||
|
||||
@Unique
|
||||
public void render(GuiGraphics guiGraphics, int mouseX, int mouseY, float partialTicks) {
|
||||
super.render(guiGraphics, mouseX, mouseY, partialTicks);
|
||||
|
||||
int maxSlots = this.getMenu().getSlots(SlotSemantics.ENCODED_PATTERN).size();
|
||||
// 只有当槽位数超过每页显示数量时才显示翻页信息
|
||||
if (maxSlots > SLOTS_PER_PAGE) {
|
||||
Font fontRenderer = Minecraft.getInstance().font;
|
||||
|
||||
// 获取当前页码
|
||||
int currentPage = getCurrentPage();
|
||||
int maxPage = getMaxPage();
|
||||
|
||||
// 获取ae通用界面样式
|
||||
int color = eap$screenStyle.getColor(PaletteColor.DEFAULT_TEXT_COLOR).toARGB();
|
||||
// 调整页码显示位置:在"样板"文字的右边
|
||||
guiGraphics.drawString(font, Component.literal("第 " + (currentPage + 1) + "/" + maxPage + " 页"),
|
||||
leftPos + 8 + 50, topPos + 30, color, false);
|
||||
}
|
||||
}
|
||||
|
||||
@Unique
|
||||
public void updateBeforeRender() {
|
||||
super.updateBeforeRender();
|
||||
try {
|
||||
ContainerExPatternProvider menu1 = this.getMenu();
|
||||
|
||||
// 调用showPage方法
|
||||
java.lang.reflect.Method showPageMethod = menu1.getClass().getMethod("showPage");
|
||||
showPageMethod.invoke(menu1);
|
||||
|
||||
// 获取当前页码和最大页码
|
||||
Field fieldPage = menu1.getClass().getDeclaredField("page");
|
||||
fieldPage.setAccessible(true);
|
||||
Integer page = (Integer) fieldPage.get(menu1);
|
||||
|
||||
Field fieldMaxPage = menu1.getClass().getDeclaredField("maxPage");
|
||||
fieldMaxPage.setAccessible(true);
|
||||
Integer maxPage = (Integer) fieldMaxPage.get(menu1);
|
||||
|
||||
// 更新按钮可见性 - 始终显示,支持循环翻页
|
||||
if (nextPage != null && prevPage != null) {
|
||||
this.nextPage.setVisibility(true);
|
||||
this.prevPage.setVisibility(true);
|
||||
}
|
||||
if (x2Button != null) {
|
||||
this.x2Button.setVisibility(true);
|
||||
}
|
||||
if (divideBy2Button != null) {
|
||||
this.divideBy2Button.setVisibility(true);
|
||||
}
|
||||
if (x10Button != null) {
|
||||
this.x10Button.setVisibility(true);
|
||||
}
|
||||
if (divideBy10Button != null) {
|
||||
this.divideBy10Button.setVisibility(true);
|
||||
}
|
||||
|
||||
// 调整槽位位置
|
||||
this.eap$adjustSlotPositions(page);
|
||||
} catch (Exception e) {
|
||||
// 忽略反射错误
|
||||
}
|
||||
|
||||
// 如果屏幕尺寸发生变化(窗口/GUI缩放),重新注册按钮,避免被 Screen.init 清空
|
||||
if (this.width != eap$lastScreenWidth || this.height != eap$lastScreenHeight) {
|
||||
eap$lastScreenWidth = this.width;
|
||||
eap$lastScreenHeight = this.height;
|
||||
try {
|
||||
if (this.divideBy2Button != null) {
|
||||
this.removeWidget(this.divideBy2Button);
|
||||
this.addRenderableWidget(this.divideBy2Button);
|
||||
}
|
||||
if (this.x2Button != null) {
|
||||
this.removeWidget(this.x2Button);
|
||||
this.addRenderableWidget(this.x2Button);
|
||||
}
|
||||
if (this.divideBy5Button != null) {
|
||||
this.removeWidget(this.divideBy5Button);
|
||||
this.addRenderableWidget(this.divideBy5Button);
|
||||
}
|
||||
if (this.x5Button != null) {
|
||||
this.removeWidget(this.x5Button);
|
||||
this.addRenderableWidget(this.x5Button);
|
||||
}
|
||||
if (this.divideBy10Button != null) {
|
||||
this.removeWidget(this.divideBy10Button);
|
||||
this.addRenderableWidget(this.divideBy10Button);
|
||||
}
|
||||
if (this.x10Button != null) {
|
||||
this.removeWidget(this.x10Button);
|
||||
this.addRenderableWidget(this.x10Button);
|
||||
}
|
||||
} catch (Throwable ignored) {}
|
||||
}
|
||||
|
||||
// 每帧定位四个按钮到 GUI 右缘外侧一点(使用绝对屏幕坐标)
|
||||
int bx = this.leftPos + this.imageWidth + 1; // 向右平移 1px 到面板外侧
|
||||
int by = this.topPos + 20;
|
||||
int spacing = 22;
|
||||
if (this.divideBy2Button != null) {
|
||||
this.divideBy2Button.setVisibility(true);
|
||||
this.divideBy2Button.setX(bx);
|
||||
this.divideBy2Button.setY(by);
|
||||
}
|
||||
if (this.x2Button != null) {
|
||||
this.x2Button.setVisibility(true);
|
||||
this.x2Button.setX(bx);
|
||||
this.x2Button.setY(by + spacing);
|
||||
}
|
||||
if (this.divideBy10Button != null) {
|
||||
this.divideBy10Button.setVisibility(true);
|
||||
this.divideBy10Button.setX(bx);
|
||||
this.divideBy10Button.setY(by + spacing * 4);
|
||||
}
|
||||
if (this.x10Button != null) {
|
||||
this.x10Button.setVisibility(true);
|
||||
this.x10Button.setX(bx);
|
||||
this.x10Button.setY(by + spacing * 5);
|
||||
}
|
||||
// 新增 /5 与 x5 的定位(位于 /2、x2 之后)
|
||||
if (this.divideBy5Button != null) {
|
||||
this.divideBy5Button.setVisibility(true);
|
||||
this.divideBy5Button.setX(bx);
|
||||
this.divideBy5Button.setY(by + spacing * 2);
|
||||
}
|
||||
if (this.x5Button != null) {
|
||||
this.x5Button.setVisibility(true);
|
||||
this.x5Button.setX(bx);
|
||||
this.x5Button.setY(by + spacing * 3);
|
||||
}
|
||||
}
|
||||
|
||||
@Unique
|
||||
private void eap$adjustSlotPositions(int currentPage) {
|
||||
try {
|
||||
List<Slot> slots = this.getMenu().getSlots(SlotSemantics.ENCODED_PATTERN);
|
||||
int totalSlots = slots.size();
|
||||
|
||||
if (totalSlots <= SLOTS_PER_PAGE) {
|
||||
return; // 不需要翻页
|
||||
}
|
||||
|
||||
int slot_id = 0;
|
||||
for (Slot s : slots) {
|
||||
int page_id = slot_id / SLOTS_PER_PAGE;
|
||||
|
||||
if (page_id == currentPage) {
|
||||
// 当前页的槽位需要调整位置
|
||||
int slotInPage = slot_id % SLOTS_PER_PAGE;
|
||||
int row = slotInPage / 9; // 0-3
|
||||
int col = slotInPage % 9; // 0-8
|
||||
|
||||
// 计算目标位置(始终在前4行)
|
||||
int x = 8 + col * 18;
|
||||
int y = 42 + row * 18;
|
||||
|
||||
// 使用反射设置槽位位置,支持混淆环境
|
||||
Field xField = null;
|
||||
Field yField = null;
|
||||
|
||||
// 尝试不同的字段名(开发环境和生产环境可能不同)
|
||||
String[] xFieldNames = {"x", "field_75262_c"};
|
||||
String[] yFieldNames = {"y", "field_75263_d"};
|
||||
|
||||
for (String fieldName : xFieldNames) {
|
||||
try {
|
||||
xField = Slot.class.getDeclaredField(fieldName);
|
||||
xField.setAccessible(true);
|
||||
break;
|
||||
} catch (NoSuchFieldException ignored) {}
|
||||
}
|
||||
|
||||
for (String fieldName : yFieldNames) {
|
||||
try {
|
||||
yField = Slot.class.getDeclaredField(fieldName);
|
||||
yField.setAccessible(true);
|
||||
break;
|
||||
} catch (NoSuchFieldException ignored) {}
|
||||
}
|
||||
|
||||
if (xField != null && yField != null) {
|
||||
xField.set(s, x);
|
||||
yField.set(s, y);
|
||||
}
|
||||
}
|
||||
++slot_id;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// 忽略反射错误
|
||||
}
|
||||
}
|
||||
|
||||
// 移除手动挪动 Slot 坐标,交由 SlotGridLayout + 原生布局控制
|
||||
|
||||
@Unique
|
||||
private int getCurrentPage() {
|
||||
try {
|
||||
ContainerExPatternProvider menu1 = this.getMenu();
|
||||
Field fieldPage = menu1.getClass().getDeclaredField("page");
|
||||
fieldPage.setAccessible(true);
|
||||
return (Integer) fieldPage.get(menu1);
|
||||
} catch (Exception e) {
|
||||
return 0;
|
||||
}
|
||||
// 优先使用本地 GUI 维护的页码
|
||||
return Math.max(0, eap$currentPage % Math.max(1, eap$maxPageLocal));
|
||||
}
|
||||
|
||||
@Unique
|
||||
private int getMaxPage() {
|
||||
// 优先使用配置倍数
|
||||
try {
|
||||
int cfg = ModConfigs.PAGE_MULTIPLIER.get();
|
||||
if (cfg > 1) return cfg;
|
||||
} catch (Throwable ignored) {}
|
||||
try {
|
||||
ContainerExPatternProvider menu1 = this.getMenu();
|
||||
Field fieldMaxPage = menu1.getClass().getDeclaredField("maxPage");
|
||||
fieldMaxPage.setAccessible(true);
|
||||
return (Integer) fieldMaxPage.get(menu1);
|
||||
} catch (Exception e) {
|
||||
return 1;
|
||||
Field fieldMaxPage = eap$findFieldRecursive(menu1.getClass(), "maxPage");
|
||||
if (fieldMaxPage != null) {
|
||||
fieldMaxPage.setAccessible(true);
|
||||
Object v = fieldMaxPage.get(menu1);
|
||||
if (v instanceof Integer i) return i;
|
||||
}
|
||||
} catch (Throwable ignored) {}
|
||||
// 回退:用槽位总数计算
|
||||
try {
|
||||
int totalSlots = this.getMenu().getSlots(SlotSemantics.ENCODED_PATTERN).size();
|
||||
return Math.max(1, (int) Math.ceil(totalSlots / (double) SLOTS_PER_PAGE));
|
||||
} catch (Throwable ignored) {}
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Unique
|
||||
private static Field eap$findFieldRecursive(Class<?> cls, String name) {
|
||||
Class<?> c = cls;
|
||||
while (c != null && c != Object.class) {
|
||||
try {
|
||||
return c.getDeclaredField(name);
|
||||
} catch (NoSuchFieldException ignored) {}
|
||||
c = c.getSuperclass();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Unique
|
||||
private static void eap$setIntFieldRecursive(Object obj, String name, int value) {
|
||||
if (obj == null) return;
|
||||
Field f = eap$findFieldRecursive(obj.getClass(), name);
|
||||
if (f != null) {
|
||||
try { f.setAccessible(true); f.set(obj, value); } catch (Throwable ignored) {}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -277,31 +122,78 @@ public abstract class GuiExPatternProviderMixin extends PatternProviderScreen<Co
|
|||
this.eap$screenStyle = style;
|
||||
// 保留:不再打印菜单类型
|
||||
|
||||
// 翻页按钮(仅在需要时显示)
|
||||
// 计算并下发 maxPage(配置优先,其次按槽位总数计算)
|
||||
int totalSlots = this.getMenu().getSlots(SlotSemantics.ENCODED_PATTERN).size();
|
||||
if (totalSlots > SLOTS_PER_PAGE) {
|
||||
int cfgPages = 1;
|
||||
try { cfgPages = Math.max(1, ModConfigs.PAGE_MULTIPLIER.get()); } catch (Throwable ignored) {}
|
||||
int calcPages = Math.max(1, (int) Math.ceil(totalSlots / (double) SLOTS_PER_PAGE));
|
||||
int desiredMaxPage = Math.max(cfgPages, calcPages);
|
||||
LOGGER.info("[EAP] GuiExPatternProvider init: totalSlots={}, cfgPages={}, calcPages={}, desiredMaxPage={}", totalSlots, cfgPages, calcPages, desiredMaxPage);
|
||||
// 更新本地最大页
|
||||
this.eap$maxPageLocal = Math.max(1, desiredMaxPage);
|
||||
this.eap$currentPage = 0;
|
||||
try {
|
||||
Field fMax = eap$findFieldRecursive(menu.getClass(), "maxPage");
|
||||
if (fMax != null) { fMax.setAccessible(true); fMax.set(menu, desiredMaxPage); }
|
||||
} catch (Throwable ignored) {}
|
||||
|
||||
// 翻页按钮(当存在多页时显示;支持仅由配置决定的“空白页”)
|
||||
if (desiredMaxPage > 1) {
|
||||
this.prevPage = new ActionEPPButton((b) -> {
|
||||
int currentPage = getCurrentPage();
|
||||
int maxPage = getMaxPage();
|
||||
int maxPage = Math.max(this.eap$maxPageLocal, getMaxPage());
|
||||
int newPage = (currentPage - 1 + maxPage) % maxPage;
|
||||
try {
|
||||
ContainerExPatternProvider menu1 = this.getMenu();
|
||||
java.lang.reflect.Method setPageMethod = menu1.getClass().getMethod("setPage", int.class);
|
||||
setPageMethod.invoke(menu1, newPage);
|
||||
// 尝试调用 setPage
|
||||
try {
|
||||
java.lang.reflect.Method setPageMethod = menu1.getClass().getMethod("setPage", int.class);
|
||||
setPageMethod.invoke(menu1, newPage);
|
||||
} catch (Throwable ignored2) {}
|
||||
// 直接写入 page 字段,确保生效
|
||||
Field f = eap$findFieldRecursive(menu1.getClass(), "page");
|
||||
if (f != null) {
|
||||
f.setAccessible(true);
|
||||
f.set(menu1, newPage);
|
||||
}
|
||||
} catch (Exception ignored) {}
|
||||
// 同步到本地 GUI 页码
|
||||
this.eap$currentPage = newPage;
|
||||
// 日志与强制重排(放在更新本地页码之后,确保布局读取到新页)
|
||||
LOGGER.info("[EAP] PrevPage clicked: {} -> {} (max={})", currentPage, newPage, maxPage);
|
||||
this.repositionSlots(SlotSemantics.ENCODED_PATTERN);
|
||||
this.repositionSlots(SlotSemantics.STORAGE);
|
||||
this.hoveredSlot = null;
|
||||
}, Icon.ARROW_LEFT);
|
||||
|
||||
this.nextPage = new ActionEPPButton((b) -> {
|
||||
int currentPage = getCurrentPage();
|
||||
int maxPage = getMaxPage();
|
||||
int maxPage = Math.max(this.eap$maxPageLocal, getMaxPage());
|
||||
int newPage = (currentPage + 1) % maxPage;
|
||||
try {
|
||||
ContainerExPatternProvider menu1 = this.getMenu();
|
||||
java.lang.reflect.Method setPageMethod = menu1.getClass().getMethod("setPage", int.class);
|
||||
setPageMethod.invoke(menu1, newPage);
|
||||
// 尝试调用 setPage
|
||||
try {
|
||||
java.lang.reflect.Method setPageMethod = menu1.getClass().getMethod("setPage", int.class);
|
||||
setPageMethod.invoke(menu1, newPage);
|
||||
} catch (Throwable ignored2) {}
|
||||
// 直接写入 page 字段,确保生效
|
||||
Field f = eap$findFieldRecursive(menu1.getClass(), "page");
|
||||
if (f != null) {
|
||||
f.setAccessible(true);
|
||||
f.set(menu1, newPage);
|
||||
}
|
||||
} catch (Exception ignored) {}
|
||||
// 同步到本地 GUI 页码
|
||||
this.eap$currentPage = newPage;
|
||||
// 日志与强制重排(放在更新本地页码之后,确保布局读取到新页)
|
||||
LOGGER.info("[EAP] NextPage clicked: {} -> {} (max={})", currentPage, newPage, maxPage);
|
||||
this.repositionSlots(SlotSemantics.ENCODED_PATTERN);
|
||||
this.repositionSlots(SlotSemantics.STORAGE);
|
||||
this.hoveredSlot = null;
|
||||
}, Icon.ARROW_RIGHT);
|
||||
|
||||
// 恢复到 AE2 左侧工具栏
|
||||
this.addToLeftToolbar(this.nextPage);
|
||||
this.addToLeftToolbar(this.prevPage);
|
||||
}
|
||||
|
|
@ -346,8 +238,104 @@ public abstract class GuiExPatternProviderMixin extends PatternProviderScreen<Co
|
|||
this.addRenderableWidget(this.x10Button);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int eap$getCurrentPage() {
|
||||
return getCurrentPage();
|
||||
}
|
||||
|
||||
// 页码文本绘制移交给 AEBaseScreenMixin.renderLabels 尾部执行
|
||||
|
||||
// 注意:不再注入 Screen#init,避免混入在某些映射情况下失败导致 TransformerError
|
||||
|
||||
@Override
|
||||
public void eap$updateButtonsLayout() {
|
||||
// 只处理按钮可见性与定位,不再强制 showPage 或挪动 Slot 坐标,避免与原布局/tooltip 冲突
|
||||
if (nextPage != null && prevPage != null) {
|
||||
this.nextPage.setVisibility(true);
|
||||
this.prevPage.setVisibility(true);
|
||||
}
|
||||
if (x2Button != null) {
|
||||
this.x2Button.setVisibility(true);
|
||||
}
|
||||
if (divideBy2Button != null) {
|
||||
this.divideBy2Button.setVisibility(true);
|
||||
}
|
||||
if (x10Button != null) {
|
||||
this.x10Button.setVisibility(true);
|
||||
}
|
||||
if (divideBy10Button != null) {
|
||||
this.divideBy10Button.setVisibility(true);
|
||||
}
|
||||
if (divideBy5Button != null) {
|
||||
this.divideBy5Button.setVisibility(true);
|
||||
}
|
||||
if (x5Button != null) {
|
||||
this.x5Button.setVisibility(true);
|
||||
}
|
||||
|
||||
// 如果屏幕尺寸发生变化(窗口/GUI缩放),重新注册右侧外列的自定义按钮,翻页按钮由左侧工具栏托管
|
||||
if (this.width != eap$lastScreenWidth || this.height != eap$lastScreenHeight) {
|
||||
eap$lastScreenWidth = this.width;
|
||||
eap$lastScreenHeight = this.height;
|
||||
try {
|
||||
if (this.divideBy2Button != null) {
|
||||
this.removeWidget(this.divideBy2Button);
|
||||
this.addRenderableWidget(this.divideBy2Button);
|
||||
}
|
||||
if (this.x2Button != null) {
|
||||
this.removeWidget(this.x2Button);
|
||||
this.addRenderableWidget(this.x2Button);
|
||||
}
|
||||
if (this.divideBy5Button != null) {
|
||||
this.removeWidget(this.divideBy5Button);
|
||||
this.addRenderableWidget(this.divideBy5Button);
|
||||
}
|
||||
if (this.x5Button != null) {
|
||||
this.removeWidget(this.x5Button);
|
||||
this.addRenderableWidget(this.x5Button);
|
||||
}
|
||||
if (this.divideBy10Button != null) {
|
||||
this.removeWidget(this.divideBy10Button);
|
||||
this.addRenderableWidget(this.divideBy10Button);
|
||||
}
|
||||
if (this.x10Button != null) {
|
||||
this.removeWidget(this.x10Button);
|
||||
this.addRenderableWidget(this.x10Button);
|
||||
}
|
||||
} catch (Throwable ignored) {}
|
||||
}
|
||||
|
||||
// 定位到 GUI 右缘外侧一点(使用绝对屏幕坐标)
|
||||
int bx = this.leftPos + this.imageWidth + 1; // 向右平移 1px 到面板外侧
|
||||
int by = this.topPos + 20;
|
||||
int spacing = 22;
|
||||
// 翻页按钮交由左侧工具栏布局,无需手动定位
|
||||
if (this.divideBy2Button != null) {
|
||||
this.divideBy2Button.setX(bx);
|
||||
this.divideBy2Button.setY(by);
|
||||
}
|
||||
if (this.x2Button != null) {
|
||||
this.x2Button.setX(bx);
|
||||
this.x2Button.setY(by + spacing);
|
||||
}
|
||||
if (this.divideBy5Button != null) {
|
||||
this.divideBy5Button.setX(bx);
|
||||
this.divideBy5Button.setY(by + spacing * 2);
|
||||
}
|
||||
if (this.x5Button != null) {
|
||||
this.x5Button.setX(bx);
|
||||
this.x5Button.setY(by + spacing * 3);
|
||||
}
|
||||
if (this.divideBy10Button != null) {
|
||||
this.divideBy10Button.setX(bx);
|
||||
this.divideBy10Button.setY(by + spacing * 4);
|
||||
}
|
||||
if (this.x10Button != null) {
|
||||
this.x10Button.setX(bx);
|
||||
this.x10Button.setY(by + spacing * 5);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 在服务器端执行样板缩放操作(单机模式)
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -2,11 +2,9 @@ package com.extendedae_plus.network;
|
|||
|
||||
import appeng.menu.implementations.PatternProviderMenu;
|
||||
import com.extendedae_plus.api.AdvancedBlockingHolder;
|
||||
import com.extendedae_plus.mixin.ae2.accessor.PatternProviderLogicAccessor;
|
||||
import com.extendedae_plus.mixin.ae2.accessor.PatternProviderMenuAdvancedAccessor;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraftforge.network.NetworkDirection;
|
||||
import net.minecraftforge.network.NetworkEvent;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
|
@ -22,7 +20,7 @@ public class ToggleAdvancedBlockingC2SPacket {
|
|||
|
||||
public static ToggleAdvancedBlockingC2SPacket decode(FriendlyByteBuf buf) {
|
||||
return new ToggleAdvancedBlockingC2SPacket();
|
||||
}
|
||||
}
|
||||
|
||||
public static void handle(ToggleAdvancedBlockingC2SPacket msg, Supplier<NetworkEvent.Context> ctxSupplier) {
|
||||
var ctx = ctxSupplier.get();
|
||||
|
|
@ -38,15 +36,8 @@ public class ToggleAdvancedBlockingC2SPacket {
|
|||
boolean current = holder.eap$getAdvancedBlocking();
|
||||
boolean next = !current;
|
||||
holder.eap$setAdvancedBlocking(next);
|
||||
// 关键:保存持久化,触发 AE2 写入逻辑(writeToNBT),并由菜单 @GuiSync 同步回客户端
|
||||
// 保存并触发 AE2 的菜单 @GuiSync 广播到所有观看该菜单的玩家
|
||||
logic.saveChanges();
|
||||
// 直接下发 S2C 强制同步(带供应器标识:维度+方块坐标)
|
||||
var host = ((PatternProviderLogicAccessor) logic).eap$host();
|
||||
var be = host.getBlockEntity();
|
||||
var level = be.getLevel();
|
||||
String dimId = level.dimension().location().toString();
|
||||
long posLong = be.getBlockPos().asLong();
|
||||
ModNetwork.CHANNEL.sendTo(new AdvancedBlockingSyncS2CPacket(dimId, posLong, next), player.connection.connection, NetworkDirection.PLAY_TO_CLIENT);
|
||||
}
|
||||
});
|
||||
ctx.setPacketHandled(true);
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
"compatibilityLevel": "JAVA_17",
|
||||
"minVersion": "0.8",
|
||||
"client": [
|
||||
"ae2.AEBaseScreenMixin",
|
||||
"PickFromWirelessMixin",
|
||||
"accessor.AbstractContainerScreenAccessor",
|
||||
"accessor.ScreenAccessor",
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user