This commit is contained in:
GaLicn 2025-09-06 17:35:34 +08:00
parent d7a690da14
commit 27f7ed0122
16 changed files with 105 additions and 273 deletions

View File

@ -135,21 +135,13 @@ sourceSets.main.java {
// accessor
include 'com/extendedae_plus/mixin/**/accessor/**'
// accessor mixin
// AE2 accessor
exclude 'com/extendedae_plus/mixin/ae2/helpers/**'
exclude 'com/extendedae_plus/mixin/ae2/autopattern/**'
exclude 'com/extendedae_plus/mixin/ae2/client/**'
exclude 'com/extendedae_plus/mixin/ae2/menu/**'
exclude 'com/extendedae_plus/mixin/ae2/AEProcessingPatternMixin.java'
// ae2 mixin accessor
exclude 'com/extendedae_plus/mixin/ae2/*.java'
// AE2 accessor
// accessor mixin
exclude 'com/extendedae_plus/mixin/ae2WTlib/**'
// mixin/jei/accessor
exclude 'com/extendedae_plus/mixin/jei/*.java'
// mixin
exclude 'com/extendedae_plus/mixin/PickFromWirelessMixin.java'
exclude 'com/extendedae_plus/mixin/extendedae/**'
}
configurations {

View File

@ -4,6 +4,7 @@ import org.slf4j.Logger;
import com.mojang.logging.LogUtils;
import com.extendedae_plus.config.ModConfigs;
import com.extendedae_plus.init.ModMenuTypes;
import com.extendedae_plus.network.ModNetwork;
import net.minecraft.resources.ResourceLocation;
@ -80,6 +81,8 @@ public class ExtendedAEPlus {
ITEMS.register(modEventBus);
// Register the Deferred Register to the mod event bus so tabs get registered
CREATIVE_MODE_TABS.register(modEventBus);
// Register the Deferred Register to the mod event bus so menu types get registered
ModMenuTypes.MENUS.register(modEventBus);
// Register ourselves for server and other game events we are interested in.
// Note that this is necessary if and only if we want *this* class (ExtendedAEPlus) to respond directly to events.

View File

@ -3,9 +3,10 @@ package com.extendedae_plus.mixin.ae2;
import appeng.crafting.pattern.EncodedPatternItem;
import com.extendedae_plus.config.ModConfigs;
import net.minecraft.ChatFormatting;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.core.component.DataComponents;
import net.minecraft.network.chat.Component;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.component.CustomData;
import net.minecraft.world.item.TooltipFlag;
import net.minecraft.world.level.Level;
import org.spongepowered.asm.mixin.Mixin;
@ -20,10 +21,13 @@ public class EncodedPatternItemMixin {
// 客户端 HoverText 显示样板的编码玩家
@Inject(method = "appendHoverText", at = @At("TAIL"))
public void epp$appendHoverText(ItemStack stack, Level level, List<Component> lines, TooltipFlag advancedTooltips, CallbackInfo ci){
if (stack.hasTag() && ModConfigs.SHOW_ENCOD_PATTERN_PLAYER.get()) {
CompoundTag tag = stack.getOrCreateTag();
String name = tag.getString("encodePlayer");
lines.add(Component.translatable("extendedae_plus.pattern.hovertext.player", name).withStyle(ChatFormatting.GRAY));
if (ModConfigs.SHOW_ENCOD_PATTERN_PLAYER.get()) {
var customData = stack.getOrDefault(DataComponents.CUSTOM_DATA, CustomData.EMPTY);
var tag = customData.copyTag();
if (tag.contains("encodePlayer")) {
String name = tag.getString("encodePlayer");
lines.add(Component.translatable("extendedae_plus.pattern.hovertext.player", name).withStyle(ChatFormatting.GRAY));
}
}
}
}

View File

@ -21,7 +21,7 @@ import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.HitResult;
import net.minecraft.world.phys.Vec3;
import net.minecraftforge.fml.ModList;
import net.neoforged.fml.ModList;
import org.lwjgl.glfw.GLFW;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique;

View File

@ -28,6 +28,7 @@ import net.minecraft.client.renderer.Rect2i;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.contents.TranslatableContents;
import net.minecraft.world.inventory.Slot;
import net.neoforged.neoforge.network.PacketDistributor;
import org.jetbrains.annotations.Nullable;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
@ -79,7 +80,7 @@ public abstract class AEBaseScreenMixin {
try {
LogUtils.getLogger().info("EAP: Send CraftingMonitorJumpC2SPacket: {}", key);
} catch (Throwable ignored2) {}
ModNetwork.CHANNEL.sendToServer(new CraftingMonitorJumpC2SPacket(key));
PacketDistributor.sendToServer(new CraftingMonitorJumpC2SPacket(key));
cir.setReturnValue(true);
} catch (Throwable ignored) {
}
@ -113,7 +114,7 @@ public abstract class AEBaseScreenMixin {
try {
LogUtils.getLogger().info("EAP: Send CraftingMonitorOpenProviderC2SPacket: {}", key);
} catch (Throwable ignored2) {}
ModNetwork.CHANNEL.sendToServer(new CraftingMonitorOpenProviderC2SPacket(key));
PacketDistributor.sendToServer(new CraftingMonitorOpenProviderC2SPacket(key));
cir.setReturnValue(true);
} catch (Throwable ignored) {
}
@ -183,10 +184,10 @@ public abstract class AEBaseScreenMixin {
GuiUtil.drawAmountText(guiGraphics, font, amountText, appEngSlot.x, appEngSlot.y, 0.6f);
try {
var details = PatternDetailsHelper.decodePattern(itemStack, Minecraft.getInstance().level, false);
var details = PatternDetailsHelper.decodePattern(itemStack, Minecraft.getInstance().level);
try {
if (details != null && details.getOutputs() != null && details.getOutputs().length > 0) {
AEKey key = details.getOutputs()[0].what();
if (details != null && details.getOutputs() != null && !details.getOutputs().isEmpty()) {
AEKey key = details.getOutputs().get(0).what();
if (key != null && ClientPatternHighlightStore.hasHighlight(key)) {
try {
GuiUtil.drawSlotRainbowHighlight(guiGraphics, s.x, s.y);

View File

@ -15,6 +15,7 @@ import net.minecraft.client.gui.GuiGraphics;
import net.minecraft.client.gui.components.Tooltip;
import net.minecraft.client.renderer.Rect2i;
import net.minecraft.network.chat.Component;
import net.neoforged.neoforge.network.PacketDistributor;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
@ -40,7 +41,7 @@ public abstract class PatternEncodingTermScreenMixin<T extends AEBaseMenu> {
}
// 复用已存在的按钮实例避免重复创建
if (eap$uploadBtn == null) {
eap$uploadBtn = new IconButton(btn -> ModNetwork.CHANNEL
eap$uploadBtn = new IconButton(btn -> PacketDistributor
.sendToServer(new com.extendedae_plus.network.RequestProvidersListC2SPacket())) {
private final float eap$scale = 0.75f; // 12x12

View File

@ -5,12 +5,14 @@ import appeng.menu.me.items.PatternEncodingTermMenu;
import appeng.menu.slot.RestrictedInputSlot;
import appeng.parts.encoding.EncodingMode;
import com.extendedae_plus.util.ExtendedAEPatternUploadUtil;
import com.glodblock.github.glodium.network.packet.sync.ActionMap;
import com.glodblock.github.glodium.network.packet.sync.IActionHolder;
import com.glodblock.github.glodium.network.packet.sync.Paras;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import org.jetbrains.annotations.NotNull;
import net.minecraft.core.component.DataComponents;
import net.minecraft.world.item.component.CustomData;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique;
@ -30,7 +32,7 @@ import java.util.function.Consumer;
public abstract class ContainerPatternEncodingTermMenuMixin implements IActionHolder {
@Unique
private final Map<String, Consumer<Paras>> eap$actions = createHolder();
private final ActionMap eap$actions = ActionMap.create();
@Unique
private Player epp$player;
@ -75,7 +77,7 @@ public abstract class ContainerPatternEncodingTermMenuMixin implements IActionHo
@NotNull
@Override
public Map<String, Consumer<Paras>> getActionMap() {
public ActionMap getActionMap() {
return this.eap$actions;
}
@ -117,7 +119,11 @@ public abstract class ContainerPatternEncodingTermMenuMixin implements IActionHo
@Inject(method = "encodePattern", at = @At("TAIL"), remap = false, cancellable = true)
private void eap$writeEncodePlayerToPattern(CallbackInfoReturnable<ItemStack> cir) {
ItemStack itemStack = cir.getReturnValue();
itemStack.getOrCreateTag().putString("encodePlayer", this.epp$player.getGameProfile().getName());
cir.setReturnValue(itemStack);
if (itemStack != null && !itemStack.isEmpty()) {
CustomData.update(DataComponents.CUSTOM_DATA, itemStack, tag -> {
tag.putString("encodePlayer", this.epp$player.getGameProfile().getName());
});
cir.setReturnValue(itemStack);
}
}
}

View File

@ -97,6 +97,9 @@ public abstract class MEStorageMenuMixin {
}
Setting<T> typedSetting = (Setting<T>) setting;
T typedValue = (T) value;
client.registerSetting(typedSetting, typedValue);
// 仅当具体实现为 ConfigManager 才能调用 registerSetting
if (client instanceof appeng.util.ConfigManager cm) {
cm.registerSetting(typedSetting, typedValue);
}
}
}

View File

@ -57,7 +57,7 @@ public abstract class PatternEncodingTermMenuMixin {
var current = blankInv.getStackInSlot(0);
int limit = blankInv.getSlotLimit(0);
int space = Math.max(0, limit - current.getCount());
space = Math.min(space, AEItems.BLANK_PATTERN.asItem().getMaxStackSize());
space = Math.min(space, AEItems.BLANK_PATTERN.stack(1).getMaxStackSize());
if (space <= 0) {
return; // 已满无需填充
}
@ -126,7 +126,7 @@ public abstract class PatternEncodingTermMenuMixin {
var current = blankInv.getStackInSlot(0);
int limit = blankInv.getSlotLimit(0);
int space = Math.max(0, limit - current.getCount());
space = Math.min(space, AEItems.BLANK_PATTERN.asItem().getMaxStackSize());
space = Math.min(space, AEItems.BLANK_PATTERN.stack(1).getMaxStackSize());
if (space <= 0) {
this.eap$blankAutoFilled = true;
return;

View File

@ -25,7 +25,7 @@ import java.lang.reflect.Method;
import static com.extendedae_plus.util.ExtendedAELogger.LOGGER;
@Mixin(GuiExPatternProvider.class)
@Mixin(value = GuiExPatternProvider.class, remap = false)
public abstract class GuiExPatternProviderMixin extends PatternProviderScreen<ContainerExPatternProvider> implements ExPatternButtonsAccessor, com.extendedae_plus.api.ExPatternPageAccessor {
@Unique

View File

@ -10,7 +10,6 @@ import appeng.client.gui.widgets.IconButton;
import appeng.menu.AEBaseMenu;
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;
@ -28,6 +27,7 @@ import net.minecraft.world.entity.player.Inventory;
import net.minecraft.world.inventory.Slot;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Level;
import net.neoforged.neoforge.network.PacketDistributor;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.spongepowered.asm.mixin.Mixin;
@ -39,12 +39,16 @@ import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import java.util.Map;
import java.util.Set;
import java.util.HashMap;
import java.util.ArrayList;
import java.util.Collection;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.*;
@Pseudo
@Mixin(value = GuiExPatternTerminal.class)
@Mixin(value = GuiExPatternTerminal.class, remap = false)
public abstract class GuiExPatternTerminalMixin extends AEBaseScreen<AEBaseMenu> {
@Unique
@ -187,7 +191,7 @@ public abstract class GuiExPatternTerminalMixin extends AEBaseScreen<AEBaseMenu>
ArrayList<?> rows = acc.getRows();
// 找到该分组对应的第一个 PatternContainerRecord
Class<?> cls = GuiExPatternTerminal.class;
Class<?> cls = this.getClass();
var byGroupField = cls.getDeclaredField("byGroup");
byGroupField.setAccessible(true);
Object byGroup = byGroupField.get(this); // HashMultimap<PatternContainerGroup, PatternContainerRecord>
@ -236,7 +240,7 @@ public abstract class GuiExPatternTerminalMixin extends AEBaseScreen<AEBaseMenu>
try {
var dimRl = net.minecraft.resources.ResourceLocation.parse(dimStr);
if (dimRl != null) {
ModNetwork.CHANNEL.sendToServer(new OpenProviderUiC2SPacket(
PacketDistributor.sendToServer(new OpenProviderUiC2SPacket(
posLong,
dimRl,
faceOrd
@ -258,8 +262,12 @@ public abstract class GuiExPatternTerminalMixin extends AEBaseScreen<AEBaseMenu>
this.eap$currentlyChoicePatterProvider = -1;
}
@Inject(method = "<init>", at = @At("TAIL"), remap = false)
private void injectConstructor(CallbackInfo ci) {
@Inject(method = "<init>(Lcom/glodblock/github/extendedae/container/ContainerExPatternTerminal;Lnet/minecraft/world/entity/player/Inventory;Lnet/minecraft/network/chat/Component;Lappeng/client/gui/style/ScreenStyle;)V", at = @At("TAIL"), remap = false)
private void injectConstructor(com.glodblock.github.extendedae.container.ContainerExPatternTerminal menu,
Inventory playerInventory,
Component title,
ScreenStyle style,
CallbackInfo ci) {
// 根据配置初始化默认显示/隐藏状态
try {
this.eap$showSlots = ModConfigs.PATTERN_TERMINAL_SHOW_SLOTS_DEFAULT.get();

View File

@ -3,9 +3,11 @@ package com.extendedae_plus.mixin.extendedae.common;
import com.extendedae_plus.config.ModConfigs;
import com.glodblock.github.extendedae.common.parts.PartExPatternProvider;
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.ModifyArg;
@Pseudo
@Mixin(value = PartExPatternProvider.class, priority = 3000, remap = false)
public abstract class PartExPatternProviderMixin {

View File

@ -10,24 +10,22 @@ import appeng.menu.guisync.GuiSync;
import appeng.menu.implementations.PatternProviderMenu;
import appeng.menu.slot.AppEngSlot;
import com.glodblock.github.extendedae.container.ContainerExPatternProvider;
import com.glodblock.github.glodium.network.packet.sync.IActionHolder;
import com.glodblock.github.glodium.network.packet.sync.Paras;
import net.minecraft.world.entity.player.Inventory;
import net.minecraft.world.inventory.MenuType;
import net.minecraft.world.inventory.Slot;
import org.jetbrains.annotations.NotNull;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Pseudo;
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;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
@Mixin(value = ContainerExPatternProvider.class, priority = 3000)
public abstract class ContainerExPatternProviderMixin extends PatternProviderMenu implements IActionHolder {
@Pseudo
@Mixin(value = ContainerExPatternProvider.class, priority = 3000, remap = false)
public abstract class ContainerExPatternProviderMixin extends PatternProviderMenu {
// 使用高位唯一ID避免与其他模组在同一类上的 @GuiSync 冲突
@GuiSync(31415)
@ -40,8 +38,7 @@ public abstract class ContainerExPatternProviderMixin extends PatternProviderMen
@Unique
private static final int SLOTS_PER_PAGE = 36; // 每页显示36个槽位
@Unique
private final Map<String, Consumer<Paras>> eap$actions = createHolder();
// glodium IActionHolder 已移除相关 actionMap 由专用网络包替代
public ContainerExPatternProviderMixin(MenuType<? extends PatternProviderMenu> menuType, int id, Inventory playerInventory, PatternProviderLogicHost host) {
super(menuType, id, playerInventory, host);
@ -72,18 +69,10 @@ public abstract class ContainerExPatternProviderMixin extends PatternProviderMen
}
}
@Inject(method = "<init>", at = @At("TAIL"))
public void init(int id, Inventory playerInventory, PatternProviderLogicHost host, CallbackInfo ci) {
@Inject(method = "<init>(ILnet/minecraft/world/entity/player/Inventory;Lappeng/helpers/patternprovider/PatternProviderLogicHost;)V", at = @At("TAIL"), remap = false, require = 0)
private void eap$initPages(int id, Inventory playerInventory, PatternProviderLogicHost host, CallbackInfo ci) {
int maxSlots = this.getSlots(SlotSemantics.ENCODED_PATTERN).size();
this.eap$maxPage = (maxSlots + SLOTS_PER_PAGE - 1) / SLOTS_PER_PAGE;
// 注册通用动作 CGenericPacket 分发
this.eap$actions.put("multiply2", p -> { eap$modifyPatterns(2, false); });
this.eap$actions.put("divide2", p -> { eap$modifyPatterns(2, true); });
this.eap$actions.put("multiply5", p -> { eap$modifyPatterns(5, false); });
this.eap$actions.put("divide5", p -> { eap$modifyPatterns(5, true); });
this.eap$actions.put("multiply10", p -> { eap$modifyPatterns(10, false);});
this.eap$actions.put("divide10", p -> { eap$modifyPatterns(10, true); });
}
@Unique
@ -102,15 +91,13 @@ public abstract class ContainerExPatternProviderMixin extends PatternProviderMen
for (var slot : this.getSlots(SlotSemantics.ENCODED_PATTERN)) {
var stack = slot.getItem();
if (stack.getItem() instanceof EncodedPatternItem pattern) {
var detail = pattern.decode(stack, this.getPlayer().level(), false);
var detail = PatternDetailsHelper.decodePattern(stack, this.getPlayer().level());
if (detail instanceof AEProcessingPattern process) {
var input = process.getSparseInputs();
var output = process.getOutputs();
var input = process.getSparseInputs(); // List<GenericStack>
var output = process.getOutputs(); // List<GenericStack>
if (eap$checkModify(input, scale, div) && eap$checkModify(output, scale, div)) {
var mulInput = new GenericStack[input.length];
var mulOutput = new GenericStack[output.length];
eap$modifyStacks(input, mulInput, scale, div);
eap$modifyStacks(output, mulOutput, scale, div);
var mulInput = eap$modifyStacks(input, scale, div);
var mulOutput = eap$modifyStacks(output, scale, div);
var newPattern = PatternDetailsHelper.encodeProcessingPattern(mulInput, mulOutput);
slot.set(newPattern);
}
@ -120,7 +107,7 @@ public abstract class ContainerExPatternProviderMixin extends PatternProviderMen
}
@Unique
private boolean eap$checkModify(GenericStack[] stacks, int scale, boolean div) {
private boolean eap$checkModify(java.util.List<GenericStack> stacks, int scale, boolean div) {
if (stacks == null) return false;
if (div) {
for (var stack : stacks) {
@ -145,22 +132,17 @@ public abstract class ContainerExPatternProviderMixin extends PatternProviderMen
}
@Unique
private void eap$modifyStacks(GenericStack[] src, GenericStack[] dst, int scale, boolean div) {
for (int i = 0; i < src.length; i++) {
var stack = src[i];
private java.util.List<GenericStack> eap$modifyStacks(java.util.List<GenericStack> src, int scale, boolean div) {
var dst = new java.util.ArrayList<GenericStack>(src.size());
for (var stack : src) {
if (stack != null) {
long amt = stack.amount();
long newAmt = div ? (amt / scale) : (amt * scale);
dst[i] = new GenericStack(stack.what(), newAmt);
dst.add(new GenericStack(stack.what(), newAmt));
} else {
dst[i] = null;
dst.add(null);
}
}
}
@NotNull
@Override
public Map<String, Consumer<Paras>> getActionMap() {
return this.eap$actions;
return dst;
}
}

View File

@ -1,41 +1,21 @@
package com.extendedae_plus.mixin.extendedae.container;
import appeng.api.util.IConfigurableObject;
import appeng.api.storage.IPatternAccessTermMenuHost;
import appeng.menu.guisync.GuiSync;
import com.extendedae_plus.util.ExtendedAEPatternUploadUtil;
import com.glodblock.github.extendedae.container.ContainerExPatternTerminal;
import com.glodblock.github.glodium.network.packet.sync.IActionHolder;
import com.glodblock.github.glodium.network.packet.sync.Paras;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.core.registries.Registries;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.InteractionResult;
import net.minecraft.world.MenuProvider;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.Vec3;
import net.minecraftforge.network.NetworkHooks;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.jetbrains.annotations.NotNull;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Pseudo;
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;
import java.util.Map;
import java.util.function.Consumer;
@Mixin(ContainerExPatternTerminal.class)
public abstract class ContainerExPatternTerminalMixin implements IActionHolder {
@Pseudo
@Mixin(value = ContainerExPatternTerminal.class, remap = false)
public abstract class ContainerExPatternTerminalMixin {
@GuiSync(25564)
@Unique
@ -56,142 +36,15 @@ public abstract class ContainerExPatternTerminalMixin implements IActionHolder {
this.eap$hidePatternSlots = !this.eap$hidePatternSlots;
}
@Unique
private Map<String, Consumer<Paras>> eap$actions;
@Unique
private Player epp$player;
@Unique
private static final Logger EAP_LOGGER = LogManager.getLogger("ExtendedAE_Plus");
@Inject(method = "<init>*", at = @At("TAIL"))
private void init(int id, net.minecraft.world.entity.player.Inventory playerInventory, IConfigurableObject host, CallbackInfo ci) {
if (this.eap$actions == null) {
this.eap$actions = createHolder();
}
@Inject(method = "<init>*", at = @At("TAIL"), remap = false)
private void init(int id, net.minecraft.world.entity.player.Inventory playerInventory, IPatternAccessTermMenuHost host, CallbackInfo ci) {
this.epp$player = playerInventory.player;
// 注册上传动作参数顺序必须与客户端 CGenericPacket 保持一致
this.eap$actions.put("upload", p -> {
try {
Object o0 = p.get(0);
Object o1 = p.get(1);
int playerSlotIndex = (o0 instanceof Number) ? ((Number) o0).intValue() : Integer.parseInt(String.valueOf(o0));
long providerId = (o1 instanceof Number) ? ((Number) o1).longValue() : Long.parseLong(String.valueOf(o1));
var sp = (ServerPlayer) this.epp$player;
ExtendedAEPatternUploadUtil.uploadPatternToProvider(sp, playerSlotIndex, providerId);
} catch (Throwable ignored) {
}
});
// 注册打开UI动作open_ui(posLong, dimensionId, faceOrdinal?)
this.eap$actions.put("open_ui", p -> {
try {
// 参数解析
Object po = p.get(0); // BlockPos as long (BlockPos#asLong)
Object do0 = p.get(1); // Dimension id string (e.g., minecraft:overworld)
Object fo;
try {
fo = p.get(2); // Optional face ordinal
} catch (Throwable __ignored) {
fo = null;
}
long posLong = (po instanceof Number) ? ((Number) po).longValue() : Long.parseLong(String.valueOf(po));
String dimStr = String.valueOf(do0);
int faceOrd = -1;
if (fo != null) {
faceOrd = (fo instanceof Number) ? ((Number) fo).intValue() : Integer.parseInt(String.valueOf(fo));
}
BlockPos pos = BlockPos.of(posLong);
ResourceLocation dimId = ResourceLocation.tryParse(dimStr);
if (dimId == null) {
EAP_LOGGER.warn("[EPlus] open_ui: invalid dim '{}'", dimStr);
return;
}
ResourceKey<Level> dimKey = ResourceKey.create(Registries.DIMENSION, dimId);
if (!(this.epp$player instanceof ServerPlayer sp)) {
EAP_LOGGER.warn("[EPlus] open_ui: not a ServerPlayer");
return;
}
ServerLevel level = sp.server.getLevel(dimKey);
if (level == null) {
EAP_LOGGER.warn("[EPlus] open_ui: level null for key {}", dimKey);
return;
}
EAP_LOGGER.debug("[EPlus] open_ui: pos={}, dim={}, faceOrd={}", pos, dimKey.location(), faceOrd);
// 目标应为供应器所面向/连接的相邻方块而非供应器自身
Direction[] tries = (faceOrd >= 0 && faceOrd < Direction.values().length)
? new Direction[]{Direction.values()[faceOrd]}
: Direction.values();
// 1) 先尝试在相邻方块直接打开 MenuProvider
for (Direction dir : tries) {
BlockPos targetPos = pos.relative(dir);
BlockEntity be = level.getBlockEntity(targetPos);
if (be instanceof MenuProvider provider) {
NetworkHooks.openScreen(sp, provider, targetPos);
EAP_LOGGER.debug("[EPlus] open_ui: opened BE MenuProvider at {} (neighbor via {})", targetPos, dir);
return;
}
var state = level.getBlockState(targetPos);
MenuProvider provider = state.getMenuProvider(level, targetPos);
if (provider != null) {
NetworkHooks.openScreen(sp, provider, targetPos);
EAP_LOGGER.debug("[EPlus] open_ui: opened State MenuProvider at {} (neighbor via {})", targetPos, dir);
return;
}
}
// 2) 兜底为避免误触发放置/覆盖仅在手上至少有一只手为空时使用 BlockState.use 进行一次徒手交互
boolean hasFace = (faceOrd >= 0 && faceOrd < Direction.values().length);
boolean anyHandEmpty = sp.getMainHandItem().isEmpty() || sp.getOffhandItem().isEmpty();
if (anyHandEmpty) {
InteractionHand hand = sp.getMainHandItem().isEmpty() ? InteractionHand.MAIN_HAND : InteractionHand.OFF_HAND;
if (hasFace) {
Direction dir = Direction.values()[faceOrd];
BlockPos targetPos = pos.relative(dir);
var state2 = level.getBlockState(targetPos);
var hit = new BlockHitResult(Vec3.atCenterOf(targetPos), dir.getOpposite(), targetPos, false);
InteractionResult r = state2.use(level, sp, hand, hit);
EAP_LOGGER.debug("[EPlus] open_ui: fallback(state.use) at {} hit {} (via {}), result={}", targetPos, dir.getOpposite(), dir, r);
} else {
// 无朝向优先尝试有方块实体的邻居否则尝试实心方块邻居各只尝试一次
Direction chosen = null;
for (Direction d : Direction.values()) {
if (level.getBlockEntity(pos.relative(d)) != null) { chosen = d; break; }
}
if (chosen == null) {
for (Direction d : Direction.values()) {
if (!level.getBlockState(pos.relative(d)).isAir()) { chosen = d; break; }
}
}
if (chosen != null) {
BlockPos targetPos = pos.relative(chosen);
var state2 = level.getBlockState(targetPos);
var hit = new BlockHitResult(Vec3.atCenterOf(targetPos), chosen.getOpposite(), targetPos, false);
InteractionResult r = state2.use(level, sp, hand, hit);
EAP_LOGGER.debug("[EPlus] open_ui: fallback(state.use) at {} hit {} (auto via {}), result={}", targetPos, chosen.getOpposite(), chosen, r);
} else {
EAP_LOGGER.debug("[EPlus] open_ui: no neighbor candidate for fallback (faceOrd<0)");
}
}
} else {
EAP_LOGGER.debug("[EPlus] open_ui: skip fallback (hands occupied)");
}
} catch (Throwable ignored) {
}
});
}
@NotNull
@Override
public Map<String, Consumer<Paras>> getActionMap() {
return this.eap$actions;
// glodium IActionHolder 已移除此处逻辑改为使用专用网络包的途径详见 network
}
}

View File

@ -1,57 +1,25 @@
package com.extendedae_plus.mixin.extendedae.container;
import com.extendedae_plus.util.ExtendedAEPatternUploadUtil;
import com.glodblock.github.extendedae.common.me.itemhost.HostWirelessExPAT;
import com.glodblock.github.extendedae.container.ContainerWirelessExPAT;
import com.glodblock.github.glodium.network.packet.sync.IActionHolder;
import com.glodblock.github.glodium.network.packet.sync.Paras;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.entity.player.Player;
import org.jetbrains.annotations.NotNull;
import com.glodblock.github.extendedae.xmod.wt.ContainerWirelessExPAT;
import com.glodblock.github.extendedae.xmod.wt.HostWirelessExPAT;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Pseudo;
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;
import java.util.Map;
import java.util.function.Consumer;
/**
* 为无线样板访问终端容器注册通用动作CGenericPacket 分发
*/
@Pseudo
@Mixin(ContainerWirelessExPAT.class)
public abstract class ContainerWirelessExPatternTerminalMixin implements IActionHolder {
@Mixin(value = ContainerWirelessExPAT.class, remap = false)
public abstract class ContainerWirelessExPatternTerminalMixin {
// 1.21 版本中 ExtendedAE 不再使用 glodium IActionHolder
// 保留空混入以便后续需要时扩展
@Unique
private final Map<String, Consumer<Paras>> eap$actions = createHolder();
@Unique
private Player epp$player;
// 明确目标构造签名<init>(int, Inventory, HostWirelessExPAT)
@Inject(method = "<init>(ILnet/minecraft/world/entity/player/Inventory;Lcom/glodblock/github/extendedae/common/me/itemhost/HostWirelessExPAT;)V", at = @At("TAIL"), require = 0)
private void init(int id, net.minecraft.world.entity.player.Inventory playerInventory, HostWirelessExPAT host, CallbackInfo ci) {
this.epp$player = playerInventory.player;
// 注册上传动作参数顺序必须与客户端 CGenericPacket 保持一致
this.eap$actions.put("upload", p -> {
try {
Object o0 = p.get(0);
Object o1 = p.get(1);
int playerSlotIndex = (o0 instanceof Number) ? ((Number) o0).intValue() : Integer.parseInt(String.valueOf(o0));
long providerId = (o1 instanceof Number) ? ((Number) o1).longValue() : Long.parseLong(String.valueOf(o1));
var sp = (ServerPlayer) this.epp$player;
ExtendedAEPatternUploadUtil.uploadPatternToProvider(sp, playerSlotIndex, providerId);
} catch (Throwable ignored) {
}
});
}
@NotNull
@Override
public Map<String, Consumer<Paras>> getActionMap() {
return this.eap$actions;
// 构造方法注入显式签名 ExtendedAE 源码保持一致
@Inject(method = "<init>(ILnet/minecraft/world/entity/player/Inventory;Lcom/glodblock/github/extendedae/xmod/wt/HostWirelessExPAT;)V", at = @At("TAIL"), require = 0, remap = false)
private void init$eap(int id, net.minecraft.world.entity.player.Inventory playerInventory, HostWirelessExPAT host, CallbackInfo ci) {
// no-op
}
}

View File

@ -12,9 +12,18 @@
"ae2.helpers.PatternProviderLogicAdvancedMixin",
"ae2.helpers.PatternProviderLogicDoublingMixin",
"ae2.AEProcessingPatternMixin",
"extendedae.client.gui.GuiExPatternProviderMixin",
"ae2.autopattern.CraftingTreeNodeAccessor",
"ae2.autopattern.CraftingTreeProcessMixin"
"ae2.autopattern.CraftingTreeProcessMixin",
"extendedae.common.PartExPatternProviderMixin",
"extendedae.common.TileExPatternProviderMixin",
"extendedae.container.ContainerExPatternProviderMixin",
"extendedae.container.ContainerExPatternTerminalMixin",
"extendedae.container.ContainerWirelessExPatternTerminalMixin"
],
"client": [
"extendedae.accessor.GuiExPatternTerminalAccessor"
],
"injectors": {
"defaultRequire": 1