diff --git a/src/main/java/com/extendedae_plus/mixin/ae2/client/gui/PatternProviderScreenMixin.java b/src/main/java/com/extendedae_plus/mixin/ae2/client/gui/PatternProviderScreenMixin.java index f4423b7..d8a906f 100644 --- a/src/main/java/com/extendedae_plus/mixin/ae2/client/gui/PatternProviderScreenMixin.java +++ b/src/main/java/com/extendedae_plus/mixin/ae2/client/gui/PatternProviderScreenMixin.java @@ -26,6 +26,9 @@ import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import java.util.List; +import java.util.function.Supplier; + import static com.extendedae_plus.util.Logger.EAP$LOGGER; /** @@ -36,152 +39,218 @@ import static com.extendedae_plus.util.Logger.EAP$LOGGER; @Mixin(PatternProviderScreen.class) public abstract class PatternProviderScreenMixin extends AEBaseScreen { + // 高级阻挡模式切换按钮 @Unique private SettingToggleButton eap$AdvancedBlockingToggle; + // 智能翻倍切换按钮 @Unique private SettingToggleButton eap$SmartDoublingToggle; + // 智能翻倍上限输入框 @Unique private EditBox eap$PerProviderLimitInput; + // 当前高级阻挡模式是否启用 @Unique private boolean eap$AdvancedBlockingEnabled = false; + // 当前智能翻倍是否启用 @Unique private boolean eap$SmartDoublingEnabled = false; + // 当前智能翻倍上限 @Unique private int eap$PerProviderScalingLimit = 0; + // 输入框上次是否处于焦点 @Unique private boolean eap$PerProviderLimitWasFocused = false; public PatternProviderScreenMixin(C menu, Inventory playerInventory, Component title, ScreenStyle style) { super(menu, playerInventory, title, style); } - @Inject(method = "", at = @At("RETURN")) - private void eap$initAdvancedBlocking(C menu, Inventory playerInventory, Component title, ScreenStyle style, CallbackInfo ci) { - // 使用 @GuiSync 初始化 + /* ---------------------------- 工具方法 ---------------------------- */ + /** + * 创建一个带有自定义点击事件和 tooltip 的切换按钮 + */ + @Unique + private SettingToggleButton eap$createToggle(boolean initial, + Runnable onClick, + Supplier> tooltipSupplier) { + return new SettingToggleButton<>( + Settings.BLOCKING_MODE, + initial ? YesNo.YES : YesNo.NO, + (btn, backwards) -> onClick.run() + ) { + @Override + public List getTooltipMessage() { + return tooltipSupplier.get(); + } + }; + } + + /** 同步服务端状态(初始化时调用) */ + @Unique + private void eap$syncInitialState(C menu) { try { + // 同步高级阻挡和智能翻倍的服务端状态 if (menu instanceof IPatternProviderMenuAdvancedSync sync) { this.eap$AdvancedBlockingEnabled = sync.eap$getAdvancedBlockingSynced(); } - } catch (Throwable t) { - EAP$LOGGER.error("Error initializing advanced sync", t); - } - - // 使用 SettingToggleButton 的外观(原版图标),但自定义悬停描述为“智能阻挡” - this.eap$AdvancedBlockingToggle = new SettingToggleButton<>( - Settings.BLOCKING_MODE, - this.eap$AdvancedBlockingEnabled ? YesNo.YES : YesNo.NO, - (btn, backwards) -> { - // 不做本地切换,点击仅发送自定义C2S,显示由@GuiSync回传 - ModNetwork.CHANNEL.sendToServer(new ToggleAdvancedBlockingC2SPacket()); - } - ) { - @Override - public java.util.List getTooltipMessage() { - boolean enabled = eap$AdvancedBlockingEnabled; - var title = Component.literal("智能阻挡"); - var line = enabled - ? Component.literal("已启用:对于同一种配方将不再阻挡(需要开启原版的阻挡模式)") - : Component.literal("已禁用:这么好的功能为什么不打开呢"); - return java.util.List.of(title, line); + if (menu instanceof IPatternProviderMenuDoublingSync sync) { + this.eap$SmartDoublingEnabled = sync.eap$getSmartDoublingSynced(); + this.eap$PerProviderScalingLimit = sync.eap$getScalingLimit(); } - }; - // 初始化后立刻对齐当前@GuiSync状态,避免首帧显示不一致 - this.eap$AdvancedBlockingToggle.set(this.eap$AdvancedBlockingEnabled ? YesNo.YES : YesNo.NO); + } catch (Throwable t) { + EAP$LOGGER.error("Error initializing sync", t); + } + } + + /** 创建并添加按钮和输入框 */ + @Unique + private void eap$createWidgets() { + // 高级阻挡 + this.eap$AdvancedBlockingToggle = eap$createToggle( + eap$AdvancedBlockingEnabled, + () -> ModNetwork.CHANNEL.sendToServer(new ToggleAdvancedBlockingC2SPacket()), + () -> { + var t = Component.literal("智能阻挡"); + var line = eap$AdvancedBlockingEnabled + ? Component.literal("已启用:对于同一种配方将不再阻挡 (需要启用原版阻挡模式)") + : Component.literal("已禁用:建议开启以获得更智能的阻挡行为"); + return List.of(t, line); + } + ); + this.eap$AdvancedBlockingToggle.set(eap$AdvancedBlockingEnabled ? YesNo.YES : YesNo.NO); this.addToLeftToolbar(this.eap$AdvancedBlockingToggle); - // 智能翻倍按钮:与高级阻挡同款样式,点击仅发送C2S,状态由@GuiSync驱动 - try { - if (menu instanceof IPatternProviderMenuDoublingSync sync2) { - this.eap$SmartDoublingEnabled = sync2.eap$getSmartDoublingSynced(); - this.eap$PerProviderScalingLimit = sync2.eap$getScalingLimit(); - } - } catch (Throwable t) { - EAP$LOGGER.error("Error initializing smart doubling sync", t); - } - - this.eap$SmartDoublingToggle = new SettingToggleButton<>( - Settings.BLOCKING_MODE, - this.eap$SmartDoublingEnabled ? YesNo.YES : YesNo.NO, - (btn, backwards) -> { - ModNetwork.CHANNEL.sendToServer(new ToggleSmartDoublingC2SPacket()); + // 智能翻倍 + this.eap$SmartDoublingToggle = eap$createToggle( + eap$SmartDoublingEnabled, + () -> ModNetwork.CHANNEL.sendToServer(new ToggleSmartDoublingC2SPacket()), + () -> { + var t = Component.literal("智能翻倍"); + var line = eap$SmartDoublingEnabled + ? Component.literal("已启用:根据请求量对处理样板进行智能缩放") + : Component.literal("已禁用:按原始样板数量进行发配"); + return List.of(t, line); } - ) { - @Override - public java.util.List getTooltipMessage() { - boolean enabled = eap$SmartDoublingEnabled; - var title = Component.literal("智能翻倍"); - var line = enabled - ? Component.literal("已启用:根据请求量对处理样板进行智能缩放") - : Component.literal("已禁用:按原始样板数量进行发配"); - return java.util.List.of(title, line); - } - }; - - this.eap$SmartDoublingToggle.set(this.eap$SmartDoublingEnabled ? YesNo.YES : YesNo.NO); + ); + this.eap$SmartDoublingToggle.set(eap$SmartDoublingEnabled ? YesNo.YES : YesNo.NO); this.addToLeftToolbar(this.eap$SmartDoublingToggle); - // 占位:每-provider 缩放上限输入框(仅作为 UI 占位,不发送网络请求) - try { - // 使用与左侧工具栏一致的布局托管,避免绝对坐标问题 - // 使用更短的输入框并去除前导 0(显示更紧凑) - this.eap$PerProviderLimitInput = new EditBox(this.font, 0, 0, 28, 12, Component.literal("Limit")); - this.eap$PerProviderLimitInput.setValue(String.valueOf(this.eap$PerProviderScalingLimit)); - this.eap$PerProviderLimitInput.setMaxLength(6); - // 值变更响应 - this.eap$PerProviderLimitInput.setResponder((s) -> { - try { - if (s != null && !s.isEmpty()) { - String trimmed = s.replaceFirst("^0+(?=.)", ""); - if (!trimmed.equals(s)) { - this.eap$PerProviderLimitInput.setValue(trimmed); - s = trimmed; - } - } - // 实时发送:尝试解析并发送到服务端(若值变化) - int val = Integer.parseInt(s == null || s.isBlank() ? "0" : s); + // 缩放上限输入框 + this.eap$PerProviderLimitInput = new EditBox(this.font, 0, 0, 28, 12, Component.literal("Limit")); + this.eap$PerProviderLimitInput.setMaxLength(6); + this.eap$PerProviderLimitInput.setValue(String.valueOf(eap$PerProviderScalingLimit)); + this.eap$PerProviderLimitInput.setResponder(this::eap$handleLimitChanged); + this.addRenderableWidget(this.eap$PerProviderLimitInput); + } - if (val != this.eap$PerProviderScalingLimit) { - this.eap$PerProviderScalingLimit = val; - ModNetwork.CHANNEL.sendToServer(new SetPerProviderScalingLimitC2SPacket(val)); - } - } catch (Throwable ignored) {} - }); - // 初次加入渲染列表(后续在 updateBeforeRender 每帧更新 tooltip/位置) - this.addRenderableWidget(this.eap$PerProviderLimitInput); - // 若菜单/逻辑已在之前同步了上限,则在控件创建后填充显示 - this.eap$PerProviderLimitInput.setValue(String.valueOf(this.eap$PerProviderScalingLimit)); + /** 输入框内容变化时调用 */ + @Unique + private void eap$handleLimitChanged(String s) { + try { + // 去除前导0,空字符串视为0 + String sValue = (s == null || s.isBlank()) ? "0" : s.replaceFirst("^0+(?=.)", ""); + if (!sValue.equals(s)) { + this.eap$PerProviderLimitInput.setValue(sValue); + } + int limit = Integer.parseInt(sValue); + // 只有变化时才发送同步包 + if (limit != this.eap$PerProviderScalingLimit) { + this.eap$PerProviderScalingLimit = limit; + ModNetwork.CHANNEL.sendToServer(new SetPerProviderScalingLimitC2SPacket(limit)); + } } catch (Throwable ignored) {} } - // 每帧刷新:仅从菜单(@GuiSync)同步布尔值,保持按钮状态一致 + /* ---------------------------- 注入点 ---------------------------- */ + @Inject(method = "", at = @At("RETURN")) + private void eap$onInit(C menu, Inventory playerInventory, Component title, ScreenStyle style, CallbackInfo ci) { + // 初始化时同步服务端状态并创建控件 + eap$syncInitialState(menu); + eap$createWidgets(); + } + + /** 每帧刷新:从服务端同步状态并更新 UI */ @Inject(method = "updateBeforeRender", at = @At("HEAD"), remap = false) - private void eap$updateAdvancedBlocking(CallbackInfo ci) { - if (this.eap$AdvancedBlockingToggle != null) { - boolean desired = this.eap$AdvancedBlockingEnabled; - if (this.menu instanceof IPatternProviderMenuAdvancedSync sync) { - desired = sync.eap$getAdvancedBlockingSynced(); - } - this.eap$AdvancedBlockingEnabled = desired; - this.eap$AdvancedBlockingToggle.set(desired ? YesNo.YES : YesNo.NO); + private void eap$updateBeforeRender(CallbackInfo ci) { + // 每帧刷新按钮和输入框状态 + eap$updateToggles(); + eap$updateLimitInput(); + eap$updateExGuiLayout(); + } + + /* ---------------------------- 刷新逻辑 ---------------------------- */ + /** + * 刷新切换按钮的状态(与服务端同步) + */ + @Unique + private void eap$updateToggles() { + if (this.eap$AdvancedBlockingToggle != null && this.menu instanceof IPatternProviderMenuAdvancedSync sync) { + this.eap$AdvancedBlockingEnabled = sync.eap$getAdvancedBlockingSynced(); + this.eap$AdvancedBlockingToggle.set(eap$AdvancedBlockingEnabled ? YesNo.YES : YesNo.NO); + } + if (this.eap$SmartDoublingToggle != null && this.menu instanceof IPatternProviderMenuDoublingSync sync) { + this.eap$SmartDoublingEnabled = sync.eap$getSmartDoublingSynced(); + this.eap$SmartDoublingToggle.set(eap$SmartDoublingEnabled ? YesNo.YES : YesNo.NO); + } + } + + /** + * 刷新输入框的内容和可见性 + */ + @Unique + private void eap$updateLimitInput() { + if (this.eap$PerProviderLimitInput == null) return; + + int remoteLimit = this.eap$PerProviderScalingLimit; + // 获取服务端最新的 scaling limit + if (this.menu instanceof IPatternProviderMenuDoublingSync sync) { + remoteLimit = sync.eap$getScalingLimit(); } - if (this.eap$SmartDoublingToggle != null) { - boolean desired2 = this.eap$SmartDoublingEnabled; - if (this.menu instanceof IPatternProviderMenuDoublingSync sync2) { - desired2 = sync2.eap$getSmartDoublingSynced(); - } - this.eap$SmartDoublingEnabled = desired2; - this.eap$SmartDoublingToggle.set(desired2 ? YesNo.YES : YesNo.NO); + boolean focused = this.eap$PerProviderLimitInput.isFocused(); + // 如果未聚焦且服务端有变化,则同步显示 + if (!focused && remoteLimit != this.eap$PerProviderScalingLimit) { + this.eap$PerProviderScalingLimit = remoteLimit; + this.eap$PerProviderLimitInput.setValue(String.valueOf(remoteLimit)); } - if (this.eap$PerProviderLimitInput != null) { - int remoteLimit = this.eap$PerProviderScalingLimit; - if (this.menu instanceof IPatternProviderMenuDoublingSync sync3) { - remoteLimit = sync3.eap$getScalingLimit(); + if (this.eap$SmartDoublingEnabled) { + // 智能翻倍启用时,确保输入框可见 + if (!this.renderables.contains(this.eap$PerProviderLimitInput)) { + this.addRenderableWidget(this.eap$PerProviderLimitInput); + } + // 未聚焦且内容为空时,显示0 + if (!focused && this.eap$PerProviderLimitInput.getValue().trim().isEmpty()) { + this.eap$PerProviderLimitInput.setValue("0"); } - // 若输入框没有焦点且远端值变化则更新控件;否则保留用户编辑的值 - boolean focused = this.eap$PerProviderLimitInput.isFocused(); - if (!focused && remoteLimit != this.eap$PerProviderScalingLimit) { - this.eap$PerProviderScalingLimit = remoteLimit; - this.eap$PerProviderLimitInput.setValue(String.valueOf(remoteLimit)); + // 定位输入框到智能翻倍按钮左侧 + Button ref = eap$SmartDoublingToggle; + if (ref != null) { + int ex = ref.getX() - this.eap$PerProviderLimitInput.getWidth() - 5; + int ey = ref.getY() + 2; + this.eap$PerProviderLimitInput.setX(ex); + this.eap$PerProviderLimitInput.setY(ey); } + + // 设置 tooltip + String cur = this.eap$PerProviderLimitInput.getValue(); + if (cur.isBlank()) cur = "0"; + this.eap$PerProviderLimitInput.setTooltip(Tooltip.create(Component.literal("样板输入物品数量上限: " + cur))); + + // 失焦时提交最新值 + boolean focusedNow = this.eap$PerProviderLimitInput.isFocused(); + if (this.eap$PerProviderLimitWasFocused && !focusedNow) { + int value = Integer.parseInt(cur); + this.eap$PerProviderScalingLimit = value; + ModNetwork.CHANNEL.sendToServer(new SetPerProviderScalingLimitC2SPacket(value)); + } + this.eap$PerProviderLimitWasFocused = focusedNow; + } else { + // 智能翻倍未启用时,移除输入框 + this.removeWidget(this.eap$PerProviderLimitInput); } + } + /** + * 如果是扩展 GUI,刷新扩展按钮布局 + */ + @Unique + private void eap$updateExGuiLayout() { if ((Object) this instanceof GuiExPatternProvider) { try { ((IExPatternButtonsAccessor) this).eap$updateButtonsLayout(); @@ -189,54 +258,5 @@ public abstract class PatternProviderScreenMixin EAP$LOGGER.debug("[EAP] updateButtonsLayout skipped: {}", t.toString()); } } - // 保证 EditBox 在每帧存在且定位到左侧工具栏旁边(参考已有左侧按钮位置) - try { - if (this.eap$PerProviderLimitInput != null) { - // 仅在智能倍增已启用时显示输入框 - if (this.eap$SmartDoublingEnabled) { - if (!this.renderables.contains(this.eap$PerProviderLimitInput)) { - this.addRenderableWidget(this.eap$PerProviderLimitInput); - } - - // 当输入框未获得焦点且内容为空时填充 0 - if (!this.eap$PerProviderLimitInput.isFocused() && this.eap$PerProviderLimitInput.getValue().trim().isEmpty()) { - this.eap$PerProviderLimitInput.setValue("0"); - } - - // 优先参考已有的左侧按钮定位 - Button ref = eap$SmartDoublingToggle; - if (ref != null) { - int ex = ref.getX() - this.eap$PerProviderLimitInput.getWidth() - 5; - int ey = ref.getY() + 2; // 向下移动 2 像素 - this.eap$PerProviderLimitInput.setX(ex); - this.eap$PerProviderLimitInput.setY(ey); - } - - // 动态更新 tooltip,简短且包含当前值 - String cur = this.eap$PerProviderLimitInput.getValue(); - if (cur.isBlank()) cur = "0"; - var tip = Component.literal("智能翻倍上限: " + cur); - this.eap$PerProviderLimitInput.setTooltip(Tooltip.create(tip)); - - // 当输入框失去焦点且之前处于聚焦时,提交当前值并更新本地同步字段 - boolean focusedNow = this.eap$PerProviderLimitInput.isFocused(); - if (this.eap$PerProviderLimitWasFocused && !focusedNow) { - int val = Integer.parseInt(cur); - // 更新本地展示值并发包到服务端 - this.eap$PerProviderScalingLimit = val; - EAP$LOGGER.info("更新"); - ModNetwork.CHANNEL.sendToServer(new SetPerProviderScalingLimitC2SPacket(val)); - } - this.eap$PerProviderLimitWasFocused = focusedNow; - - } else { - // 隐藏输入框 - if (this.renderables.contains(this.eap$PerProviderLimitInput)) { - this.removeWidget(this.eap$PerProviderLimitInput); - } - } - } - } catch (Throwable ignored) {} } } - diff --git a/src/main/java/com/extendedae_plus/network/provider/SetPerProviderScalingLimitC2SPacket.java b/src/main/java/com/extendedae_plus/network/provider/SetPerProviderScalingLimitC2SPacket.java index 6e4762f..4537c47 100644 --- a/src/main/java/com/extendedae_plus/network/provider/SetPerProviderScalingLimitC2SPacket.java +++ b/src/main/java/com/extendedae_plus/network/provider/SetPerProviderScalingLimitC2SPacket.java @@ -41,12 +41,14 @@ public class SetPerProviderScalingLimitC2SPacket { var logic = accessor.eap$logic(); if (logic instanceof ISmartDoublingHolder handler) { handler.eap$setProviderSmartDoublingLimit(msg.limit); + logic.saveChanges(); } } else if (containerMenu instanceof AdvPatternProviderMenu advMenu) { var accessor = (AdvPatternProviderMenuAdvancedAccessor) advMenu; var logic = accessor.eap$logic(); if (logic instanceof ISmartDoublingHolder handler) { handler.eap$setProviderSmartDoublingLimit(msg.limit); + logic.saveChanges(); } } } catch (Throwable ignored) {