重写了recipeViewers的适配,现在可以适配jei了( •̀ ω •́ )✧
This commit is contained in:
parent
5da1290fc7
commit
e80ed5a55c
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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=
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
|
@ -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) {
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
|
@ -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;
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
|
@ -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) {}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
|
@ -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;
|
||||
|
|
@ -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;
|
||||
|
|
@ -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();
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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)) {
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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": "扩展样板供应器槽位倍率",
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user