From b5ac0b6fbe92925e2acbdccc2128dbbd77745404 Mon Sep 17 00:00:00 2001 From: GaLi <3096147684@qq.com> Date: Sun, 1 Mar 2026 16:20:06 +0800 Subject: [PATCH] =?UTF-8?q?=E9=85=8D=E6=96=B9=E4=B9=A6=E7=AD=BE=E5=8A=9F?= =?UTF-8?q?=E8=83=BD=E7=A7=BB=E6=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../client/event/CtrlQPatternKeyHandler.java | 63 ++++++++++++++++++- .../integration/jei/JeiRuntimeProxy.java | 60 ++++++++++++++++++ 2 files changed, 122 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/extendedae_plus/client/event/CtrlQPatternKeyHandler.java b/src/main/java/com/extendedae_plus/client/event/CtrlQPatternKeyHandler.java index da44c85..1ca9eee 100644 --- a/src/main/java/com/extendedae_plus/client/event/CtrlQPatternKeyHandler.java +++ b/src/main/java/com/extendedae_plus/client/event/CtrlQPatternKeyHandler.java @@ -123,6 +123,10 @@ public final class CtrlQPatternKeyHandler { try { ResourceLocation recipeId = getRecipeId(recipeBookmark); if (recipeId == null) { + Minecraft mc = Minecraft.getInstance(); + if (mc.player != null) { + mc.player.displayClientMessage(Component.translatable("message.extendedae_plus.recipe_not_found"), true); + } return; } @@ -164,6 +168,10 @@ public final class CtrlQPatternKeyHandler { try { ResourceLocation recipeId = getRecipeId(recipeBookmark); if (recipeId == null) { + Minecraft mc = Minecraft.getInstance(); + if (mc.player != null) { + mc.player.displayClientMessage(Component.translatable("message.extendedae_plus.recipe_not_found"), true); + } return; } @@ -220,6 +228,18 @@ public final class CtrlQPatternKeyHandler { } } + try { + var getDisplayIngredientMethod = recipeBookmark.getClass().getMethod("getDisplayIngredient"); + Object displayIngredient = getDisplayIngredientMethod.invoke(recipeBookmark); + if (displayIngredient instanceof ITypedIngredient typed) { + List infos = RecipeFinderUtil.findRecipesByIngredient(typed); + if (!infos.isEmpty()) { + return infos; + } + } + } catch (Throwable ignored) { + } + try { var getRecipeOutputMethod = recipeBookmark.getClass().getMethod("getRecipeOutput"); Object recipeOutput = getRecipeOutputMethod.invoke(recipeBookmark); @@ -241,6 +261,11 @@ public final class CtrlQPatternKeyHandler { } private static ResourceLocation getRecipeId(Object recipeBookmark) { + if (recipeBookmark == null) { + return null; + } + + // JEI 1.20 兼容分支 try { var getRecipeUidMethod = recipeBookmark.getClass().getMethod("getRecipeUid"); Object recipeId = getRecipeUidMethod.invoke(recipeBookmark); @@ -249,6 +274,43 @@ public final class CtrlQPatternKeyHandler { } } catch (Throwable ignored) { } + + // JEI 1.21+:从 recipeCategory.getRegistryName(recipe) 获取 + try { + Object recipe = recipeBookmark.getClass().getMethod("getRecipe").invoke(recipeBookmark); + Object recipeCategory = recipeBookmark.getClass().getMethod("getRecipeCategory").invoke(recipeBookmark); + if (recipe != null && recipeCategory != null) { + try { + Object recipeId = recipeCategory.getClass().getMethod("getRegistryName", Object.class).invoke(recipeCategory, recipe); + if (recipeId instanceof ResourceLocation rl) { + return rl; + } + } catch (Throwable ignored) { + for (var m : recipeCategory.getClass().getMethods()) { + if (!"getRegistryName".equals(m.getName()) || m.getParameterCount() != 1) { + continue; + } + Object recipeId = m.invoke(recipeCategory, recipe); + if (recipeId instanceof ResourceLocation rl) { + return rl; + } + } + } + } + } catch (Throwable ignored) { + } + + // 反射字段兜底(JEI 内部 RecipeBookmark 存在 recipeUid 字段) + try { + var f = recipeBookmark.getClass().getDeclaredField("recipeUid"); + f.setAccessible(true); + Object recipeId = f.get(recipeBookmark); + if (recipeId instanceof ResourceLocation rl) { + return rl; + } + } catch (Throwable ignored) { + } + return null; } @@ -316,4 +378,3 @@ public final class CtrlQPatternKeyHandler { .toList(); } } - diff --git a/src/main/java/com/extendedae_plus/integration/jei/JeiRuntimeProxy.java b/src/main/java/com/extendedae_plus/integration/jei/JeiRuntimeProxy.java index fb88395..ff55ca3 100644 --- a/src/main/java/com/extendedae_plus/integration/jei/JeiRuntimeProxy.java +++ b/src/main/java/com/extendedae_plus/integration/jei/JeiRuntimeProxy.java @@ -151,6 +151,32 @@ public final class JeiRuntimeProxy { Object overlay = rt.getClass().getMethod("getBookmarkOverlay").invoke(rt); if (overlay == null) return Optional.empty(); + // JEI 1.21 优先路径:直接拿鼠标下 clickable element 的 bookmark + try { + double mouseX = getGuiMouseX(); + double mouseY = getGuiMouseY(); + Object clickableStream = overlay.getClass() + .getMethod("getIngredientUnderMouse", double.class, double.class) + .invoke(overlay, mouseX, mouseY); + if (clickableStream instanceof java.util.stream.Stream stream) { + Object firstClickable = stream.findFirst().orElse(null); + if (firstClickable != null) { + Object element = firstClickable.getClass().getMethod("getElement").invoke(firstClickable); + if (element != null) { + Object bookmarkOpt = element.getClass().getMethod("getBookmark").invoke(element); + if (bookmarkOpt instanceof Optional b && b.isPresent()) { + Object bookmark = b.get(); + if (bookmark != null && "RecipeBookmark".equals(bookmark.getClass().getSimpleName())) { + return Optional.of(bookmark); + } + } + } + } + } + } catch (Throwable ignored) { + } + + // 兼容回退:基于 typed ingredient 匹配 bookmarkList 元素 Object ingredientOpt = overlay.getClass().getMethod("getIngredientUnderMouse").invoke(overlay); if (!(ingredientOpt instanceof Optional opt) || opt.isEmpty()) return Optional.empty(); Object hoveredIngredient = opt.get(); @@ -189,6 +215,40 @@ public final class JeiRuntimeProxy { return Optional.empty(); } + private static double getGuiMouseX() { + try { + Class mcCls = Class.forName("net.minecraft.client.Minecraft"); + Object mc = mcCls.getMethod("getInstance").invoke(null); + Object mouseHandler = mcCls.getField("mouseHandler").get(mc); + Object window = mcCls.getMethod("getWindow").invoke(mc); + + double xpos = (double) mouseHandler.getClass().getMethod("xpos").invoke(mouseHandler); + int guiW = (int) window.getClass().getMethod("getGuiScaledWidth").invoke(window); + int screenW = (int) window.getClass().getMethod("getScreenWidth").invoke(window); + if (screenW <= 0) return xpos; + return xpos * ((double) guiW / (double) screenW); + } catch (Throwable ignored) { + return 0.0D; + } + } + + private static double getGuiMouseY() { + try { + Class mcCls = Class.forName("net.minecraft.client.Minecraft"); + Object mc = mcCls.getMethod("getInstance").invoke(null); + Object mouseHandler = mcCls.getField("mouseHandler").get(mc); + Object window = mcCls.getMethod("getWindow").invoke(mc); + + double ypos = (double) mouseHandler.getClass().getMethod("ypos").invoke(mouseHandler); + int guiH = (int) window.getClass().getMethod("getGuiScaledHeight").invoke(window); + int screenH = (int) window.getClass().getMethod("getScreenHeight").invoke(window); + if (screenH <= 0) return ypos; + return ypos * ((double) guiH / (double) screenH); + } catch (Throwable ignored) { + return 0.0D; + } + } + public static void addBookmark(ItemStack stack) { Object rt = RUNTIME; if (rt == null || stack == null || stack.isEmpty()) return;