Merge remote-tracking branch 'origin/develop/iava'
This commit is contained in:
commit
ef8fa756ed
|
|
@ -0,0 +1,34 @@
|
|||
package com.extendedae_plus.content;
|
||||
|
||||
import appeng.api.stacks.AEKey;
|
||||
import appeng.api.crafting.IPatternDetails;
|
||||
import appeng.client.gui.me.patternaccess.PatternContainerRecord;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* 客户端本地的高亮存储,仅作用于接收该客户端。
|
||||
* 使用 AEKey 作为标识,渲染时可通过 AEKey 与本地解码的 IPatternDetails 比对。
|
||||
*/
|
||||
public final class ClientPatternHighlightStore {
|
||||
private static final Set<AEKey> HIGHLIGHTS = Collections.synchronizedSet(new HashSet<>());
|
||||
|
||||
private ClientPatternHighlightStore() {}
|
||||
|
||||
public static void setHighlight(AEKey key, boolean highlight) {
|
||||
if (key == null) return;
|
||||
if (highlight) HIGHLIGHTS.add(key);
|
||||
else HIGHLIGHTS.remove(key);
|
||||
}
|
||||
|
||||
public static boolean hasHighlight(AEKey key) {
|
||||
if (key == null) return false;
|
||||
return HIGHLIGHTS.contains(key);
|
||||
}
|
||||
|
||||
public static void clearAll() { HIGHLIGHTS.clear(); }
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -8,7 +8,7 @@ import org.spongepowered.asm.mixin.Unique;
|
|||
@Mixin(value = AEProcessingPattern.class, remap = false)
|
||||
public class AEProcessingPatternMixin implements SmartDoublingAwarePattern {
|
||||
@Unique
|
||||
private boolean eap$allowScaling = true; // 默认允许缩放
|
||||
private boolean eap$allowScaling = false; // 默认不允许缩放
|
||||
|
||||
@Override
|
||||
public boolean eap$allowScaling() {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package com.extendedae_plus.mixin.ae2.accessor;
|
||||
|
||||
import appeng.api.networking.IManagedGridNode;
|
||||
import appeng.helpers.patternprovider.PatternProviderLogic;
|
||||
import appeng.helpers.patternprovider.PatternProviderLogicHost;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
|
|
@ -9,4 +10,7 @@ import org.spongepowered.asm.mixin.gen.Accessor;
|
|||
public interface PatternProviderLogicAccessor {
|
||||
@Accessor("host")
|
||||
PatternProviderLogicHost eap$host();
|
||||
|
||||
@Accessor("mainNode")
|
||||
IManagedGridNode eap$mainNode();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,39 +0,0 @@
|
|||
package com.extendedae_plus.mixin.ae2.autopattern.adaptation;
|
||||
|
||||
import appeng.api.crafting.IPatternDetails;
|
||||
import com.extendedae_plus.content.ScaledProcessingPattern;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Pseudo;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**适配
|
||||
* Redirect PatternProviderLogic.pushPattern 中对 List.contains 的调用,
|
||||
* 在遇到缩放样板时回退匹配到原始样板实例。
|
||||
*/
|
||||
@Pseudo
|
||||
@Mixin(targets = "net.pedroksl.advanced_ae.common.logic.AdvPatternProviderLogic", remap = false)
|
||||
public class AdvPatternProviderLogicContainsRedirectMixin {
|
||||
|
||||
@Redirect(method = "pushPattern",
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
target = "Ljava/util/List;contains(Ljava/lang/Object;)Z")
|
||||
)
|
||||
private boolean eap$patternsContains(List<?> list, Object o) {
|
||||
try {
|
||||
if (o instanceof ScaledProcessingPattern scaled) {
|
||||
IPatternDetails base = scaled.getOriginal();
|
||||
if (base != null && list.indexOf(base) != -1) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// 使用 indexOf 避免再次触发对 List.contains 的 redirect(防止递归)
|
||||
return list.indexOf(o) != -1;
|
||||
} catch (Throwable t) {
|
||||
return list.indexOf(o) != -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,10 +1,12 @@
|
|||
package com.extendedae_plus.mixin.ae2.client.gui;
|
||||
|
||||
import appeng.api.crafting.PatternDetailsHelper;
|
||||
import appeng.api.stacks.AEKey;
|
||||
import appeng.client.Point;
|
||||
import appeng.client.gui.AEBaseScreen;
|
||||
import appeng.client.gui.StackWithBounds;
|
||||
import appeng.client.gui.TextOverride;
|
||||
import appeng.client.gui.implementations.PatternProviderScreen;
|
||||
import appeng.client.gui.me.crafting.CraftingCPUScreen;
|
||||
import appeng.client.gui.style.PaletteColor;
|
||||
import appeng.client.gui.style.ScreenStyle;
|
||||
|
|
@ -12,12 +14,14 @@ import appeng.client.gui.style.Text;
|
|||
import appeng.client.gui.style.TextAlignment;
|
||||
import appeng.menu.slot.AppEngSlot;
|
||||
import com.extendedae_plus.api.ExPatternPageAccessor;
|
||||
import com.extendedae_plus.content.ClientPatternHighlightStore;
|
||||
import com.extendedae_plus.network.CraftingMonitorJumpC2SPacket;
|
||||
import com.extendedae_plus.network.CraftingMonitorOpenProviderC2SPacket;
|
||||
import com.extendedae_plus.network.ModNetwork;
|
||||
import com.extendedae_plus.util.GuiUtil;
|
||||
import com.glodblock.github.extendedae.client.gui.GuiExPatternProvider;
|
||||
import com.mojang.logging.LogUtils;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.Font;
|
||||
import net.minecraft.client.gui.GuiGraphics;
|
||||
import net.minecraft.client.renderer.Rect2i;
|
||||
|
|
@ -26,6 +30,7 @@ import net.minecraft.network.chat.contents.TranslatableContents;
|
|||
import net.minecraft.world.inventory.Slot;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
|
|
@ -150,40 +155,54 @@ public abstract class AEBaseScreenMixin {
|
|||
@Inject(method = "renderSlot", at = @At("TAIL"))
|
||||
private void eap$renderSlotAmounts(GuiGraphics guiGraphics, Slot s, CallbackInfo ci) {
|
||||
Object self = this;
|
||||
|
||||
|
||||
// 只处理AppEngSlot类型的槽位
|
||||
if (!(s instanceof AppEngSlot appEngSlot)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// 检查槽位是否可见且有效
|
||||
if (!appEngSlot.isActive() || !appEngSlot.isSlotEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// 获取槽位中的物品
|
||||
var itemStack = appEngSlot.getItem();
|
||||
if (itemStack.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// 使用GuiUtil的格式化方法获取数量文本
|
||||
String amountText = GuiUtil.getPatternOutputText(itemStack);
|
||||
if (amountText.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// 在槽位右下角绘制数量文本
|
||||
Font font = eap$getFont(self);
|
||||
GuiUtil.drawAmountText(guiGraphics, font, amountText, appEngSlot.x, appEngSlot.y, 0.6f);
|
||||
|
||||
try {
|
||||
var details = PatternDetailsHelper.decodePattern(itemStack, Minecraft.getInstance().level, false);
|
||||
try {
|
||||
if (details != null && details.getOutputs() != null && details.getOutputs().length > 0) {
|
||||
AEKey key = details.getOutputs()[0].what();
|
||||
if (key != null && ClientPatternHighlightStore.hasHighlight(key)) {
|
||||
try {
|
||||
GuiUtil.drawSlotRainbowHighlight(guiGraphics, s.x, s.y);
|
||||
} catch (Throwable ignored) {}
|
||||
}
|
||||
}
|
||||
} catch (Throwable ignore) {}
|
||||
} catch (Throwable ignore) {}
|
||||
}
|
||||
|
||||
// 在 AEBaseScreen.drawText 完成某个文本绘制后,若该文本为“样板”标签,则紧接着绘制页码。
|
||||
@Inject(method = "drawText", at = @At("TAIL"), remap = false)
|
||||
private void eap$appendPageAfterPatternsLabel(GuiGraphics guiGraphics,
|
||||
Text text,
|
||||
@Nullable TextOverride override,
|
||||
CallbackInfo ci) {
|
||||
Text text,
|
||||
@Nullable TextOverride override,
|
||||
CallbackInfo ci) {
|
||||
Object self = this;
|
||||
if (!(self instanceof GuiExPatternProvider)) {
|
||||
return;
|
||||
|
|
@ -237,8 +256,10 @@ public abstract class AEBaseScreenMixin {
|
|||
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;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
@ -263,16 +284,18 @@ public abstract class AEBaseScreenMixin {
|
|||
if (v instanceof Integer i) {
|
||||
max = Math.max(1, i);
|
||||
}
|
||||
} catch (Throwable ignored) {}
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
|
||||
String pageText = "第"+cur+"页" + "/" + max + "页";
|
||||
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) {}
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
}
|
||||
int padding = 4;
|
||||
if (scale == 1.0f) {
|
||||
|
|
@ -284,6 +307,24 @@ public abstract class AEBaseScreenMixin {
|
|||
guiGraphics.drawString(font, pageText, lineWidth + padding, 0, color, false);
|
||||
guiGraphics.pose().popPose();
|
||||
}
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Shadow
|
||||
protected void setTextContent(String id, Component content) {};
|
||||
|
||||
@Inject(method = "updateBeforeRender", at = @At("RETURN"), remap = false)
|
||||
private void onUpdateBeforeRender(CallbackInfo ci) {
|
||||
try {
|
||||
AEBaseScreen<?> self = (AEBaseScreen<?>) (Object) this;
|
||||
if (self instanceof PatternProviderScreen screen){
|
||||
Component t = screen.getTitle();
|
||||
if (t != null && !t.getString().isEmpty()) {
|
||||
this.setTextContent(AEBaseScreen.TEXT_ID_DIALOG_TITLE, t);
|
||||
}
|
||||
}
|
||||
} catch (Throwable ignored) {}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,28 @@
|
|||
package com.extendedae_plus.mixin.ae2.client.gui;
|
||||
|
||||
import appeng.client.gui.implementations.PatternProviderScreen;
|
||||
import com.extendedae_plus.content.ClientPatternHighlightStore;
|
||||
import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen;
|
||||
import net.minecraft.world.inventory.AbstractContainerMenu;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
@Mixin(value = AbstractContainerScreen.class, remap = false)
|
||||
public class PatternProviderCloseMixin {
|
||||
|
||||
@Shadow
|
||||
protected AbstractContainerMenu menu;
|
||||
|
||||
@Inject(method = "removed", at = @At("HEAD"))
|
||||
private void onRemoved(CallbackInfo ci) {
|
||||
try {
|
||||
if (((Object) this) instanceof PatternProviderScreen) {
|
||||
ClientPatternHighlightStore.clearAll();
|
||||
}
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -21,6 +21,7 @@ import org.spongepowered.asm.mixin.injection.Inject;
|
|||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import static com.extendedae_plus.util.ExtendedAELogger.LOGGER;
|
||||
|
||||
|
|
@ -49,8 +50,8 @@ public abstract class GuiExPatternProviderMixin extends PatternProviderScreen<Co
|
|||
super(menu, playerInventory, title, style);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// 移除手动挪动 Slot 坐标,交由 SlotGridLayout + 原生布局控制
|
||||
|
||||
@Unique
|
||||
|
|
@ -144,7 +145,7 @@ public abstract class GuiExPatternProviderMixin extends PatternProviderScreen<Co
|
|||
ContainerExPatternProvider menu1 = this.getMenu();
|
||||
// 尝试调用 setPage
|
||||
try {
|
||||
java.lang.reflect.Method setPageMethod = menu1.getClass().getMethod("setPage", int.class);
|
||||
Method setPageMethod = menu1.getClass().getMethod("setPage", int.class);
|
||||
setPageMethod.invoke(menu1, newPage);
|
||||
} catch (Throwable ignored2) {}
|
||||
// 直接写入 page 字段,确保生效
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ import appeng.api.crafting.PatternDetailsHelper;
|
|||
import appeng.client.gui.AEBaseScreen;
|
||||
import appeng.client.gui.Icon;
|
||||
import appeng.client.gui.me.patternaccess.PatternContainerRecord;
|
||||
import appeng.client.gui.me.patternaccess.PatternSlot;
|
||||
import appeng.client.gui.style.ScreenStyle;
|
||||
import appeng.client.gui.widgets.AETextField;
|
||||
import appeng.client.gui.widgets.IconButton;
|
||||
|
|
@ -13,6 +12,7 @@ import com.extendedae_plus.config.ModConfigs;
|
|||
import com.extendedae_plus.mixin.extendedae.accessor.GuiExPatternTerminalAccessor;
|
||||
import com.extendedae_plus.network.ModNetwork;
|
||||
import com.extendedae_plus.network.OpenProviderUiC2SPacket;
|
||||
import com.extendedae_plus.util.GuiUtil;
|
||||
import com.glodblock.github.extendedae.client.gui.GuiExPatternTerminal;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.GuiGraphics;
|
||||
|
|
@ -76,41 +76,6 @@ public abstract class GuiExPatternTerminalMixin extends AEBaseScreen<AEBaseMenu>
|
|||
super(menu, playerInventory, title, style);
|
||||
}
|
||||
|
||||
@Unique
|
||||
private static int eap$withAlpha(int rgb, int alpha255) {
|
||||
return ((alpha255 & 0xFF) << 24) | (rgb & 0x00FFFFFF);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将 HSV 转换为 RGB(返回 0xRRGGBB,不含 alpha)。
|
||||
* h: 0.0~1.0,s: 0.0~1.0,v: 0.0~1.0
|
||||
*/
|
||||
@Unique
|
||||
private static int eap$hsvToRgb(float h, float s, float v) {
|
||||
if (s <= 0.0f) {
|
||||
int g = Math.round(v * 255.0f);
|
||||
return (g << 16) | (g << 8) | g;
|
||||
}
|
||||
float hh = (h - (float) Math.floor(h)) * 6.0f;
|
||||
int sector = (int) Math.floor(hh);
|
||||
float f = hh - sector;
|
||||
float p = v * (1.0f - s);
|
||||
float q = v * (1.0f - s * f);
|
||||
float t = v * (1.0f - s * (1.0f - f));
|
||||
float r, g, b;
|
||||
switch (sector) {
|
||||
case 0: r = v; g = t; b = p; break;
|
||||
case 1: r = q; g = v; b = p; break;
|
||||
case 2: r = p; g = v; b = t; break;
|
||||
case 3: r = p; g = q; b = v; break;
|
||||
case 4: r = t; g = p; b = v; break;
|
||||
default: r = v; g = p; b = q; break;
|
||||
}
|
||||
int ri = Math.round(r * 255.0f);
|
||||
int gi = Math.round(g * 255.0f);
|
||||
int bi = Math.round(b * 255.0f);
|
||||
return (ri << 16) | (gi << 8) | bi;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前选择的样板供应器ID
|
||||
|
|
@ -579,53 +544,9 @@ public abstract class GuiExPatternTerminalMixin extends AEBaseScreen<AEBaseMenu>
|
|||
return;
|
||||
}
|
||||
|
||||
// 彩虹色的流转:基于时间在 HSV 色环上循环(4 秒为一周期)
|
||||
long now = System.currentTimeMillis();
|
||||
final long rainbowPeriodMs = 4000L;
|
||||
float hue = (now % rainbowPeriodMs) / (float) rainbowPeriodMs; // 0.0 ~ 1.0
|
||||
int rainbowRgb = eap$hsvToRgb(hue, 1.0f, 1.0f);
|
||||
// 使用 GuiUtil 的通用绘制方法绘制槽位高亮(包含彩虹流转效果)
|
||||
GuiUtil.drawPatternSlotHighlights(guiGraphics, this.menu.slots, this.matchedStack, this.matchedProvider);
|
||||
|
||||
for (Slot slot : this.menu.slots) {
|
||||
if (!(slot instanceof PatternSlot ps)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int sx = slot.x;
|
||||
int sy = slot.y;
|
||||
|
||||
boolean isMatchedSlot = this.matchedStack != null && this.matchedStack.contains(slot.getItem());
|
||||
boolean isMatchedProvider = false;
|
||||
try {
|
||||
PatternContainerRecord container = ps.getMachineInv();
|
||||
isMatchedProvider = this.matchedProvider != null && this.matchedProvider.contains(container);
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
|
||||
// 依据命中状态选择颜色方案
|
||||
int borderColor;
|
||||
int backgroundColor;
|
||||
|
||||
if (isMatchedSlot) {
|
||||
// 命中槽位:使用彩虹色边框与浅底色(固定透明度,呈现色相流转效果)
|
||||
borderColor = eap$withAlpha(rainbowRgb, 0xA0);
|
||||
backgroundColor = eap$withAlpha(rainbowRgb, 0x3C);
|
||||
} else if (!isMatchedProvider) {
|
||||
borderColor = eap$withAlpha(0xFFFFFF, 0x40);
|
||||
backgroundColor = eap$withAlpha(0x000000, 0x18);
|
||||
} else {
|
||||
borderColor = eap$withAlpha(0xFFFFFF, 0x30);
|
||||
backgroundColor = eap$withAlpha(0xFFFFFF, 0x14);
|
||||
}
|
||||
|
||||
// 绘制 18x18 边框(1px 宽)
|
||||
eap$fill(guiGraphics, new Rect2i(sx - 1, sy - 1, 18, 1), borderColor);
|
||||
eap$fill(guiGraphics, new Rect2i(sx - 1, sy + 16, 18, 1), borderColor);
|
||||
eap$fill(guiGraphics, new Rect2i(sx - 1, sy, 1, 16), borderColor);
|
||||
eap$fill(guiGraphics, new Rect2i(sx + 16, sy, 1, 16), borderColor);
|
||||
|
||||
// 绘制 16x16 浅底色(半透明,叠加在槽位上方)
|
||||
eap$fill(guiGraphics, new Rect2i(sx, sy, 16, 16), backgroundColor);
|
||||
}
|
||||
}
|
||||
|
||||
@Unique
|
||||
|
|
|
|||
|
|
@ -12,14 +12,27 @@ import appeng.menu.locator.MenuLocators;
|
|||
import appeng.menu.me.crafting.CraftingCPUMenu;
|
||||
import appeng.parts.AEBasePart;
|
||||
import com.extendedae_plus.mixin.ae2.accessor.PatternProviderLogicAccessor;
|
||||
import com.mojang.logging.LogUtils;
|
||||
import com.extendedae_plus.util.PatternProviderDataUtil;
|
||||
import com.glodblock.github.extendedae.util.FCClientUtil;
|
||||
import com.glodblock.github.glodium.util.GlodUtil;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.phys.AABB;
|
||||
import net.minecraftforge.network.NetworkDirection;
|
||||
import net.minecraftforge.network.NetworkEvent;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import static com.glodblock.github.extendedae.client.render.EAEHighlightHandler.highlight;
|
||||
|
||||
/**
|
||||
* 客户端从 CraftingCPUScreen 发送:鼠标下条目对应的 AEKey。
|
||||
* 服务端在当前打开的 CraftingCPUMenu 所属网络中,定位匹配该 AEKey 的样板供应器,
|
||||
|
|
@ -47,11 +60,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;
|
||||
}
|
||||
|
||||
|
|
@ -62,19 +72,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;
|
||||
}
|
||||
|
|
@ -89,29 +96,72 @@ public class CraftingMonitorOpenProviderC2SPacket {
|
|||
if (host == null) continue;
|
||||
var pbe = host.getBlockEntity();
|
||||
if (pbe == null) continue;
|
||||
// 在服务端上下文中执行,pbe 仅用于构造菜单定位器
|
||||
|
||||
// 跳过未连接到网格或不活跃的 provider(使用 util 判断并传入当前 grid)
|
||||
if (!PatternProviderDataUtil.isProviderAvailable(ppl, grid)) continue;
|
||||
|
||||
// 直接打开供应器自身的 UI(调用 Host 默认方法)
|
||||
try {
|
||||
// 部件与方块实体分别选择定位器
|
||||
// 部件与方块实体分别选择定位器并打开界面
|
||||
if (host instanceof AEBasePart part) {
|
||||
host.openMenu(player, MenuLocators.forPart(part));
|
||||
highlightWithMessage(pbe.getBlockPos(), part.getSide(), Objects.requireNonNull(pbe.getLevel()).dimension(), 1.0, player);
|
||||
} else {
|
||||
host.openMenu(player, MenuLocators.forBlockEntity(pbe));
|
||||
highlightWithMessage(pbe.getBlockPos(), null, Objects.requireNonNull(pbe.getLevel()).dimension(), 1.0, player);
|
||||
}
|
||||
context.setPacketHandled(true);
|
||||
|
||||
// 高亮打开的供应器位置并发送聊天提示
|
||||
|
||||
|
||||
// 先在该 provider 中定位 pattern 的槽位索引,以便计算页码(尽量早退出,按槽位逐个解码)
|
||||
int foundSlot = PatternProviderDataUtil.findSlotForPattern(ppl, pattern.getDefinition());
|
||||
if (foundSlot >= 0) {
|
||||
int pageId = foundSlot / 36;
|
||||
if (pageId > 0) {
|
||||
// 发送 S2C 包通知客户端切换到指定页(客户端会写入 mixin 字段并重排槽位)
|
||||
ModNetwork.CHANNEL.sendTo(new SetProviderPageS2CPacket(pageId), player.connection.connection, NetworkDirection.PLAY_TO_CLIENT);
|
||||
}
|
||||
}
|
||||
|
||||
// 最后发送高亮包,保证界面已打开
|
||||
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 void highlightWithMessage(BlockPos pos, Direction face, ResourceKey<Level> dim, double multiplier, Player player) {
|
||||
if (pos == null || dim == null) {
|
||||
return;
|
||||
}
|
||||
long endTime = System.currentTimeMillis() + (long) (6000 * GlodUtil.clamp(multiplier, 1, 30));
|
||||
if (face == null) {
|
||||
highlight(pos, dim, endTime);
|
||||
} else {
|
||||
var origin = new AABB(2 / 16D, 2 / 16D, 0, 14 / 16D, 14 / 16D, 2 / 16D).move(pos);
|
||||
var center = new AABB(pos).getCenter();
|
||||
switch (face) {
|
||||
case WEST -> origin = FCClientUtil.rotor(origin, center, Direction.Axis.Y, (float) (Math.PI / 2));
|
||||
case SOUTH -> origin = FCClientUtil.rotor(origin, center, Direction.Axis.Y, (float) Math.PI);
|
||||
case EAST -> origin = FCClientUtil.rotor(origin, center, Direction.Axis.Y, (float) (-Math.PI / 2));
|
||||
case UP -> origin = FCClientUtil.rotor(origin, center, Direction.Axis.X, (float) (-Math.PI / 2));
|
||||
case DOWN -> origin = FCClientUtil.rotor(origin, center, Direction.Axis.X, (float) (Math.PI / 2));
|
||||
}
|
||||
highlight(pos, face, dim, endTime, origin);
|
||||
}
|
||||
|
||||
if (player != null) {
|
||||
player.displayClientMessage(Component.translatable("chat.ex_pattern_access_terminal.pos", pos.toShortString(), dim.location().getPath()), false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -60,6 +60,18 @@ public class ModNetwork {
|
|||
.consumerNetworkThread(ProvidersListS2CPacket::handle)
|
||||
.add();
|
||||
|
||||
CHANNEL.messageBuilder(SetPatternHighlightS2CPacket.class, nextId(), NetworkDirection.PLAY_TO_CLIENT)
|
||||
.encoder(SetPatternHighlightS2CPacket::encode)
|
||||
.decoder(SetPatternHighlightS2CPacket::decode)
|
||||
.consumerNetworkThread(SetPatternHighlightS2CPacket::handle)
|
||||
.add();
|
||||
|
||||
CHANNEL.messageBuilder(SetProviderPageS2CPacket.class, nextId(), NetworkDirection.PLAY_TO_CLIENT)
|
||||
.encoder(SetProviderPageS2CPacket::encode)
|
||||
.decoder(SetProviderPageS2CPacket::decode)
|
||||
.consumerNetworkThread(SetProviderPageS2CPacket::handle)
|
||||
.add();
|
||||
|
||||
CHANNEL.messageBuilder(ToggleAdvancedBlockingC2SPacket.class, nextId(), NetworkDirection.PLAY_TO_SERVER)
|
||||
.encoder(ToggleAdvancedBlockingC2SPacket::encode)
|
||||
.decoder(ToggleAdvancedBlockingC2SPacket::decode)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,45 @@
|
|||
package com.extendedae_plus.network;
|
||||
|
||||
import appeng.api.stacks.AEKey;
|
||||
import com.extendedae_plus.content.ClientPatternHighlightStore;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraftforge.network.NetworkEvent;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* S2C: 指示客户端对某个 AEKey 的样板进行高亮/取消高亮(仅作用于接收该包的客户端)
|
||||
*/
|
||||
public class SetPatternHighlightS2CPacket {
|
||||
private final AEKey key;
|
||||
private final boolean highlight;
|
||||
|
||||
public SetPatternHighlightS2CPacket(AEKey key, boolean highlight) {
|
||||
this.key = key;
|
||||
this.highlight = highlight;
|
||||
}
|
||||
|
||||
public static void encode(SetPatternHighlightS2CPacket msg, FriendlyByteBuf buf) {
|
||||
AEKey.writeKey(buf, msg.key);
|
||||
buf.writeBoolean(msg.highlight);
|
||||
}
|
||||
|
||||
public static SetPatternHighlightS2CPacket decode(FriendlyByteBuf buf) {
|
||||
AEKey key = AEKey.readKey(buf);
|
||||
boolean h = buf.readBoolean();
|
||||
return new SetPatternHighlightS2CPacket(key, h);
|
||||
}
|
||||
|
||||
public static void handle(SetPatternHighlightS2CPacket msg, Supplier<NetworkEvent.Context> ctxSupplier) {
|
||||
var ctx = ctxSupplier.get();
|
||||
ctx.enqueueWork(() -> {
|
||||
try {
|
||||
ClientPatternHighlightStore.setHighlight(msg.key, msg.highlight);
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
});
|
||||
ctx.setPacketHandled(true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
package com.extendedae_plus.network;
|
||||
|
||||
import appeng.menu.SlotSemantics;
|
||||
import com.glodblock.github.extendedae.client.gui.GuiExPatternProvider;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.screens.Screen;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraftforge.network.NetworkEvent;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* S2C: 指示客户端在已打开的样板供应器界面切换到指定页
|
||||
*/
|
||||
public class SetProviderPageS2CPacket {
|
||||
private final int page;
|
||||
|
||||
public SetProviderPageS2CPacket(int page) {
|
||||
this.page = page;
|
||||
}
|
||||
|
||||
public static void encode(SetProviderPageS2CPacket msg, FriendlyByteBuf buf) {
|
||||
buf.writeVarInt(msg.page);
|
||||
}
|
||||
|
||||
public static SetProviderPageS2CPacket decode(FriendlyByteBuf buf) {
|
||||
int p = buf.readVarInt();
|
||||
return new SetProviderPageS2CPacket(p);
|
||||
}
|
||||
|
||||
public static void handle(SetProviderPageS2CPacket msg, Supplier<NetworkEvent.Context> ctxSupplier) {
|
||||
var ctx = ctxSupplier.get();
|
||||
ctx.enqueueWork(() -> {
|
||||
try {
|
||||
Screen screen = Minecraft.getInstance().screen;
|
||||
if (screen instanceof GuiExPatternProvider guiExPatternProvider) {
|
||||
Field currentPage = screen.getClass().getDeclaredField("eap$currentPage");
|
||||
currentPage.setAccessible(true);
|
||||
currentPage.setInt(guiExPatternProvider, msg.page);
|
||||
|
||||
|
||||
guiExPatternProvider.repositionSlots(SlotSemantics.ENCODED_PATTERN);
|
||||
guiExPatternProvider.repositionSlots(SlotSemantics.STORAGE);
|
||||
|
||||
Field hs = screen.getClass().getDeclaredField("hoveredSlot");
|
||||
hs.setAccessible(true);
|
||||
hs.set(screen, null);
|
||||
}
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
}
|
||||
);
|
||||
ctx.setPacketHandled(true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -2,11 +2,17 @@ package com.extendedae_plus.util;
|
|||
|
||||
import appeng.api.crafting.PatternDetailsHelper;
|
||||
import appeng.api.stacks.GenericStack;
|
||||
import appeng.client.gui.me.patternaccess.PatternContainerRecord;
|
||||
import appeng.client.gui.me.patternaccess.PatternSlot;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.Font;
|
||||
import net.minecraft.client.gui.GuiGraphics;
|
||||
import net.minecraft.world.inventory.Slot;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
|
||||
/**
|
||||
* GUI工具类,提供样板获取、绘制等通用功能
|
||||
|
|
@ -77,4 +83,105 @@ public class GuiUtil {
|
|||
guiGraphics.drawString(font, text, (int)(textX / scale), (int)(textY / scale), 0xFFFFFFFF, true);
|
||||
guiGraphics.pose().popPose();
|
||||
}
|
||||
|
||||
// Helper: add alpha channel to RGB (rgb is 0xRRGGBB)
|
||||
private static int withAlpha(int rgb, int alpha255) {
|
||||
return ((alpha255 & 0xFF) << 24) | (rgb & 0x00FFFFFF);
|
||||
}
|
||||
|
||||
// HSV -> RGB (returns 0xRRGGBB)
|
||||
private static int hsvToRgb(float h, float s, float v) {
|
||||
if (s <= 0.0f) {
|
||||
int g = Math.round(v * 255.0f);
|
||||
return (g << 16) | (g << 8) | g;
|
||||
}
|
||||
float hh = (h - (float) Math.floor(h)) * 6.0f;
|
||||
int sector = (int) Math.floor(hh);
|
||||
float f = hh - sector;
|
||||
float p = v * (1.0f - s);
|
||||
float q = v * (1.0f - s * f);
|
||||
float t = v * (1.0f - s * (1.0f - f));
|
||||
float r, g, b;
|
||||
switch (sector) {
|
||||
case 0: r = v; g = t; b = p; break;
|
||||
case 1: r = q; g = v; b = p; break;
|
||||
case 2: r = p; g = v; b = t; break;
|
||||
case 3: r = p; g = q; b = v; break;
|
||||
case 4: r = t; g = p; b = v; break;
|
||||
default: r = v; g = p; b = q; break;
|
||||
}
|
||||
int ri = Math.round(r * 255.0f);
|
||||
int gi = Math.round(g * 255.0f);
|
||||
int bi = Math.round(b * 255.0f);
|
||||
return (ri << 16) | (gi << 8) | bi;
|
||||
}
|
||||
|
||||
// 返回当前时间对应的彩虹色(0xRRGGBB),周期固定为 4000ms
|
||||
private static int getRainbowRgb() {
|
||||
long now = System.currentTimeMillis();
|
||||
final long rainbowPeriodMs = 4000L;
|
||||
float hue = (now % rainbowPeriodMs) / (float) rainbowPeriodMs; // 0.0 ~ 1.0
|
||||
return hsvToRgb(hue, 1.0f, 1.0f);
|
||||
}
|
||||
|
||||
// 在给定槽位坐标绘制 1px 边框(18x18)和 16x16 半透明背景
|
||||
private static void drawSlotBox(GuiGraphics guiGraphics, int sx, int sy, int borderColor, int backgroundColor) {
|
||||
guiGraphics.fill(sx - 1, sy - 1, sx + 17, sy, borderColor);
|
||||
guiGraphics.fill(sx - 1, sy + 16, sx + 17, sy + 17, borderColor);
|
||||
guiGraphics.fill(sx - 1, sy, sx, sy + 16, borderColor);
|
||||
guiGraphics.fill(sx + 16, sy, sx + 17, sy + 16, borderColor);
|
||||
guiGraphics.fill(sx, sy, sx + 16, sy + 16, backgroundColor);
|
||||
}
|
||||
|
||||
/**
|
||||
* 在槽位上绘制彩色流转的高亮和浅底色
|
||||
*/
|
||||
public static void drawPatternSlotHighlights(GuiGraphics guiGraphics, List<Slot> slots, Set<ItemStack> matchedStack, Set<PatternContainerRecord> matchedProvider) {
|
||||
if (slots == null) return;
|
||||
|
||||
int rainbowRgb = getRainbowRgb();
|
||||
|
||||
for (Slot slot : slots) {
|
||||
if (!(slot instanceof PatternSlot ps)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int sx = slot.x;
|
||||
int sy = slot.y;
|
||||
|
||||
boolean isMatchedSlot = matchedStack != null && matchedStack.contains(slot.getItem());
|
||||
boolean isMatchedProvider = false;
|
||||
try {
|
||||
PatternContainerRecord container = ps.getMachineInv();
|
||||
isMatchedProvider = matchedProvider != null && matchedProvider.contains(container);
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
|
||||
int borderColor;
|
||||
int backgroundColor;
|
||||
|
||||
if (isMatchedSlot) {
|
||||
borderColor = withAlpha(rainbowRgb, 0xA0);
|
||||
backgroundColor = withAlpha(rainbowRgb, 0x3C);
|
||||
} else if (!isMatchedProvider) {
|
||||
borderColor = withAlpha(0xFFFFFF, 0x40);
|
||||
backgroundColor = withAlpha(0x000000, 0x18);
|
||||
} else {
|
||||
borderColor = withAlpha(0xFFFFFF, 0x30);
|
||||
backgroundColor = withAlpha(0xFFFFFF, 0x14);
|
||||
}
|
||||
|
||||
drawSlotBox(guiGraphics, sx, sy, borderColor, backgroundColor);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 在指定槽位坐标绘制彩虹流转的边框与浅底色(用于非 PatternSlot 的高亮场景)
|
||||
*/
|
||||
public static void drawSlotRainbowHighlight(GuiGraphics guiGraphics, int sx, int sy) {
|
||||
int rainbowRgb = getRainbowRgb();
|
||||
int borderColor = withAlpha(rainbowRgb, 0xA0);
|
||||
int backgroundColor = withAlpha(rainbowRgb, 0x3C);
|
||||
drawSlotBox(guiGraphics, sx, sy, borderColor, backgroundColor);
|
||||
}
|
||||
}
|
||||
|
|
@ -3,11 +3,15 @@ package com.extendedae_plus.util;
|
|||
import appeng.api.crafting.IPatternDetails;
|
||||
import appeng.api.crafting.PatternDetailsHelper;
|
||||
import appeng.api.inventories.InternalInventory;
|
||||
import appeng.api.networking.IGrid;
|
||||
import appeng.api.stacks.AEKey;
|
||||
import appeng.api.stacks.GenericStack;
|
||||
import appeng.helpers.patternprovider.PatternProviderLogic;
|
||||
import com.extendedae_plus.mixin.ae2.accessor.PatternProviderLogicAccessor;
|
||||
import com.mojang.logging.LogUtils;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
|
|
@ -230,26 +234,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;
|
||||
}
|
||||
|
|
@ -257,12 +243,15 @@ public class PatternProviderDataUtil {
|
|||
// 遍历所有样板槽位
|
||||
for (int i = 0; i < patternInventory.size(); i++) {
|
||||
ItemStack patternStack = patternInventory.getStackInSlot(i);
|
||||
if (!patternStack.isEmpty()) {
|
||||
if (patternStack.isEmpty()) continue;
|
||||
try {
|
||||
// 解码样板
|
||||
IPatternDetails patternDetails = PatternDetailsHelper.decodePattern(patternStack, level);
|
||||
if (patternDetails != null) {
|
||||
patternDataList.add(new PatternData(patternDetails, patternStack, i));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
if (DEBUG) LogUtils.getLogger().debug("Pattern decode failed at slot {}: {}", i, e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -353,24 +342,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;
|
||||
}
|
||||
|
|
@ -429,6 +401,59 @@ public class PatternProviderDataUtil {
|
|||
return patternProvider.getGrid() != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断 provider 是否可用并属于指定网格(在线且有频道/处于活跃状态)
|
||||
*/
|
||||
public static boolean isProviderAvailable(PatternProviderLogic provider, IGrid expectedGrid) {
|
||||
if (provider == null || expectedGrid == null) return false;
|
||||
try {
|
||||
var grid = provider.getGrid();
|
||||
if (grid == null || !grid.equals(expectedGrid)) return false;
|
||||
|
||||
// 使用 accessor 获取 mainNode,再调用 isActive
|
||||
if (provider instanceof PatternProviderLogicAccessor accessor) {
|
||||
var mainNode = accessor.eap$mainNode();
|
||||
if (mainNode == null) return false;
|
||||
try {
|
||||
var isActiveMethod = mainNode.getClass().getMethod("isActive");
|
||||
Object active = isActiveMethod.invoke(mainNode);
|
||||
if (active instanceof Boolean && !((Boolean) active)) return false;
|
||||
} catch (NoSuchMethodException nsme) {
|
||||
// 没有 isActive 方法时,退回到检查 channels
|
||||
try {
|
||||
var getChannels = mainNode.getClass().getMethod("getChannels");
|
||||
Object channels = getChannels.invoke(mainNode);
|
||||
if (channels instanceof java.util.Collection) {
|
||||
if (((java.util.Collection<?>) channels).isEmpty()) return false;
|
||||
}
|
||||
} catch (Exception ignored) {
|
||||
// 无法判断 channels 时,认为不可用
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 没有 accessor 的情况,尽量通过反射判断 mainNode.channels
|
||||
try {
|
||||
var mainNodeField = provider.getClass().getDeclaredField("mainNode");
|
||||
mainNodeField.setAccessible(true);
|
||||
var mainNode = mainNodeField.get(provider);
|
||||
if (mainNode == null) return false;
|
||||
var getChannelsMethod = mainNode.getClass().getMethod("getChannels");
|
||||
Object channels = getChannelsMethod.invoke(mainNode);
|
||||
if (channels instanceof java.util.Collection) {
|
||||
return !((java.util.Collection<?>) channels).isEmpty();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查样板供应器是否处于活跃状态
|
||||
*
|
||||
|
|
@ -443,18 +468,21 @@ public class PatternProviderDataUtil {
|
|||
if (grid == null) {
|
||||
return false;
|
||||
}
|
||||
// 检查网格节点是否活跃
|
||||
// 检查网格节点是否活跃(使用 accessor 代替反射)
|
||||
try {
|
||||
// 使用反射安全地访问mainNode字段
|
||||
var mainNodeField = patternProvider.getClass().getDeclaredField("mainNode");
|
||||
mainNodeField.setAccessible(true);
|
||||
var mainNode = mainNodeField.get(patternProvider);
|
||||
if (mainNode != null) {
|
||||
var isActiveMethod = mainNode.getClass().getMethod("isActive");
|
||||
return (Boolean) isActiveMethod.invoke(mainNode);
|
||||
if (patternProvider instanceof PatternProviderLogicAccessor accessor) {
|
||||
var mainNode = accessor.eap$mainNode();
|
||||
if (mainNode != null) {
|
||||
try {
|
||||
var isActiveMethod = mainNode.getClass().getMethod("isActive");
|
||||
return (Boolean) isActiveMethod.invoke(mainNode);
|
||||
} catch (Exception e) {
|
||||
// 无法调用 isActive 时,认为活跃
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// 如果反射失败,假设是活跃的
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
@ -522,6 +550,34 @@ public class PatternProviderDataUtil {
|
|||
return scalePatternAmountsExtendedAEStyle(patternProvider, multiplier, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查找 provider 中匹配给定定义的样板槽位(轻量、按需解码并早退出)
|
||||
* @param patternProvider 要搜索的 provider
|
||||
* @param targetDefinition pattern.getDefinition() 返回的对象(用于 equals 比较)
|
||||
* @return 找到的槽位索引,未找到返回 -1
|
||||
*/
|
||||
public static int findSlotForPattern(PatternProviderLogic patternProvider, Object targetDefinition) {
|
||||
if (patternProvider == null || targetDefinition == null) return -1;
|
||||
InternalInventory inv = patternProvider.getPatternInv();
|
||||
if (inv == null) return -1;
|
||||
Level level = getPatternProviderLevel(patternProvider);
|
||||
if (level == null) return -1;
|
||||
|
||||
for (int i = 0; i < inv.size(); i++) {
|
||||
ItemStack s = inv.getStackInSlot(i);
|
||||
if (s.isEmpty()) continue;
|
||||
try {
|
||||
IPatternDetails d = PatternDetailsHelper.decodePattern(s, level);
|
||||
if (d != null && d.getDefinition().equals(targetDefinition)) {
|
||||
return i;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
if (DEBUG) LogUtils.getLogger().debug("findSlotForPattern decode failed at {}: {}", i, e.toString());
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* ExtendedAE风格的样板复制倍增
|
||||
* 支持更精确的样板处理和错误恢复
|
||||
|
|
@ -862,50 +918,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 +956,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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@
|
|||
"ae2.accessor.PatternAccessTermScreenSlotsRowAccessor",
|
||||
"ae2.client.gui.AEBaseScreenMixin",
|
||||
"ae2.client.gui.PatternEncodingTermScreenMixin",
|
||||
"ae2.client.gui.PatternProviderCloseMixin",
|
||||
"ae2.client.gui.PatternProviderScreenMixin",
|
||||
"ae2.client.gui.SlotGridLayoutMixin",
|
||||
"extendedae.accessor.GuiExPatternTerminalAccessor",
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user