jei运行时检查

This commit is contained in:
GaLicn 2025-09-26 23:01:08 +08:00
parent 7bd15e7d13
commit 74309779fd
2 changed files with 33 additions and 125 deletions

View File

@ -33,10 +33,7 @@ public final class InputEvents {
if (!ModList.get().isLoaded("jei")) { if (!ModList.get().isLoaded("jei")) {
return; return;
} }
// JEI 运行时尚未就绪跳过 // 注意不要在 try/catch 之外直接访问 JEI 运行时避免类加载崩溃
if (JeiRuntimeProxy.get() == null) {
return;
}
// 优先处理Shift + 左键拉取或下单 // 优先处理Shift + 左键拉取或下单
if (event.getButton() == GLFW.GLFW_MOUSE_BUTTON_LEFT && Screen.hasShiftDown()) { if (event.getButton() == GLFW.GLFW_MOUSE_BUTTON_LEFT && Screen.hasShiftDown()) {
try { try {
@ -103,9 +100,7 @@ public final class InputEvents {
if (!ModList.get().isLoaded("jei")) { if (!ModList.get().isLoaded("jei")) {
return; return;
} }
if (JeiRuntimeProxy.get() == null) { // 注意不要在 try/catch 之外直接访问 JEI 运行时避免类加载崩溃
return;
}
if (event.getKeyCode() != GLFW.GLFW_KEY_F) return; if (event.getKeyCode() != GLFW.GLFW_KEY_F) return;
// 仅当鼠标确实悬停在 JEI 配料上时触发 // 仅当鼠标确实悬停在 JEI 配料上时触发

View File

@ -1,24 +1,16 @@
package com.extendedae_plus.integration.jei; package com.extendedae_plus.integration.jei;
import com.extendedae_plus.mixin.jei.accessor.BookmarkOverlayAccessor;
import mezz.jei.api.constants.VanillaTypes; import mezz.jei.api.constants.VanillaTypes;
import mezz.jei.api.forge.ForgeTypes;
import mezz.jei.api.ingredients.IIngredientType;
import mezz.jei.api.ingredients.ITypedIngredient; import mezz.jei.api.ingredients.ITypedIngredient;
import mezz.jei.api.runtime.IBookmarkOverlay; import mezz.jei.api.runtime.IBookmarkOverlay;
import mezz.jei.api.runtime.IIngredientListOverlay; import mezz.jei.api.runtime.IIngredientListOverlay;
import mezz.jei.api.runtime.IJeiRuntime; 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.minecraft.world.item.ItemStack;
import net.minecraftforge.fluids.FluidStack; import net.minecraftforge.fluids.FluidStack;
import net.minecraftforge.fml.ModList; import net.minecraftforge.fml.ModList;
import org.spongepowered.asm.mixin.Pseudo; import org.spongepowered.asm.mixin.Pseudo;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
@ -131,48 +123,35 @@ public final class JeiRuntimeProxy {
} }
/** /**
* 获取JEI书签列表 * 获取 JEI 书签列表为避免在未安装 JEI GUI 时崩溃使用反射委托到桥接类
*/ */
public static List<? extends ITypedIngredient<?>> getBookmarkList() { public static List<? extends ITypedIngredient<?>> getBookmarkList() {
IJeiRuntime rt = RUNTIME; try {
if (rt == null) return Collections.emptyList(); Class<?> bridge = Class.forName("com.extendedae_plus.integration.jei.JeiBookmarkBridge");
IBookmarkOverlay bookmarkOverlay = rt.getBookmarkOverlay(); var m = bridge.getMethod("getBookmarkList");
if (bookmarkOverlay instanceof BookmarkOverlayAccessor accessor) { @SuppressWarnings("unchecked")
BookmarkList bookmarkList = accessor.eap$getBookmarkList(); List<? extends ITypedIngredient<?>> list = (List<? extends ITypedIngredient<?>>) m.invoke(null);
return bookmarkList.getElements().stream().map(IElement::getTypedIngredient).toList(); return list == null ? Collections.emptyList() : list;
} catch (Throwable ignored) {
return Collections.emptyList();
} }
return Collections.emptyList();
} }
public static void addBookmark(ItemStack stack) { public static void addBookmark(ItemStack stack) {
IJeiRuntime rt = RUNTIME; try {
if (rt == null) return; Class<?> bridge = Class.forName("com.extendedae_plus.integration.jei.JeiBookmarkBridge");
var m = bridge.getMethod("addBookmark", ItemStack.class);
IBookmarkOverlay overlay = rt.getBookmarkOverlay(); m.invoke(null, stack);
if (overlay instanceof BookmarkOverlayAccessor accessor) { } catch (Throwable ignored) {
BookmarkList list = accessor.eap$getBookmarkList();
Optional<ITypedIngredient<ItemStack>> typedOpt = rt.getIngredientManager()
.createTypedIngredient(VanillaTypes.ITEM_STACK, stack);
typedOpt.ifPresent(typed -> {
IngredientBookmark<ItemStack> bookmark = IngredientBookmark.create(typed, rt.getIngredientManager());
list.add(bookmark); // add 内部会自动保存到配置
});
} }
} }
public static void addBookmark(FluidStack fluidStack) { public static void addBookmark(FluidStack fluidStack) {
IJeiRuntime rt = RUNTIME; try {
if (rt == null) return; Class<?> bridge = Class.forName("com.extendedae_plus.integration.jei.JeiBookmarkBridge");
var m = bridge.getMethod("addBookmark", FluidStack.class);
IBookmarkOverlay overlay = rt.getBookmarkOverlay(); m.invoke(null, fluidStack);
if (overlay instanceof BookmarkOverlayAccessor accessor) { } catch (Throwable ignored) {
BookmarkList list = accessor.eap$getBookmarkList();
Optional<ITypedIngredient<FluidStack>> typedOpt = rt.getIngredientManager()
.createTypedIngredient(ForgeTypes.FLUID_STACK, fluidStack);
typedOpt.ifPresent(typed -> {
IngredientBookmark<FluidStack> bookmark = IngredientBookmark.create(typed, rt.getIngredientManager());
list.add(bookmark); // add 内部会自动保存到配置
});
} }
} }
@ -180,91 +159,25 @@ public final class JeiRuntimeProxy {
* 如果存在 Mekanism/appmek则将 Mekanism 化学堆栈添加到 JEI 书签 * 如果存在 Mekanism/appmek则将 Mekanism 化学堆栈添加到 JEI 书签
*/ */
public static void addBookmark(Object chemicalStack) { public static void addBookmark(Object chemicalStack) {
if (!ModList.get().isLoaded("mekanism") && !ModList.get().isLoaded("appmek")) return; try {
Class<?> bridge = Class.forName("com.extendedae_plus.integration.jei.JeiBookmarkBridge");
IJeiRuntime rt = RUNTIME; var m = bridge.getMethod("addBookmark", Object.class);
if (rt == null) return; m.invoke(null, chemicalStack);
} catch (Throwable ignored) {
IBookmarkOverlay overlay = rt.getBookmarkOverlay();
if (overlay instanceof BookmarkOverlayAccessor 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.jei.MekanismJEI";
Class<?> jeiCls = Class.forName(mekanismJeiClass);
Field typeField = getField(clsName, jeiCls);
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();
}
} }
} }
private static @Nullable Field getField(String clsName, Class<?> jeiCls) throws NoSuchFieldException { // Note: helper methods moved to bridge to avoid referencing JEI GUI at class load time.
Field typeField = null;
if ("mekanism.api.chemical.gas.GasStack".equals(clsName)) {
typeField = jeiCls.getField("TYPE_GAS");
} else if ("mekanism.api.chemical.infuse.InfusionStack".equals(clsName)) {
typeField = jeiCls.getField("TYPE_INFUSION");
} else if ("mekanism.api.chemical.pigment.PigmentStack".equals(clsName)) {
typeField = jeiCls.getField("TYPE_PIGMENT");
} else if ("mekanism.api.chemical.slurry.SlurryStack".equals(clsName)) {
typeField = jeiCls.getField("TYPE_SLURRY");
}
return typeField;
}
/** /**
* JEI 书签移除物品 * JEI 书签移除物品反射委托
*/ */
public static void removeBookmark(ItemStack stack) { public static void removeBookmark(ItemStack stack) {
IJeiRuntime rt = RUNTIME; try {
if (rt == null) return; Class<?> bridge = Class.forName("com.extendedae_plus.integration.jei.JeiBookmarkBridge");
var m = bridge.getMethod("removeBookmark", ItemStack.class);
IBookmarkOverlay overlay = rt.getBookmarkOverlay(); m.invoke(null, stack);
if (overlay instanceof BookmarkOverlayAccessor accessor) { } catch (Throwable ignored) {
BookmarkList list = accessor.eap$getBookmarkList();
Optional<ITypedIngredient<ItemStack>> typedOpt = rt.getIngredientManager()
.createTypedIngredient(VanillaTypes.ITEM_STACK, stack);
typedOpt.ifPresent(typed -> {
IngredientBookmark<ItemStack> bookmark = IngredientBookmark.create(typed, rt.getIngredientManager());
list.remove(bookmark);
});
} }
} }
} }