重写了recipeViewers的适配,现在可以适配jei了( •̀ ω •́ )✧

This commit is contained in:
XianYuFish001 2025-09-27 11:23:50 +08:00
parent 5da1290fc7
commit e80ed5a55c
28 changed files with 533 additions and 97 deletions

View File

@ -145,7 +145,7 @@ dependencies {
compileOnly "curse.maven:jade-324717:5427817"
compileOnly "me.shedaniel:RoughlyEnoughItems-neoforge:16.0.729"
compileOnly "curse.maven:applied-mekanistics-574300:5978711"
compileOnly "mekanism:Mekanism:1.21.1-10.7.15.80:api"
compileOnly "mekanism:Mekanism:1.21.1-10.7.15.81:api"
compileOnly "dev.latvian.mods:kubejs-neoforge:2101.7.1-build.188"
compileOnly "dev.latvian.mods:rhino:2101.2.5-build.54"
compileOnly "curse.maven:framedblocks-441647:6127891"

View File

@ -15,7 +15,7 @@ loader_version_range=[1,)
mod_id=extendedae_plus
mod_name=ExtendedAE-Plus
mod_license=LGPL-3.0-or-later
mod_version=1.0.2
mod_version=1.0.2jei
mod_group_id=com.extendedae_plus
mod_authors=GaLicn, XianYuFish001
mod_description=

View File

@ -3,16 +3,14 @@ package com.extendedae_plus.client;
import appeng.api.stacks.GenericStack;
import appeng.client.gui.me.common.MEStorageScreen;
import appeng.core.AEConfig;
import appeng.integration.modules.emi.EmiStackHelper;
import com.extendedae_plus.ExtendedAEPlus;
import com.extendedae_plus.integration.RecipeViewer.RecipeViewerHelper;
import com.extendedae_plus.mixin.ae2.accessor.MEStorageScreenAccessor;
import com.extendedae_plus.mixin.extendedae.accessor.GuiExPatternTerminalAccessor;
import com.extendedae_plus.network.C2SPacketTargetKeyTriggered;
import com.extendedae_plus.network.OpenCraftFromJeiC2SPacket;
import com.extendedae_plus.network.PullFromJeiOrCraftC2SPacket;
import com.glodblock.github.extendedae.client.gui.GuiExPatternTerminal;
import dev.emi.emi.api.EmiApi;
import dev.emi.emi.api.stack.EmiStack;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.screens.Screen;
import net.neoforged.api.distmarker.Dist;
@ -34,14 +32,10 @@ public final class InputEvents {
if (event.getAction() != GLFW.GLFW_PRESS) return;
if (Minecraft.getInstance().screen == null) return;
// 优先处理Shift + 左键拉取或下单
if (event.getButton() == GLFW.GLFW_MOUSE_BUTTON_LEFT && Screen.hasShiftDown()) {
// 🤓哎wc这Jemi兼容层怎么这么好用啊(
// 😡我们要完全去jei化, 不准用
// 😭
// Optional<ITypedIngredient<?>> hovered = new JemiBookmarkOverlay().getIngredientUnderMouse();
List<EmiStack> stacks = EmiApi.getHoveredStack(false).getStack().getEmiStacks();
if (stacks.isEmpty()) return;
GenericStack stack = EmiStackHelper.toGenericStack(stacks.getFirst());
if (event.getButton() == GLFW.GLFW_MOUSE_BUTTON_LEFT && Screen.hasShiftDown() &&
!RecipeViewerHelper.isCheatMode()) {
List<GenericStack> stacks = RecipeViewerHelper.getHoveredStacks();
GenericStack stack = stacks.isEmpty() ? null : stacks.getFirst();
if (stack == null) return;
PacketDistributor.sendToServer(new PullFromJeiOrCraftC2SPacket(stack));
}
@ -49,10 +43,8 @@ public final class InputEvents {
// 中键打开 AE 下单界面保持原有功能
if (event.getButton() == GLFW.GLFW_MOUSE_BUTTON_MIDDLE) {
// 优先在 JEI 配方界面基于坐标获取若无再从覆盖层/书签获取
List<EmiStack> stacks = EmiApi.getHoveredStack(false).getStack().getEmiStacks();
if (stacks.isEmpty()) return;
GenericStack stack = EmiStackHelper.toGenericStack(stacks.getFirst());
List<GenericStack> stacks = RecipeViewerHelper.getHoveredStacks();
GenericStack stack = stacks.isEmpty() ? null : stacks.getFirst();
if (stack == null) return;
// 发送到服务端让其验证并打开 CraftAmountMenu
@ -68,17 +60,14 @@ public final class InputEvents {
if (event.getKeyCode() == GLFW.GLFW_KEY_F) {
// 仅当鼠标确实悬停在 JEI 配料上时触发
// 大概会在一格有多个(?)stack的时候出bug, 但是真的会有那种时候吗?
List<EmiStack> stack = EmiApi.getHoveredStack(false).getStack().getEmiStacks();
if (stack.isEmpty()) return;
String name = stack.getFirst().getName().getString();
if (name.isEmpty()) return;
String name = RecipeViewerHelper.getHoveredStacks().getFirst().what().getDisplayName().getString();
// 写入 AE2 终端的搜索框
var screen = Minecraft.getInstance().screen;
if (screen instanceof MEStorageScreen<?> me) {
try {
// 如果用EMI搜索框
if (AEConfig.instance().isUseExternalSearch()) EmiApi.setSearchText(name);
if (AEConfig.instance().isUseExternalSearch()) RecipeViewerHelper.setSearchText(name);
else {
MEStorageScreenAccessor acc = (MEStorageScreenAccessor) me;
acc.eap$getSearchField().setValue(name);
@ -89,7 +78,7 @@ public final class InputEvents {
}
} else if (screen instanceof GuiExPatternTerminal<?> gpt) {
try {
if (AEConfig.instance().isUseExternalSearch()) EmiApi.setSearchText(name);
if (AEConfig.instance().isUseExternalSearch()) RecipeViewerHelper.setSearchText(name);
else {
GuiExPatternTerminalAccessor acc = (GuiExPatternTerminalAccessor) gpt;
acc.getSearchField().setValue(name);

View File

@ -1,36 +0,0 @@
package com.extendedae_plus.client.gui;
public final class PageLayoutContext {
private static final ThreadLocal<Boolean> ACTIVE = ThreadLocal.withInitial(() -> false);
private static final ThreadLocal<Integer> CURRENT_PAGE = ThreadLocal.withInitial(() -> 0);
private PageLayoutContext() {}
public static void enable(int page) {
ACTIVE.set(true);
CURRENT_PAGE.set(page);
}
public static void disable() {
ACTIVE.set(false);
}
public static boolean isActive() {
Boolean b = ACTIVE.get();
return b != null && b;
}
public static int getCurrentPage() {
Integer i = CURRENT_PAGE.get();
return i != null ? i : 0;
}
public static void withPage(int page, Runnable action) {
enable(page);
try {
action.run();
} finally {
disable();
}
}
}

View File

@ -1,4 +1,4 @@
package com.extendedae_plus.client.ui;
package com.extendedae_plus.client.screen;
import com.extendedae_plus.network.UploadEncodedPatternToProviderC2SPacket;
import com.extendedae_plus.util.ExtendedAEPatternUploadUtil;

View File

@ -0,0 +1,31 @@
package com.extendedae_plus.integration.RecipeViewer;
import appeng.api.stacks.GenericStack;
import java.util.List;
public class EmptyHelper implements IRecipeViewerHelper{
@Override
public List<GenericStack> getHoveredStacks(double mouseX, double mouseY) {
return List.of();
}
@Override
public List<GenericStack> getHoveredStacks() {
return List.of();
}
@Override
public List<GenericStack> getFavorites() {
return List.of();
}
@Override
public void addFavorite(GenericStack stack) {
}
@Override
public void setSearch(String text) {
}
}

View File

@ -0,0 +1,21 @@
package com.extendedae_plus.integration.RecipeViewer;
import appeng.api.stacks.GenericStack;
import java.util.List;
public interface IRecipeViewerHelper {
List<GenericStack> getHoveredStacks(double mouseX, double mouseY);
List<GenericStack> getHoveredStacks();
List<GenericStack> getFavorites();
default boolean isCheatMode() {
return false;
}
void addFavorite(GenericStack stack);
void setSearch(String text);
}

View File

@ -0,0 +1,44 @@
package com.extendedae_plus.integration.RecipeViewer;
import appeng.api.stacks.GenericStack;
import com.extendedae_plus.integration.RecipeViewer.emi.EMIHelper;
import com.extendedae_plus.integration.RecipeViewer.jei.JeiHelper;
import net.neoforged.fml.ModList;
import java.util.List;
import java.util.Optional;
public class RecipeViewerHelper {
private static IRecipeViewerHelper activeViewer;
public static void init() {
if (ModList.get().isLoaded("emi")) activeViewer = new EMIHelper();
else if (ModList.get().isLoaded("jei")) activeViewer = new JeiHelper();
else activeViewer = new EmptyHelper();
}
public static Optional<IRecipeViewerHelper> getViewer() {
if (activeViewer == null) init();
return Optional.ofNullable(activeViewer);
}
public static List<GenericStack> getHoveredStacks() {
return getViewer().map(IRecipeViewerHelper::getHoveredStacks).orElse(List.of());
}
public static List<GenericStack> getFavorites() {
return getViewer().map(IRecipeViewerHelper::getFavorites).orElse(List.of());
}
public static boolean isCheatMode() {
return getViewer().map(IRecipeViewerHelper::isCheatMode).orElse(false);
}
public static void addFavorite(GenericStack stack) {
getViewer().ifPresent(viewer -> viewer.addFavorite(stack));
}
public static void setSearchText(String text) {
getViewer().ifPresent(viewer -> viewer.setSearch(text));
}
}

View File

@ -0,0 +1,90 @@
package com.extendedae_plus.integration.RecipeViewer.emi;
import appeng.api.stacks.AEFluidKey;
import appeng.api.stacks.AEItemKey;
import appeng.api.stacks.AEKey;
import appeng.api.stacks.GenericStack;
import appeng.integration.modules.emi.EmiStackHelper;
import com.extendedae_plus.ExtendedAEPlus;
import com.extendedae_plus.integration.RecipeViewer.IRecipeViewerHelper;
import dev.emi.emi.api.EmiApi;
import dev.emi.emi.api.stack.EmiStack;
import dev.emi.emi.runtime.EmiFavorite;
import dev.emi.emi.runtime.EmiFavorites;
import net.neoforged.fml.ModList;
import java.lang.reflect.Method;
import java.util.List;
public class EMIHelper implements IRecipeViewerHelper {
private final Object mekHelper = getMekHelper();
private final boolean mekChecked =
ModList.get().isLoaded("mekanism") ||
ModList.get().isLoaded("appmek");
@Override
public List<GenericStack> getHoveredStacks(double mouseX, double mouseY) {
return EmiApi.getHoveredStack((int) mouseX, (int) mouseY, false).getStack().getEmiStacks()
.stream().map(EmiStackHelper::toGenericStack).toList();
}
@Override
public List<GenericStack> getHoveredStacks() {
return EmiApi.getHoveredStack(false).getStack().getEmiStacks()
.stream().map(EmiStackHelper::toGenericStack).toList();
}
@Override
public List<GenericStack> getFavorites() {
return EmiFavorites.favorites.stream()
.map(EmiFavorite::getEmiStacks)
.map(List::getFirst)
.map(EmiStackHelper::toGenericStack)
.toList();
}
@Override
public void addFavorite(GenericStack stack) {
AEKey key = stack.what();
if (key instanceof AEItemKey itemKey)
EmiFavorites.addFavorite(EmiStack.of(itemKey.toStack((int) stack.amount())));
else if (key instanceof AEFluidKey fluidKey)
EmiFavorites.addFavorite(EmiStack.of(fluidKey.getFluid(), stack.amount()));
else if (mekChecked && mekHelper != null) {
try {
Class<?> keyClass = key.getClass();
if (keyClass.getName().contains("MekanismKey")) {
Method getChemicalStackMethod = keyClass.getMethod("withAmount", long.class);
Object chemicalStack = getChemicalStackMethod.invoke(key, stack.amount());
Method createEmiStack = mekHelper.getClass().getMethod("createEmiStack",
Class.forName("mekanism.api.chemical.ChemicalStack"));
EmiStack mekEmiStack = (EmiStack) createEmiStack.invoke(mekHelper, chemicalStack);
if (mekEmiStack == null) return;
EmiFavorites.addFavorite(mekEmiStack);
}
} catch (Exception ignored) {}
}
}
@Override
public void setSearch(String text) {
EmiApi.setSearchText(text);
}
private Object getMekHelper() {
try {
Class<?> mekanismAPIClass = Class.forName("mekanism.api.MekanismAPI");
Method getServiceMethod = mekanismAPIClass.getMethod("getService", Class.class);
Class<?> mekanismAccessClass = Class.forName("mekanism.api.IMekanismAccess");
Object mekanismAccess = getServiceMethod.invoke(null, mekanismAccessClass);
Method emiHelperMethod = mekanismAccessClass.getMethod("emiHelper");
return emiHelperMethod.invoke(mekanismAccess);
} catch (Exception e) {
ExtendedAEPlus.LOGGER.error("Failed to get Mekanism EMI Helper: {}", String.valueOf(e));
return null;
}
}
}

View File

@ -1,4 +1,4 @@
package com.extendedae_plus.integration.emi;
package com.extendedae_plus.integration.RecipeViewer.emi;
import appeng.api.stacks.GenericStack;
import appeng.integration.modules.emi.EmiStackHelper;

View File

@ -1,4 +1,4 @@
package com.extendedae_plus.integration.emi;
package com.extendedae_plus.integration.RecipeViewer.emi;
import appeng.api.stacks.AEItemKey;
import appeng.api.stacks.GenericStack;

View File

@ -0,0 +1,64 @@
package com.extendedae_plus.integration.RecipeViewer.jei;
import appeng.api.stacks.AEFluidKey;
import appeng.api.stacks.AEItemKey;
import appeng.api.stacks.AEKey;
import appeng.api.stacks.GenericStack;
import com.extendedae_plus.integration.RecipeViewer.IRecipeViewerHelper;
import mezz.jei.api.ingredients.ITypedIngredient;
import net.neoforged.fml.ModList;
import tamaized.ae2jeiintegration.integration.modules.jei.GenericEntryStackHelper;
import java.lang.reflect.Method;
import java.util.Collections;
import java.util.List;
public class JeiHelper implements IRecipeViewerHelper {
@Override
public List<GenericStack> getHoveredStacks(double mouseX, double mouseY) {
return getHoveredStacks();
}
@Override
public List<GenericStack> getHoveredStacks() {
ITypedIngredient<?> hovered = JeiRuntimeProxy.getIngredientUnderMouse().orElse(null);
if (hovered != null)
return Collections.singletonList(GenericEntryStackHelper.ingredientToStack(hovered));
else return null;
}
@Override
public List<GenericStack> getFavorites() {
return JeiRuntimeProxy.getBookmarkList().stream()
.map(GenericEntryStackHelper::ingredientToStack).toList();
}
@Override
public boolean isCheatMode() {
return JeiRuntimeProxy.isJeiCheatModeEnabled();
}
@Override
public void addFavorite(GenericStack stack) {
AEKey key = stack.what();
if (key instanceof AEItemKey itemKey)
JeiRuntimeProxy.addBookmark(itemKey.toStack());
else if (key instanceof AEFluidKey fluidKey)
JeiRuntimeProxy.addBookmark(fluidKey.toStack(1000));
else if (ModList.get().isLoaded("mekanism") || ModList.get().isLoaded("appmek")) {
try {
Class<?> keyClass = key.getClass();
if (keyClass.getName().contains("MekanismKey")) {
Method getChemicalStackMethod = keyClass.getMethod("getStack");
Object chemicalStack = getChemicalStackMethod.invoke(key);
JeiRuntimeProxy.addBookmark(chemicalStack);
}
} catch (Exception ignored) {}
}
}
@Override
public void setSearch(String text) {
JeiRuntimeProxy.setIngredientFilterText(text);
}
}

View File

@ -0,0 +1,196 @@
package com.extendedae_plus.integration.RecipeViewer.jei;
import com.extendedae_plus.mixin.recipeViewer.jei.accessor.AccessorBookmarkOverlay;
import mezz.jei.api.constants.VanillaTypes;
import mezz.jei.api.ingredients.IIngredientType;
import mezz.jei.api.ingredients.ITypedIngredient;
import mezz.jei.api.neoforge.NeoForgeTypes;
import mezz.jei.api.runtime.IBookmarkOverlay;
import mezz.jei.api.runtime.IIngredientListOverlay;
import mezz.jei.api.runtime.IJeiRuntime;
import mezz.jei.gui.bookmarks.BookmarkList;
import mezz.jei.gui.bookmarks.IngredientBookmark;
import mezz.jei.gui.overlay.elements.IElement;
import net.minecraft.world.item.ItemStack;
import net.neoforged.fml.ModList;
import net.neoforged.neoforge.fluids.FluidStack;
import javax.annotation.Nullable;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
/**
* 线程安全地缓存并访问 JEI Runtime
*/
public final class JeiRuntimeProxy {
private static volatile IJeiRuntime RUNTIME;
private JeiRuntimeProxy() {}
static void setRuntime(IJeiRuntime runtime) {
RUNTIME = runtime;
}
@Nullable
public static IJeiRuntime get() {
return RUNTIME;
}
public static Optional<ITypedIngredient<?>> getIngredientUnderMouse() {
IJeiRuntime rt = RUNTIME;
if (rt == null) return Optional.empty();
IIngredientListOverlay list = rt.getIngredientListOverlay();
if (list != null) {
var ing = list.getIngredientUnderMouse();
if (ing.isPresent()) return ing;
}
IBookmarkOverlay bm = rt.getBookmarkOverlay();
if (bm != null) {
var ing = bm.getIngredientUnderMouse();
if (ing.isPresent()) return ing;
}
return Optional.empty();
}
/**
* 检测 JEI 是否开启了作弊模式给物品
* 使用 JEI 内部开关 JEI 未初始化或异常则返回 false
*/
public static boolean isJeiCheatModeEnabled() {
try {
// 使用完全限定名以避免在源码缺失时的编译依赖问题
return mezz.jei.common.Internal.getClientToggleState().isCheatItemsEnabled();
} catch (Throwable t) {
return false;
}
}
/**
* 将文本写入 JEI 的搜索过滤框
* JEI runtime 不可用则静默返回
*/
public static void setIngredientFilterText(String text) {
IJeiRuntime rt = RUNTIME;
if (rt == null) return;
try {
rt.getIngredientFilter().setFilterText(text == null ? "" : text);
} catch (Throwable ignored) {
// 兼容不同 JEI 版本或在启动阶段尚未就绪
}
}
/**
* 获取JEI书签列表
*/
public static List<? extends ITypedIngredient<?>> getBookmarkList() {
IJeiRuntime rt = RUNTIME;
if (rt == null) return Collections.emptyList();
IBookmarkOverlay bookmarkOverlay = rt.getBookmarkOverlay();
if (bookmarkOverlay instanceof AccessorBookmarkOverlay accessor) {
BookmarkList bookmarkList = accessor.eap$getBookmarkList();
return bookmarkList.getElements().stream().map(IElement::getTypedIngredient).toList();
}
return Collections.emptyList();
}
/**
* 将物品添加到 JEI 书签
*/
public static void addBookmark(ItemStack stack) {
IJeiRuntime rt = RUNTIME;
if (rt == null || stack == null || stack.isEmpty()) return;
IBookmarkOverlay overlay = rt.getBookmarkOverlay();
if (overlay instanceof AccessorBookmarkOverlay accessor) {
BookmarkList list = accessor.eap$getBookmarkList();
try {
var typedOpt = rt.getIngredientManager().createTypedIngredient(VanillaTypes.ITEM_STACK, stack);
typedOpt.ifPresent(typed -> {
IngredientBookmark<ItemStack> bookmark = IngredientBookmark.create(typed, rt.getIngredientManager());
list.add(bookmark); // add 内部会自动保存到配置
});
} catch (Throwable ignored) {}
}
}
public static void addBookmark(FluidStack fluidStack) {
IJeiRuntime rt = RUNTIME;
if (rt == null) return;
IBookmarkOverlay overlay = rt.getBookmarkOverlay();
if (overlay instanceof AccessorBookmarkOverlay accessor) {
BookmarkList list = accessor.eap$getBookmarkList();
Optional<ITypedIngredient<FluidStack>> typedOpt = rt.getIngredientManager()
.createTypedIngredient(NeoForgeTypes.FLUID_STACK, fluidStack);
typedOpt.ifPresent(typed -> {
IngredientBookmark<FluidStack> bookmark = IngredientBookmark.create(typed, rt.getIngredientManager());
list.add(bookmark); // add 内部会自动保存到配置
});
}
}
/**
* 如果存在 Mekanism/appmek则将 Mekanism 化学堆栈添加到 JEI 书签
*/
public static void addBookmark(Object chemicalStack) {
if (!ModList.get().isLoaded("mekanism") && !ModList.get().isLoaded("appmek")) return;
IJeiRuntime rt = RUNTIME;
if (rt == null) return;
IBookmarkOverlay overlay = rt.getBookmarkOverlay();
if (overlay instanceof AccessorBookmarkOverlay accessor) {
BookmarkList list = accessor.eap$getBookmarkList();
try {
if (chemicalStack == null) return;
// Determine Mekanism JEI ingredient type constant by runtime class name
String clsName = chemicalStack.getClass().getName();
String mekanismJeiClass = "mekanism.client.recipe_viewer.jei.MekanismJEI";
Class<?> jeiCls = Class.forName(mekanismJeiClass);
Field typeField = null;
if ("mekanism.api.chemical.ChemicalStack".equals(clsName)) {
typeField = jeiCls.getField("TYPE_CHEMICAL");
}
if (typeField == null) return;
Object typeConst = typeField.get(null);
// Use ingredient manager reflectively to create a typed ingredient
Object ingredientManager = rt.getIngredientManager();
Method createTypedIngredient = ingredientManager.getClass().getMethod("createTypedIngredient", IIngredientType.class, Object.class);
Object opt = createTypedIngredient.invoke(ingredientManager, typeConst, chemicalStack);
if (!(opt instanceof Optional<?> typedOpt)) return;
if (typedOpt.isPresent()) {
Object typed = typedOpt.get();
// Find a compatible static create(...) method on IngredientBookmark where
// the second parameter is assignable from the actual ingredientManager instance.
Method createMethod = null;
for (Method m : IngredientBookmark.class.getMethods()) {
if (!m.getName().equals("create")) continue;
Class<?>[] params = m.getParameterTypes();
if (params.length != 2) continue;
// first param should accept the typed ingredient
boolean firstOk = params[0].isAssignableFrom(typed.getClass()) || params[0].isAssignableFrom(ITypedIngredient.class);
boolean secondOk = params[1].isAssignableFrom(ingredientManager.getClass());
if (firstOk && secondOk) {
createMethod = m;
break;
}
}
if (createMethod != null) {
Object bookmark = createMethod.invoke(null, typed, ingredientManager);
if (bookmark != null) {
list.add((IngredientBookmark) bookmark);
}
}
}
} catch (Throwable e) {
e.printStackTrace();
}
}
}
}

View File

@ -0,0 +1,23 @@
package com.extendedae_plus.integration.RecipeViewer.jei;
import com.extendedae_plus.ExtendedAEPlus;
import mezz.jei.api.IModPlugin;
import mezz.jei.api.JeiPlugin;
import mezz.jei.api.runtime.IJeiRuntime;
import net.minecraft.resources.ResourceLocation;
@JeiPlugin
public class ModJeiPlugin implements IModPlugin {
private static final ResourceLocation UID =
ResourceLocation.fromNamespaceAndPath(ExtendedAEPlus.MODID, "jei_plugin");
@Override
public ResourceLocation getPluginUid() {
return UID;
}
@Override
public void onRuntimeAvailable(IJeiRuntime jeiRuntime) {
JeiRuntimeProxy.setRuntime(jeiRuntime);
}
}

View File

@ -1,4 +1,4 @@
package com.extendedae_plus.mixin.hooks;
package com.extendedae_plus.mixin;
import com.extendedae_plus.hooks.BuiltInModelHooks;
import net.minecraft.client.resources.model.ModelBakery;

View File

@ -1,12 +1,11 @@
package com.extendedae_plus.mixin.ae2.menu;
import appeng.api.stacks.GenericStack;
import appeng.menu.me.crafting.CraftConfirmMenu;
import appeng.menu.me.crafting.CraftingPlanSummary;
import appeng.menu.me.crafting.CraftingPlanSummaryEntry;
import dev.emi.emi.api.stack.EmiIngredient;
import dev.emi.emi.runtime.EmiFavorites;
import com.extendedae_plus.integration.RecipeViewer.RecipeViewerHelper;
import net.minecraft.client.gui.screens.Screen;
import net.minecraft.world.item.crafting.Ingredient;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
@ -45,12 +44,9 @@ public class CraftConfirmMenuGoBackMixin {
for (CraftingPlanSummaryEntry entry : entries) {
if (entry.getMissingAmount() > 0) {
try {
var display = entry.getWhat().wrapForDisplayOrFilter();
if (display != null && !display.isEmpty()) {
// wrapForDisplayOrFilter 返回 ItemStack
// JeiRuntimeProxy.addBookmark(display);
EmiFavorites.addFavorite(EmiIngredient.of(Ingredient.of(display)));
}
var display = entry.getWhat();
if (display != null)
RecipeViewerHelper.addFavorite(new GenericStack(display, entry.getMissingAmount()));
} catch (Throwable ignored) {}
}
}

View File

@ -1,14 +1,10 @@
package com.extendedae_plus.mixin.emi;
package com.extendedae_plus.mixin.recipeViewer;
import appeng.api.stacks.AEFluidKey;
import appeng.api.stacks.AEItemKey;
import appeng.api.stacks.AEKey;
import appeng.integration.modules.itemlists.EncodingHelper;
import appeng.menu.me.common.GridInventoryEntry;
import appeng.menu.me.common.MEStorageMenu;
import dev.emi.emi.runtime.EmiFavorites;
import net.minecraft.world.item.Item;
import net.minecraft.world.level.material.Fluid;
import com.extendedae_plus.integration.RecipeViewer.RecipeViewerHelper;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
@ -25,12 +21,9 @@ public class MixinEncodingHelper {
private static void epp$addJeiIngredientPriorities(MEStorageMenu menu, Comparator<GridInventoryEntry> comparator, CallbackInfoReturnable<Map<AEKey, Integer>> cir){
Map<AEKey, Integer> result = cir.getReturnValue();
AtomicInteger index = new AtomicInteger(Integer.MAX_VALUE);
EmiFavorites.favorites.forEach(favorite -> {
Object stack = favorite.getEmiStacks().getFirst().getKey();
if (stack instanceof Item item) result.put(AEItemKey.of(item), index.getAndDecrement());
else if (stack instanceof Fluid fluid) result.put(AEFluidKey.of(fluid), index.getAndDecrement());
RecipeViewerHelper.getFavorites().forEach(favorite -> {
if (favorite != null) result.put(favorite.what(), index.getAndDecrement());
});
cir.setReturnValue(result);
}
}

View File

@ -1,9 +1,9 @@
package com.extendedae_plus.mixin.emi;
package com.extendedae_plus.mixin.recipeViewer.emi;
import appeng.integration.modules.emi.EmiStackHelper;
import appeng.menu.me.items.PatternEncodingTermMenu;
import com.extendedae_plus.integration.emi.HandlerBoMRecipes;
import com.extendedae_plus.integration.emi.PatternFillingHelper;
import com.extendedae_plus.integration.RecipeViewer.emi.HandlerBoMRecipes;
import com.extendedae_plus.integration.RecipeViewer.emi.PatternFillingHelper;
import com.extendedae_plus.network.RequestProvidersListC2SPacket;
import com.extendedae_plus.util.ExtendedAEPatternUploadUtil;
import dev.emi.emi.api.stack.EmiStack;

View File

@ -1,4 +1,4 @@
package com.extendedae_plus.mixin.emi;
package com.extendedae_plus.mixin.recipeViewer.emi;
import appeng.integration.modules.emi.EmiEncodePatternHandler;
import appeng.integration.modules.itemlists.EncodingHelper;

View File

@ -1,4 +1,4 @@
package com.extendedae_plus.mixin.emi;
package com.extendedae_plus.mixin.recipeViewer.emi;
import appeng.menu.me.items.PatternEncodingTermMenu;
import dev.emi.emi.api.widget.RecipeFillButtonWidget;

View File

@ -0,0 +1,12 @@
package com.extendedae_plus.mixin.recipeViewer.jei.accessor;
import mezz.jei.gui.bookmarks.BookmarkList;
import mezz.jei.gui.overlay.bookmarks.BookmarkOverlay;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;
@Mixin(BookmarkOverlay.class)
public interface AccessorBookmarkOverlay {
@Accessor(value = "bookmarkList", remap = false)
BookmarkList eap$getBookmarkList();
}

View File

@ -1,7 +1,7 @@
package com.extendedae_plus.network;
import com.extendedae_plus.ExtendedAEPlus;
import com.extendedae_plus.client.ui.ProviderSelectScreen;
import com.extendedae_plus.client.screen.ProviderSelectScreen;
import com.mojang.serialization.Codec;
import net.minecraft.client.Minecraft;
import net.minecraft.network.RegistryFriendlyByteBuf;

View File

@ -963,7 +963,7 @@ public class ExtendedAEPatternUploadUtil {
return false;
}
// 读取已编码槽位的物品通过 accessor
var encodedSlot = ((PatternEncodingTermMenuAccessor) (Object) menu)
var encodedSlot = ((PatternEncodingTermMenuAccessor) menu)
.eap$getEncodedPatternSlot();
ItemStack stack = encodedSlot.getItem();
if (stack.isEmpty() || !PatternDetailsHelper.isEncodedPattern(stack)) {

View File

@ -28,10 +28,14 @@
"item.extendedae_plus.infinity_biginteger_cell": "§4De§cvou§6rer §eof §aCo§bsmic §dSilence",
"tooltip.extendedae_plus.infinity_biginteger_cell.summon1": "§6Through ninefold sacrifice, the Void echoes§r—§8Iava, Lord of the Void§r, bestows upon thee this artifact",
"tooltip.extendedae_plus.infinity_biginteger_cell.summon2": "§b—§4A §dUni§cverse §eWith§ain §6A §bSin§5gle §9Point",
"tooltip.extendedae_plus.encode_button_description": "\n§a[Ctrl] §7Upload patterns",
"screen.extendedae_plus.title": "ExtendedAE Plus Config",
"config.extendedae_plus": "ExtendedAE Plus",
"config.extendedae_plus.override_ae2wt_picking": "Override AE2WT Picking",
"config.extendedae_plus.independence_uploading_button": "Independence Upload Button",
"extendedae_plus.configuration.general": "General",
"extendedae_plus.configuration.pageMultiplier": "Pattern Provider Page Multiplier",

View File

@ -31,7 +31,7 @@
"item.extendedae_plus.infinity_biginteger_cell": "§4吞§c噬§6万§e籁§a的§b寂§d静",
"tooltip.extendedae_plus.infinity_biginteger_cell.summon1": "§6九重献祭, 终得虚空回响§r——觐见§8虚空之主Iava§r, 赐汝此物",
"tooltip.extendedae_plus.infinity_biginteger_cell.summon2": "§b——§4方§d寸§c之§e间§a, §6自§b有§5千§9寰",
"tooltip.extendedae_plus.encode_button_description": "\n§a[Shift] §7上传样板",
"tooltip.extendedae_plus.encode_button_description": "\n§a[Ctrl] §7上传样板",
"config.jade.plugin_extendedae_plus.wireless_transceiver_info": "无线收发器信息",
"config.jade.plugin_extendedae_plus.wt_frequency": "显示频率",
@ -46,6 +46,7 @@
"screen.extendedae_plus.title": "ExtendedAE Plus 配置",
"config.extendedae_plus": "ExtendedAE Plus",
"config.extendedae_plus.override_ae2wt_picking": "覆盖AE2WT选取",
"config.extendedae_plus.independence_uploading_button": "独立上传按钮",
"extendedae_plus.configuration.general": "通用",
"extendedae_plus.configuration.pageMultiplier": "扩展样板供应器槽位倍率",

View File

@ -26,15 +26,17 @@
"ae2.menu.PatternEncodingTermMenuMixin",
"ae2.menu.PatternProviderMenuAdvancedMixin",
"ae2.menu.PatternProviderMenuDoublingMixin",
"emi.MixinEncodePatternTransfer",
"emi.MixinRecipeFillButton",
"extendedae.common.PartExPatternProviderMixin",
"extendedae.common.TileExPatternProviderMixin",
"extendedae.container.ContainerExPatternProviderMixin",
"extendedae.container.ContainerExPatternTerminalMixin",
"extendedae.container.ContainerWirelessExPatternTerminalMixin"
"extendedae.container.ContainerWirelessExPatternTerminalMixin",
"recipeViewer.emi.MixinEncodePatternTransfer",
"recipeViewer.emi.MixinRecipeFillButton",
"recipeViewer.jei.accessor.AccessorBookmarkOverlay"
],
"client": [
"ModelBakeryMixin",
"PickFromWirelessMixin",
"accessor.AbstractContainerScreenAccessor",
"accessor.ScreenAccessor",
@ -52,14 +54,13 @@
"ae2.client.gui.PatternProviderScreenMixin",
"ae2.client.gui.SlotGridLayoutMixin",
"ae2.menu.CraftConfirmMenuGoBackMixin",
"emi.MixinBoMScreen",
"emi.MixinEncodingHelper",
"extendedae.accessor.GuiExPatternTerminalAccessor",
"extendedae.accessor.GuiExPatternTerminalSlotsRowAccessor",
"extendedae.client.HighlightButtonMixin",
"extendedae.client.gui.GuiExPatternProviderMixin",
"extendedae.client.gui.GuiExPatternTerminalMixin",
"hooks.ModelBakeryMixin"
"recipeViewer.MixinEncodingHelper",
"recipeViewer.emi.MixinBoMScreen"
],
"injectors": {
"defaultRequire": 1

View File

@ -95,6 +95,13 @@ config="${mod_id}.mixins.json"
ordering="AFTER"
side="BOTH"
[[dependencies.${mod_id}]]
modId="expandedae"
type="discouraged"
versionRange="*"
ordering="AFTER"
side="BOTH"
# Features are specific properties of the game environment, that you may want to declare you require. This example declares
# that your mod requires GL version 3.2 or higher. Other features will be added. They are side aware so declaring this won't
# stop your mod loading on the server for example.