From 70937013e0309125585a850f030d27f673506bd6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9A=D0=B8=D1=80=D0=B8=D0=BB=D0=BB=20=D0=93=D1=80=D0=B8?= =?UTF-8?q?=D0=BD=D0=B5=D0=B2?= Date: Tue, 8 Sep 2026 19:02:16 +0500 Subject: [PATCH 1/4] Migrate to Rinku 3.0.4 and drop a Chromium flag that no longer exists MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes the 26.2 client crash: with Rinku 2.2.1 a GUI open/close loop died on the fourth iteration with "Texture view Sampler0 (MCEF Browser Texture 1x1) has been closed!", and on 3.0.4 the same loop runs 25 times with the browser preload pool left enabled. The API move is mostly mechanical — com.cinemamod.mcef.MCEF becomes de.keksuccino.rinku.Rinku, MCEFBrowser becomes RinkuBrowser, the mixin follows CefUtil to de.keksuccino.rinku.util, and getTextureLocation is now getTextureIdentifier. Three things kept it small: RinkuInitListener is a functional interface so the scheduleForInit lambdas are untouched, RinkuBrowser still extends CefBrowserOsr so setFocus/getURL/ executeJavaScript/loadURL/setZoomLevel come along for free, and CefUtil.init still calls CefApp.startup(String[]) so the GPU-flag ModifyArg survives the move. mcef_version is renamed to rinku_version since MCEF is gone from the project entirely. CefUtilMixin no longer injects --use-gl=desktop. That flag was removed from Chromium years ago; it slipped through while Rinku shipped an older build, but against Chromium 151 it fails CefApp.startup outright, so CEF reached INITIALIZATION_FAILED and no browser was ever created. It was our own mine, not something the migration broke — any Chromium bump would have set it off. Rinku publishes its NeoForge builds inconsistently: 26.2 carries classes at the top level while 1.21.1, 1.21.11 and 26.1.2 are jarJar wrappers whose real mod sits in META-INF/jarjar/. NeoForge unpacks that at runtime, but javac sees an empty jar, so the build now compiles against whatever actually holds the classes and leaves the published artifact on the runtime classpath untouched. All seven targets build. Verified end to end on 1.21.11-fabric and 26.2-neoforge: CEF initializes, the page loads, the bridge is injected, page-to-game channels arrive, and the GUI reopens without crashing. Closes #28 Closes #31 --- build.gradle | 51 +++++++++++++++++-- src/main/java/land/webgui/WebGUIClient.java | 18 +++---- src/main/java/land/webgui/WebGUIKeys.java | 6 +-- src/main/java/land/webgui/WebHudOverlay.java | 24 ++++----- src/main/java/land/webgui/WebSession.java | 20 ++++---- src/main/java/land/webgui/WebViewLayout.java | 6 +-- src/main/java/land/webgui/WebViewScreen.java | 16 +++--- .../java/land/webgui/WebviewClientBridge.java | 12 ++--- .../java/land/webgui/WebviewClientEmit.java | 8 +-- .../land/webgui/WebviewPageLoadHooks.java | 14 ++--- .../webgui/WebviewPageToClientBridge.java | 4 +- .../java/land/webgui/mixin/CefUtilMixin.java | 4 +- .../java/land/webgui/mixin/MouseMixin.java | 8 +-- versions/1.20.1-fabric/gradle.properties | 2 +- versions/1.20.1-neoforge/gradle.properties | 2 +- versions/1.21.1-fabric/gradle.properties | 2 +- versions/1.21.1-neoforge/gradle.properties | 2 +- versions/1.21.11-fabric/gradle.properties | 2 +- versions/1.21.11-neoforge/gradle.properties | 2 +- versions/26.1.2-neoforge/gradle.properties | 2 +- versions/26.2-neoforge/gradle.properties | 2 +- 21 files changed, 123 insertions(+), 84 deletions(-) diff --git a/build.gradle b/build.gradle index 8b04f63..d18b529 100644 --- a/build.gradle +++ b/build.gradle @@ -60,10 +60,7 @@ if (sc_loader == "fabric") { mappings "net.fabricmc:yarn:${yarn_mappings}:v2" modImplementation "net.fabricmc:fabric-loader:${loader_version}" modImplementation "net.fabricmc.fabric-api:fabric-api:${fabric_version}" - // MCEF [Keksuccino's Fork] was renamed to Rinku; the old `mcef-keksuccino` - // slug no longer resolves on Modrinth's maven. The 2.2.x artifacts are - // still served under the new slug, so only the artifact id changes here. - include(modImplementation("maven.modrinth:rinku:${mcef_version}")) + include(modImplementation("maven.modrinth:rinku:${rinku_version}")) } } else { neoForge { @@ -90,8 +87,52 @@ if (sc_loader == "fabric") { } } } + // Rinku ships its NeoForge builds inconsistently: 26.2 has classes at the + // top level, but 1.21.1 and 26.1.2 are jarJar wrappers whose real mod sits + // in META-INF/jarjar/. NeoForge unpacks that at runtime, so the game is + // happy either way — javac is not, and sees an empty jar. Compile against + // whatever actually holds the classes; the published artifact stays on the + // runtime classpath untouched. + configurations { + rinkuArtifact + } + + dependencies { + rinkuArtifact "maven.modrinth:rinku:${rinku_version}" + runtimeOnly "maven.modrinth:rinku:${rinku_version}" + } + + def rinkuClassesDir = layout.buildDirectory.dir('rinku-compile') + def unwrapRinku = tasks.register('unwrapRinku') { + def artifact = configurations.rinkuArtifact + inputs.files(artifact) + outputs.dir(rinkuClassesDir) + doLast { + def dir = rinkuClassesDir.get().asFile + dir.deleteDir() + dir.mkdirs() + def outer = artifact.singleFile + def target = new File(dir, 'rinku.jar') + new java.util.zip.ZipFile(outer).withCloseable { zip -> + def nested = zip.entries().toList().find { + it.name.startsWith('META-INF/jarjar/') && it.name.endsWith('.jar') + } + if (nested != null) { + target.withOutputStream { out -> out << zip.getInputStream(nested) } + logger.lifecycle("rinku: compiling against nested ${nested.name}") + } else { + target.bytes = outer.bytes + } + } + } + } + dependencies { - implementation "maven.modrinth:rinku:${mcef_version}" + // Point at the jar itself, not the directory holding it — a directory + // on the compile classpath is searched for loose .class files only. + compileOnly files(rinkuClassesDir.map { it.file('rinku.jar') }) { + builtBy unwrapRinku + } } } diff --git a/src/main/java/land/webgui/WebGUIClient.java b/src/main/java/land/webgui/WebGUIClient.java index 8b19203..6b41065 100644 --- a/src/main/java/land/webgui/WebGUIClient.java +++ b/src/main/java/land/webgui/WebGUIClient.java @@ -1,6 +1,6 @@ package land.webgui; -import com.cinemamod.mcef.MCEF; +import de.keksuccino.rinku.Rinku; //? if fabric { import net.fabricmc.api.ClientModInitializer; import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents; @@ -24,12 +24,12 @@ public final class WebGUIClient //? if fabric { @Override public void onInitializeClient() { - MCEF.scheduleForInit(success -> { + Rinku.scheduleForInit(success -> { if (!success) { - WebGUIMod.LOGGER.error("MCEF (Chromium) failed to initialize — web GUI will not work."); + WebGUIMod.LOGGER.error("Rinku (Chromium) failed to initialize — web GUI will not work."); return; } - MCEF.getClient().addDisplayHandler(new WebviewBrowserConsoleLogger()); + Rinku.getClient().addDisplayHandler(new WebviewBrowserConsoleLogger()); WebviewPageToClientBridge.register(); WebviewPageLoadHooks.register(); WebGUIMod.LOGGER.info("WebGUI bridge ready (console log, page↔game, client data)."); @@ -108,7 +108,7 @@ private static void onLeaveWorld() { } private static void handleOpenPayload(net.minecraft.client.MinecraftClient client, int mode, String url) { - if (!MCEF.isInitialized()) { + if (!Rinku.isInitialized()) { if (client.player != null) { client.player.sendMessage(net.minecraft.text.Text.translatable("message.webgui.mcef_not_ready"), false); } @@ -123,12 +123,12 @@ private static void handleOpenPayload(net.minecraft.client.MinecraftClient clien } //? } else { /*public static void initClient(IEventBus modBus) { - MCEF.scheduleForInit(success -> { + Rinku.scheduleForInit(success -> { if (!success) { - WebGUIMod.LOGGER.error("MCEF (Chromium) failed to initialize - web GUI will not work."); + WebGUIMod.LOGGER.error("Rinku (Chromium) failed to initialize - web GUI will not work."); return; } - MCEF.getClient().addDisplayHandler(new WebviewBrowserConsoleLogger()); + Rinku.getClient().addDisplayHandler(new WebviewBrowserConsoleLogger()); WebviewPageToClientBridge.register(); WebviewPageLoadHooks.register(); WebGUIMod.LOGGER.info("WebGUI bridge ready (console log, page<->game, client data)."); @@ -182,7 +182,7 @@ private static void onClientTick(ClientTickEvent.Post event) { } private static void handleOpenPayload(Minecraft client, int mode, String url) { - if (!MCEF.isInitialized()) { + if (!Rinku.isInitialized()) { if (client.player != null) { //? if >=26 { client.player.sendSystemMessage( diff --git a/src/main/java/land/webgui/WebGUIKeys.java b/src/main/java/land/webgui/WebGUIKeys.java index 506ade2..3ca0fa8 100644 --- a/src/main/java/land/webgui/WebGUIKeys.java +++ b/src/main/java/land/webgui/WebGUIKeys.java @@ -1,6 +1,6 @@ package land.webgui; -import com.cinemamod.mcef.MCEF; +import de.keksuccino.rinku.Rinku; //? if fabric { import net.fabricmc.fabric.api.client.keybinding.v1.KeyBindingHelper; import net.minecraft.client.MinecraftClient; @@ -117,7 +117,7 @@ private static void tryOpenMainMenu(MinecraftClient client) { if (client.currentScreen instanceof WebViewScreen) { return; } - if (!MCEF.isInitialized()) { + if (!Rinku.isInitialized()) { if (client.player != null) { client.player.sendMessage(Text.translatable("message.webgui.mcef_not_ready"), false); } @@ -134,7 +134,7 @@ private static void tryOpenMainMenu(MinecraftClient client) { //? } return; } - if (!MCEF.isInitialized()) { + if (!Rinku.isInitialized()) { if (client.player != null) { //? if >=26 { client.player.sendSystemMessage(Component.translatable("message.webgui.mcef_not_ready")); diff --git a/src/main/java/land/webgui/WebHudOverlay.java b/src/main/java/land/webgui/WebHudOverlay.java index 0f7afab..227b4a0 100644 --- a/src/main/java/land/webgui/WebHudOverlay.java +++ b/src/main/java/land/webgui/WebHudOverlay.java @@ -1,7 +1,7 @@ package land.webgui; -import com.cinemamod.mcef.MCEF; -import com.cinemamod.mcef.MCEFBrowser; +import de.keksuccino.rinku.Rinku; +import de.keksuccino.rinku.RinkuBrowser; //? if fabric { import net.fabricmc.fabric.api.client.rendering.v1.HudRenderCallback; import net.minecraft.client.MinecraftClient; @@ -60,7 +60,7 @@ public static boolean shouldDeliverHudBrowserInput(MinecraftClient client) { //? } else { /*public static boolean shouldDeliverHudBrowserInput(Minecraft client) {*/ //? } - if (client == null || !MCEF.isInitialized()) { + if (client == null || !Rinku.isInitialized()) { return false; } //? if fabric { @@ -86,7 +86,7 @@ public static boolean shouldForwardHudArrowKeys(MinecraftClient client) { //? } else { /*public static boolean shouldForwardHudArrowKeys(Minecraft client) {*/ //? } - if (client == null || !MCEF.isInitialized()) { + if (client == null || !Rinku.isInitialized()) { return false; } //? if fabric { @@ -229,7 +229,7 @@ public static void toggleHud(MinecraftClient client) { //? } return; } - if (!MCEF.isInitialized()) { + if (!Rinku.isInitialized()) { notifyMcefMissing(client); return; } @@ -262,7 +262,7 @@ public static void toggleInteractive(MinecraftClient client) { //? } else { /*public static void toggleInteractive(Minecraft client) {*/ //? } - if (!MCEF.isInitialized()) { + if (!Rinku.isInitialized()) { return; } //? if fabric { @@ -356,7 +356,7 @@ public static void onGuiClosed(MinecraftClient client) { } } - static void onHudBrowserLoadStart(MCEFBrowser browser) { + static void onHudBrowserLoadStart(RinkuBrowser browser) { if (browser == null) { return; } @@ -365,7 +365,7 @@ static void onHudBrowserLoadStart(MCEFBrowser browser) { } } - static void onHudBrowserLoadFinished(MCEFBrowser browser) { + static void onHudBrowserLoadFinished(RinkuBrowser browser) { if (browser == null) { return; } @@ -407,7 +407,7 @@ private static void onHudRender(RenderGuiEvent.Post event) { if (!hudVisible) { return; } - MCEFBrowser browser = WebSession.hudBrowser(); + RinkuBrowser browser = WebSession.hudBrowser(); if (browser == null) { return; } @@ -429,7 +429,7 @@ private static void onHudRender(RenderGuiEvent.Post event) { //? if >=1.21.5 { net.minecraft.util.Identifier tex = browser.getTextureIdentifier(); //? } else { - /*net.minecraft.util.Identifier tex = browser.getTextureLocation();*/ + /*net.minecraft.util.Identifier tex = browser.getTextureIdentifier();*/ //? } if (tex == null) { return; @@ -446,7 +446,7 @@ private static void onHudRender(RenderGuiEvent.Post event) { //? if >=1.21.5 { net.minecraft.resources.Identifier tex = browser.getTextureIdentifier(); //? } else { - net.minecraft.resources.ResourceLocation tex = browser.getTextureLocation(); + net.minecraft.resources.ResourceLocation tex = browser.getTextureIdentifier(); //? } if (tex == null) return; int sw = client.getWindow().getGuiScaledWidth(); @@ -502,7 +502,7 @@ static void resizeBrowser(MinecraftClient client) { //? } else { /*static void resizeBrowser(Minecraft client) {*/ //? } - MCEFBrowser browser = WebSession.hudBrowser(); + RinkuBrowser browser = WebSession.hudBrowser(); if (browser == null) { return; } diff --git a/src/main/java/land/webgui/WebSession.java b/src/main/java/land/webgui/WebSession.java index 14906d0..e0dd0e4 100644 --- a/src/main/java/land/webgui/WebSession.java +++ b/src/main/java/land/webgui/WebSession.java @@ -1,11 +1,11 @@ package land.webgui; -import com.cinemamod.mcef.MCEF; -import com.cinemamod.mcef.MCEFBrowser; +import de.keksuccino.rinku.Rinku; +import de.keksuccino.rinku.RinkuBrowser; public final class WebSession { - private static MCEFBrowser browser; - private static MCEFBrowser suspendedHudBrowser; + private static RinkuBrowser browser; + private static RinkuBrowser suspendedHudBrowser; private static Mode mode = Mode.NONE; public enum Mode { @@ -16,11 +16,11 @@ public enum Mode { private WebSession() {} - public static MCEFBrowser browser() { + public static RinkuBrowser browser() { return browser; } - public static MCEFBrowser hudBrowser() { + public static RinkuBrowser hudBrowser() { if (mode == Mode.HUD_OVERLAY) { return browser; } @@ -70,7 +70,7 @@ public static void closeGuiAndRestoreHud() { WebviewClientBridge.clearCache(); } - public static MCEFBrowser openForGui(String url) { + public static RinkuBrowser openForGui(String url) { closeSuspendedHudBrowser(); if (mode == Mode.HUD_OVERLAY && browser != null) { suspendedHudBrowser = browser; @@ -78,13 +78,13 @@ public static MCEFBrowser openForGui(String url) { } else { closeActiveBrowser(); } - browser = MCEF.createBrowser(url, true); + browser = Rinku.createBrowser(url, true); mode = Mode.GUI_SCREEN; WebviewClientBridge.clearCache(); return browser; } - public static MCEFBrowser openForHud(String url) { + public static RinkuBrowser openForHud(String url) { closeSuspendedHudBrowser(); if (mode == Mode.HUD_OVERLAY && browser != null) { browser.loadURL(url); @@ -92,7 +92,7 @@ public static MCEFBrowser openForHud(String url) { return browser; } closeActiveBrowser(); - browser = MCEF.createBrowser(url, true); + browser = Rinku.createBrowser(url, true); mode = Mode.HUD_OVERLAY; WebviewClientBridge.clearCache(); return browser; diff --git a/src/main/java/land/webgui/WebViewLayout.java b/src/main/java/land/webgui/WebViewLayout.java index 3f42e31..1003406 100644 --- a/src/main/java/land/webgui/WebViewLayout.java +++ b/src/main/java/land/webgui/WebViewLayout.java @@ -1,13 +1,13 @@ package land.webgui; -import com.cinemamod.mcef.MCEFBrowser; +import de.keksuccino.rinku.RinkuBrowser; import org.lwjgl.glfw.GLFW; /** * Sizing/scale helpers for the embedded browser. The browser view is the physical framebuffer size * (so its texture draws 1:1 → crisp on HiDPI), and CEF page zoom scales the content up by the OS * content scale so the page's CSS viewport stays logical and independent of Minecraft's GUI Scale. - * Uses only the public {@link MCEFBrowser#setZoomLevel} API — no device_scale_factor, no reflection. + * Uses only the public {@link RinkuBrowser#setZoomLevel} API — no device_scale_factor, no reflection. */ final class WebViewLayout { private WebViewLayout() {} @@ -40,7 +40,7 @@ static double zoomLevel() { } /** Applies the content-scale zoom. CEF may reset zoom on navigation, so call after load too. */ - static void applyZoom(MCEFBrowser browser) { + static void applyZoom(RinkuBrowser browser) { if (browser == null) { return; } diff --git a/src/main/java/land/webgui/WebViewScreen.java b/src/main/java/land/webgui/WebViewScreen.java index dc3c1e5..8a88506 100644 --- a/src/main/java/land/webgui/WebViewScreen.java +++ b/src/main/java/land/webgui/WebViewScreen.java @@ -1,7 +1,7 @@ package land.webgui; -import com.cinemamod.mcef.MCEF; -import com.cinemamod.mcef.MCEFBrowser; +import de.keksuccino.rinku.Rinku; +import de.keksuccino.rinku.RinkuBrowser; //? if >=1.21.5 { //? if fabric { import net.minecraft.client.gl.RenderPipelines; @@ -43,7 +43,7 @@ public class WebViewScreen extends Screen { private static boolean guiPageReady; private final String initialUrl; - private MCEFBrowser browser; + private RinkuBrowser browser; //? if fabric { public WebViewScreen(String startUrl) { @@ -60,7 +60,7 @@ public WebViewScreen(String startUrl) { @Override protected void init() { super.init(); - if (!MCEF.isInitialized()) { + if (!Rinku.isInitialized()) { //? if fabric { if (this.client != null && this.client.player != null) { this.client.player.sendMessage(Text.translatable("message.webgui.mcef_not_ready"), false); @@ -177,7 +177,7 @@ public void onClose() { }*/ //? } - static void onGuiBrowserLoadStart(MCEFBrowser browser) { + static void onGuiBrowserLoadStart(RinkuBrowser browser) { if (browser == null) { return; } @@ -186,7 +186,7 @@ static void onGuiBrowserLoadStart(MCEFBrowser browser) { } } - static void onGuiBrowserLoadFinished(MCEFBrowser browser) { + static void onGuiBrowserLoadFinished(RinkuBrowser browser) { if (browser == null) { return; } @@ -203,7 +203,7 @@ public void render(DrawContext context, int mouseX, int mouseY, float delta) { //? if >=1.21.5 { Identifier textureLocation = browser.getTextureIdentifier(); //? } else { - /*Identifier textureLocation = browser.getTextureLocation();*/ + /*Identifier textureLocation = browser.getTextureIdentifier();*/ //? } if (textureLocation != null) { int fw = getBrowserWidth(); @@ -227,7 +227,7 @@ public void render(GuiGraphics context, int mouseX, int mouseY, float delta) { //? if >=1.21.5 { net.minecraft.resources.Identifier textureLocation = browser.getTextureIdentifier(); //? } else { - net.minecraft.resources.ResourceLocation textureLocation = browser.getTextureLocation(); + net.minecraft.resources.ResourceLocation textureLocation = browser.getTextureIdentifier(); //? } if (textureLocation != null) { int fw = getBrowserWidth(); diff --git a/src/main/java/land/webgui/WebviewClientBridge.java b/src/main/java/land/webgui/WebviewClientBridge.java index 9345bbf..a457e2e 100644 --- a/src/main/java/land/webgui/WebviewClientBridge.java +++ b/src/main/java/land/webgui/WebviewClientBridge.java @@ -1,7 +1,7 @@ package land.webgui; -import com.cinemamod.mcef.MCEF; -import com.cinemamod.mcef.MCEFBrowser; +import de.keksuccino.rinku.Rinku; +import de.keksuccino.rinku.RinkuBrowser; import com.google.gson.Gson; import com.google.gson.JsonObject; //? if fabric { @@ -54,11 +54,11 @@ private static void tryPush(MinecraftClient client, boolean requireTexture, bool /*private static void tryPush(Minecraft client, boolean requireTexture, boolean ignoreDedup) { LocalPlayer player = client.player;*/ //? } - if (!MCEF.isInitialized()) return; + if (!Rinku.isInitialized()) return; if (player == null) return; - MCEFBrowser main = WebSession.browser(); - MCEFBrowser hud = WebSession.hudBrowser(); + RinkuBrowser main = WebSession.browser(); + RinkuBrowser hud = WebSession.hudBrowser(); //? if fabric { boolean hasMain = main != null && (!requireTexture || main.isTextureReady()); //? } else { @@ -90,7 +90,7 @@ private static void tryPush(MinecraftClient client, boolean requireTexture, bool if (hasHud) executeJs(hud, js); } - private static void executeJs(MCEFBrowser browser, String js) { + private static void executeJs(RinkuBrowser browser, String js) { try { String url = browser.getURL(); browser.executeJavaScript(js, url != null ? url : "", 0); diff --git a/src/main/java/land/webgui/WebviewClientEmit.java b/src/main/java/land/webgui/WebviewClientEmit.java index 9047053..4ff4967 100644 --- a/src/main/java/land/webgui/WebviewClientEmit.java +++ b/src/main/java/land/webgui/WebviewClientEmit.java @@ -1,6 +1,6 @@ package land.webgui; -import com.cinemamod.mcef.MCEFBrowser; +import de.keksuccino.rinku.RinkuBrowser; import com.google.gson.Gson; public final class WebviewClientEmit { @@ -10,8 +10,8 @@ private WebviewClientEmit() {} /** Called on the Minecraft client thread after receiving a WebviewEmitS2CPayload. */ public static void dispatch(String eventName, String jsonPayload) { - MCEFBrowser main = WebSession.browser(); - MCEFBrowser hud = WebSession.hudBrowser(); + RinkuBrowser main = WebSession.browser(); + RinkuBrowser hud = WebSession.hudBrowser(); if (main == null && hud == null) return; // GSON.toJson produces a properly escaped JS string literal, e.g. "\"my\\\"event\"" @@ -30,7 +30,7 @@ public static void dispatch(String eventName, String jsonPayload) { if (hud != null && hud != main) executeJs(hud, js); } - private static void executeJs(MCEFBrowser browser, String js) { + private static void executeJs(RinkuBrowser browser, String js) { try { String url = browser.getURL(); browser.executeJavaScript(js, url != null ? url : "", 0); diff --git a/src/main/java/land/webgui/WebviewPageLoadHooks.java b/src/main/java/land/webgui/WebviewPageLoadHooks.java index 4ff352a..6130257 100644 --- a/src/main/java/land/webgui/WebviewPageLoadHooks.java +++ b/src/main/java/land/webgui/WebviewPageLoadHooks.java @@ -1,7 +1,7 @@ package land.webgui; -import com.cinemamod.mcef.MCEF; -import com.cinemamod.mcef.MCEFBrowser; +import de.keksuccino.rinku.Rinku; +import de.keksuccino.rinku.RinkuBrowser; //? if fabric { import net.minecraft.client.MinecraftClient; //? } else { @@ -16,12 +16,12 @@ public final class WebviewPageLoadHooks { private WebviewPageLoadHooks() {} public static void register() { - MCEF.getClient().addLoadHandler(new CefLoadHandlerAdapter() { + Rinku.getClient().addLoadHandler(new CefLoadHandlerAdapter() { @Override public void onLoadStart(CefBrowser browser, CefFrame frame, org.cef.network.CefRequest.TransitionType transitionType) { - MCEFBrowser active = WebSession.browser(); + RinkuBrowser active = WebSession.browser(); if (active == null || browser != active) return; if (WebSession.mode() == WebSession.Mode.HUD_OVERLAY) { WebHudOverlay.onHudBrowserLoadStart(active); @@ -32,7 +32,7 @@ public void onLoadStart(CefBrowser browser, CefFrame frame, @Override public void onLoadEnd(CefBrowser browser, CefFrame frame, int httpStatusCode) { - MCEFBrowser active = WebSession.browser(); + RinkuBrowser active = WebSession.browser(); if (active == null || browser != active) return; injectBridgeScript(active); @@ -57,7 +57,7 @@ public void onLoadEnd(CefBrowser browser, CefFrame frame, int httpStatusCode) { public void onLoadError(CefBrowser browser, CefFrame frame, CefLoadHandler.ErrorCode errorCode, String errorText, String failedUrl) { - MCEFBrowser active = WebSession.browser(); + RinkuBrowser active = WebSession.browser(); if (active == null || browser != active) return; if (WebSession.mode() == WebSession.Mode.HUD_OVERLAY) { WebHudOverlay.onHudBrowserLoadFinished(active); @@ -68,7 +68,7 @@ public void onLoadError(CefBrowser browser, CefFrame frame, }); } - private static void injectBridgeScript(MCEFBrowser browser) { + private static void injectBridgeScript(RinkuBrowser browser) { try { String url = browser.getURL(); browser.executeJavaScript(WebviewScriptInject.bridgeSetup(), url != null ? url : "", 0); diff --git a/src/main/java/land/webgui/WebviewPageToClientBridge.java b/src/main/java/land/webgui/WebviewPageToClientBridge.java index d739782..f1abac3 100644 --- a/src/main/java/land/webgui/WebviewPageToClientBridge.java +++ b/src/main/java/land/webgui/WebviewPageToClientBridge.java @@ -1,6 +1,6 @@ package land.webgui; -import com.cinemamod.mcef.MCEF; +import de.keksuccino.rinku.Rinku; import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.JsonParser; @@ -43,7 +43,7 @@ public boolean onQuery(CefBrowser browser, CefFrame frame, long queryId, return true; } }, true); - MCEF.getClient().getHandle().addMessageRouter(router); + Rinku.getClient().getHandle().addMessageRouter(router); } private static void dispatch(CefFrame frame, String request, CefQueryCallback callback) { diff --git a/src/main/java/land/webgui/mixin/CefUtilMixin.java b/src/main/java/land/webgui/mixin/CefUtilMixin.java index 6ec4712..3021b30 100644 --- a/src/main/java/land/webgui/mixin/CefUtilMixin.java +++ b/src/main/java/land/webgui/mixin/CefUtilMixin.java @@ -8,7 +8,7 @@ import java.util.Arrays; import java.util.List; -@Mixin(targets = "com.cinemamod.mcef.CefUtil", remap = false) +@Mixin(targets = "de.keksuccino.rinku.util.CefUtil", remap = false) public class CefUtilMixin { @ModifyArg( @@ -25,8 +25,6 @@ public class CefUtilMixin { args.add("--enable-gpu-rasterization"); // Out-of-process rasterization: separate GPU process handles raster work args.add("--enable-oop-rasterization"); - // Prefer native desktop OpenGL over ANGLE (better performance on desktop) - args.add("--use-gl=desktop"); // Remove GPU vsync so CEF isn't throttled by display refresh during off-screen render args.add("--disable-gpu-vsync"); return args.toArray(new String[0]); diff --git a/src/main/java/land/webgui/mixin/MouseMixin.java b/src/main/java/land/webgui/mixin/MouseMixin.java index ee3898e..22e57d0 100644 --- a/src/main/java/land/webgui/mixin/MouseMixin.java +++ b/src/main/java/land/webgui/mixin/MouseMixin.java @@ -1,6 +1,6 @@ package land.webgui.mixin; -import com.cinemamod.mcef.MCEFBrowser; +import de.keksuccino.rinku.RinkuBrowser; import land.webgui.WebHudOverlay; import land.webgui.WebSession; //? if fabric { @@ -93,7 +93,7 @@ public class MouseMixin { if (!WebHudOverlay.containsMouse(mx, my, client)) { return; } - MCEFBrowser browser = WebSession.browser(); + RinkuBrowser browser = WebSession.browser(); if (browser != null) { int lx = WebHudOverlay.toBrowserLocalX(mx, client); int ly = WebHudOverlay.toBrowserLocalY(my, client); @@ -157,7 +157,7 @@ public class MouseMixin { //? } return; } - MCEFBrowser browser = WebSession.browser(); + RinkuBrowser browser = WebSession.browser(); if (browser == null) { return; } @@ -219,7 +219,7 @@ public class MouseMixin { if (!WebHudOverlay.containsMouse(mx, my, client)) { return; } - MCEFBrowser browser = WebSession.browser(); + RinkuBrowser browser = WebSession.browser(); if (browser == null) { return; } diff --git a/versions/1.20.1-fabric/gradle.properties b/versions/1.20.1-fabric/gradle.properties index 639d910..34d7a6c 100644 --- a/versions/1.20.1-fabric/gradle.properties +++ b/versions/1.20.1-fabric/gradle.properties @@ -2,6 +2,6 @@ minecraft_version=1.20.1 yarn_mappings=1.20.1+build.10 loader_version=0.19.5 fabric_version=0.92.12+1.20.1 -mcef_version=2.2.0-1.20.1-fabric +rinku_version=3.0.4-1.20.1-fabric minecraft_range=>=1.20.1 <=1.20.1 loader_type=fabric diff --git a/versions/1.20.1-neoforge/gradle.properties b/versions/1.20.1-neoforge/gradle.properties index 102eb91..5ce3888 100644 --- a/versions/1.20.1-neoforge/gradle.properties +++ b/versions/1.20.1-neoforge/gradle.properties @@ -1,5 +1,5 @@ minecraft_version=1.20.1 loader_type=neoforge neoforge_version=47.3.0 -mcef_version=2.2.0-1.20.1-forge +rinku_version=3.0.4-1.20.1-forge minecraft_range=[1.20.1] diff --git a/versions/1.21.1-fabric/gradle.properties b/versions/1.21.1-fabric/gradle.properties index f689c10..e3f31cf 100644 --- a/versions/1.21.1-fabric/gradle.properties +++ b/versions/1.21.1-fabric/gradle.properties @@ -2,6 +2,6 @@ minecraft_version=1.21.1 yarn_mappings=1.21.1+build.3 loader_version=0.19.5 fabric_version=0.116.17+1.21.1 -mcef_version=2.2.0-1.21.1-fabric +rinku_version=3.0.4-1.21.1-fabric minecraft_range=>=1.21 <=1.21.1 loader_type=fabric diff --git a/versions/1.21.1-neoforge/gradle.properties b/versions/1.21.1-neoforge/gradle.properties index f408eb2..f6e27a8 100644 --- a/versions/1.21.1-neoforge/gradle.properties +++ b/versions/1.21.1-neoforge/gradle.properties @@ -1,5 +1,5 @@ minecraft_version=1.21.1 loader_type=neoforge neoforge_version=21.1.250 -mcef_version=2.2.0-1.21.1-neoforge +rinku_version=3.0.4-1.21.1-neoforge minecraft_range=[1.21,1.21.1] diff --git a/versions/1.21.11-fabric/gradle.properties b/versions/1.21.11-fabric/gradle.properties index a1671cd..79f34df 100644 --- a/versions/1.21.11-fabric/gradle.properties +++ b/versions/1.21.11-fabric/gradle.properties @@ -2,6 +2,6 @@ minecraft_version=1.21.11 yarn_mappings=1.21.11+build.6 loader_version=0.19.5 fabric_version=0.141.6+1.21.11 -mcef_version=2.2.0-1.21.11-fabric +rinku_version=3.0.4-1.21.11-fabric minecraft_range=>=1.21.5 <=1.21.11 loader_type=fabric diff --git a/versions/1.21.11-neoforge/gradle.properties b/versions/1.21.11-neoforge/gradle.properties index 412d06d..9fb04a6 100644 --- a/versions/1.21.11-neoforge/gradle.properties +++ b/versions/1.21.11-neoforge/gradle.properties @@ -1,5 +1,5 @@ minecraft_version=1.21.11 loader_type=neoforge neoforge_version=21.11.45 -mcef_version=2.2.0-1.21.11-neoforge +rinku_version=3.0.4-1.21.11-neoforge minecraft_range=[1.21.5,1.21.11] diff --git a/versions/26.1.2-neoforge/gradle.properties b/versions/26.1.2-neoforge/gradle.properties index 255a19f..1ef1c8e 100644 --- a/versions/26.1.2-neoforge/gradle.properties +++ b/versions/26.1.2-neoforge/gradle.properties @@ -1,6 +1,6 @@ minecraft_version=26.1.2 loader_type=neoforge neoforge_version=26.1.2.107 -mcef_version=2.2.0-26.1.1-neoforge +rinku_version=3.0.4-26.1.2-neoforge minecraft_range=[26.1.2,26.1.2] java_version=25 diff --git a/versions/26.2-neoforge/gradle.properties b/versions/26.2-neoforge/gradle.properties index 1d07792..1636ad0 100644 --- a/versions/26.2-neoforge/gradle.properties +++ b/versions/26.2-neoforge/gradle.properties @@ -1,6 +1,6 @@ minecraft_version=26.2 loader_type=neoforge neoforge_version=26.2.0.82 -mcef_version=2.2.1-26.2-neoforge +rinku_version=3.0.4-26.2-neoforge minecraft_range=[26.2,26.2] java_version=25 From b2571dbaa8e8fc50a0405b1eb8fd6df560f48786 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9A=D0=B8=D1=80=D0=B8=D0=BB=D0=BB=20=D0=93=D1=80=D0=B8?= =?UTF-8?q?=D0=BD=D0=B5=D0=B2?= Date: Tue, 8 Sep 2026 19:18:56 +0500 Subject: [PATCH 2/4] Keep Rinku out of the dev server run MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Both Minecraft 26 dev servers died during mod loading: ModLoadingException: Rinku (rinku) has failed to load correctly java.lang.NoClassDefFoundError: net/minecraft/client/gui/screens/Screen Rinku is a client-side library, and NeoForge's dev dist cleaner strips the client classes it constructs against. A plain runtimeOnly dependency feeds both dev runs, so runServer loaded it and died. Production was never affected — WebGUI does not declare Rinku in neoforge.mods.toml, so a real server never loads it — which is why this only ever bit developers, and only on 26.x where Rinku's builds are not dist-safe. The obvious fix does not work: moddev has dropped per-run classpaths on modern Minecraft, and clientAdditionalRuntimeClasspath answers "no additional classpath anymore for Minecraft 1.21.11". So Rinku now stays off the runtime classpath entirely and reaches the client run as a jar in its mods folder, installed by a task bound to runClient alone. That also matches how the mod is actually used: a player installs Rinku, a server operator does not. Minecraft 26 gets the server smoke test in CI as well. The dev server was broken on both 26 targets for months only because nothing ever booted one there. Verified: both 26 servers reach "Done" with WebGUI initialised, 1.21.1 and 1.21.11 servers still do, and the 26.2 client still brings up Chromium and the page bridge with Rinku coming from the mods folder. Closes #29 --- .github/workflows/build.yml | 5 +++++ build.gradle | 30 +++++++++++++++++++++++++----- 2 files changed, 30 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f02c478..fcd2c4c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -26,10 +26,15 @@ jobs: - version: 1.21.1-neoforge - version: 1.21.11-neoforge smoke: true + # Minecraft 26 gets the server smoke test too: the dev server was + # broken on both 26 targets for months precisely because nothing + # booted one in CI. - version: 26.2-neoforge java: '25' + smoke: true - version: 26.1.2-neoforge java: '25' + smoke: true steps: - uses: actions/checkout@v4 diff --git a/build.gradle b/build.gradle index d18b529..6cba9bf 100644 --- a/build.gradle +++ b/build.gradle @@ -63,6 +63,24 @@ if (sc_loader == "fabric") { include(modImplementation("maven.modrinth:rinku:${rinku_version}")) } } else { + configurations { + rinkuArtifact + } + + // Rinku is a client-side library, and NeoForge's dev dist cleaner strips + // the client classes it constructs against. A plain `runtimeOnly` feeds + // BOTH dev runs, so runServer used to die during mod loading with + // NoClassDefFoundError on Screen — on every Minecraft 26 build, and never + // caught because 26.x had no server smoke test. moddev's per-run classpath + // is gone on modern Minecraft ("no additional classpath anymore"), so scope + // it by installing the jar into the client run's mods folder instead. That + // also mirrors production, where a server operator simply never installs + // Rinku and WebGUI never declares it as a dependency. + def installRinkuForClientRun = tasks.register('installRinkuForClientRun', Copy) { + from configurations.rinkuArtifact + into layout.projectDirectory.dir('run/mods') + } + neoForge { version = project.neoforge_version // Register the mod so moddev's JUnit runtime can load it during tests. @@ -81,25 +99,27 @@ if (sc_loader == "fabric") { runs { client { client() + // Rinku reaches the client the way a player installs it: as a + // jar in the mods folder, not on the runtime classpath. Keeping + // it off the classpath is what keeps runServer alive — see the + // installRinkuForClientRun task below. + taskBefore(installRinkuForClientRun) } server { server() } } } + + // Rinku ships its NeoForge builds inconsistently: 26.2 has classes at the // top level, but 1.21.1 and 26.1.2 are jarJar wrappers whose real mod sits // in META-INF/jarjar/. NeoForge unpacks that at runtime, so the game is // happy either way — javac is not, and sees an empty jar. Compile against // whatever actually holds the classes; the published artifact stays on the // runtime classpath untouched. - configurations { - rinkuArtifact - } - dependencies { rinkuArtifact "maven.modrinth:rinku:${rinku_version}" - runtimeOnly "maven.modrinth:rinku:${rinku_version}" } def rinkuClassesDir = layout.buildDirectory.dir('rinku-compile') From 6670d23fa1dc5337be73991e5ba2bdfe77c94081 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9A=D0=B8=D1=80=D0=B8=D0=BB=D0=BB=20=D0=93=D1=80=D0=B8?= =?UTF-8?q?=D0=BD=D0=B5=D0=B2?= Date: Tue, 8 Sep 2026 19:24:11 +0500 Subject: [PATCH 3/4] release: 1.7.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Covers the whole stack: the Rinku rename and dependency refresh, the Rinku 3.0.4 migration with its 26.2 crash fix, and the dev server fix. The upgrade is not transparent for everyone, so the entry says who has to act. Rinku's mod id changed from mcef to rinku, and WebGUI bundles it on Fabric via jar-in-jar but not on NeoForge — so Fabric players do nothing while NeoForge players must swap the jar in their mods folder. The README claimed NeoForge "needs no extra dependencies", which was never true: nothing bundles the browser library there. The installation steps now say so per loader. --- CHANGELOG.md | 15 +++++++++++++++ README.md | 4 +++- gradle.properties | 2 +- 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2742202..a7ca7c6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,20 @@ # Changelog +## 1.7.0 - 2026-09-08 + +### Changed +- **The browser library is now [Rinku](https://modrinth.com/mod/rinku), not MCEF.** MCEF [Keksuccino's Fork] was renamed, and its mod id changed from `mcef` to `rinku`. + - **On Fabric there is nothing to do** — Rinku ships inside the WebGUI jar, as MCEF did before it. + - **On NeoForge, replace the MCEF jar in your mods folder with Rinku.** WebGUI does not bundle it there, so without the swap there is no browser to draw. +- Chromium is now version 151. The browser is no longer capped at 60 FPS, and browser windows can be muted. +- **Minecraft 26.1 and 26.2 are out of beta.** Both ship as regular releases now instead of beta builds. +- Updated NeoForge (26.2.0.82, 26.1.2.107, 21.11.45, 21.1.250), Fabric Loader 0.19.5, Fabric API, and Yarn for 1.21.11. + +### Fixed +- **Fixed a client crash on Minecraft 26.2** that hit after opening a web GUI a handful of times: `Texture view Sampler0 (MCEF Browser Texture 1x1) has been closed!`. If you worked around it by setting `browser-preload-enabled=false`, you can turn browser preloading back on. +- **Fixed the browser refusing to start against current Chromium.** WebGUI passed `--use-gl=desktop`, a switch Chromium removed years ago. It went unnoticed while the browser library still shipped an older Chromium, and would have broken on any Chromium update. +- Fixed the Minecraft 26 development server failing to boot, which made the mod impossible to test locally on those versions. Released servers were never affected. + ## 1.6.2 - 2026-07-26 ### Fixed diff --git a/README.md b/README.md index 1558529..9414db1 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,9 @@ Chromium (~150 MB) is downloaded automatically on first launch. Include [Rinku]( ## Installation 1. Download the latest JAR from [Modrinth](https://modrinth.com/project/webgui) (pick the right MC version). -2. Place it in `.minecraft/mods/` (on Fabric, also add Fabric API; NeoForge needs no extra dependencies). +2. Place it in `.minecraft/mods/`. + - **Fabric:** also add Fabric API. The browser library ships inside the WebGUI jar. + - **NeoForge:** also add [Rinku](https://modrinth.com/mod/rinku) — it is not bundled, and WebGUI cannot render a page without it. 3. Start the game. --- diff --git a/gradle.properties b/gradle.properties index 6b9ec92..5a52918 100644 --- a/gradle.properties +++ b/gradle.properties @@ -2,7 +2,7 @@ org.gradle.jvmargs=-Xmx4G org.gradle.parallel=true dev.kikugie.stonecutter.hard_mode=true -mod_version=1.6.2 +mod_version=1.7.0 maven_group=land.webgui archives_base_name=webgui From 9c7776403541d8f9ce4ed2b889b3b65a395c0bfe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9A=D0=B8=D1=80=D0=B8=D0=BB=D0=BB=20=D0=93=D1=80=D0=B8?= =?UTF-8?q?=D0=BD=D0=B5=D0=B2?= Date: Tue, 8 Sep 2026 19:35:00 +0500 Subject: [PATCH 4/4] Declare Rinku as a NeoForge dependency so the game stops crashing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Installing WebGUI alone on NeoForge kills the game during mod loading: @Mixin target com.cinemamod.mcef.CefUtil was not found NoClassDefFoundError: org/cef/handler/CefDisplayHandler at land.webgui.WebGUIMod. Reproduced on the released 1.6.2 from Modrinth with Prism, so this is not a regression — it is shipping today. WebGUI declared the browser library nowhere: not in neoforge.mods.toml, not in the Modrinth version metadata, and nothing is bundled into the NeoForge jar. So the launcher installs nothing, FML does not know the mod is needed, and the player gets a crash report instead of a missing-dependency screen. Fabric was never affected: there the library rides inside the jar via jar-in-jar, which is why it feels like it installs itself. Declaring it required does both jobs — launchers resolve and install Rinku, and a hand-copied install fails with FML's readable screen rather than a stack trace. Bundling it with jarJar the way Fabric does is worth considering too, but Rinku's NeoForge artifacts are themselves jarJar wrappers on several versions, so that needs more care than a one-liner. Refs #34 --- .github/workflows/release.yml | 4 ++++ CHANGELOG.md | 3 ++- src/main/resources/META-INF/neoforge.mods.toml | 12 ++++++++++++ 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index cc4a18c..c82f3f5 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -155,6 +155,7 @@ jobs: version: ${{ steps.meta.outputs.version }}+mc1.21.1-neoforge version-type: release loaders: neoforge + dependencies: rinku(required) game-versions: '1.21.1' changelog: ${{ steps.notes.outputs.body }} files: versions/1.21.1-neoforge/build/libs/!(*-@(dev|sources|javadoc)).jar @@ -170,6 +171,7 @@ jobs: version: ${{ steps.meta.outputs.version }}+mc1.21.11-neoforge version-type: release loaders: neoforge + dependencies: rinku(required) game-versions: '1.21.11' changelog: ${{ steps.notes.outputs.body }} files: versions/1.21.11-neoforge/build/libs/!(*-@(dev|sources|javadoc)).jar @@ -185,6 +187,7 @@ jobs: version: ${{ steps.meta.outputs.version }}+mc26.2-neoforge version-type: release loaders: neoforge + dependencies: rinku(required) game-versions: '26.2' changelog: ${{ steps.notes.outputs.body }} files: versions/26.2-neoforge/build/libs/!(*-@(dev|sources|javadoc)).jar @@ -200,6 +203,7 @@ jobs: version: ${{ steps.meta.outputs.version }}+mc26.1.2-neoforge version-type: release loaders: neoforge + dependencies: rinku(required) game-versions: '26.1.2' changelog: ${{ steps.notes.outputs.body }} files: versions/26.1.2-neoforge/build/libs/!(*-@(dev|sources|javadoc)).jar diff --git a/CHANGELOG.md b/CHANGELOG.md index a7ca7c6..19d2932 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ ### Changed - **The browser library is now [Rinku](https://modrinth.com/mod/rinku), not MCEF.** MCEF [Keksuccino's Fork] was renamed, and its mod id changed from `mcef` to `rinku`. - **On Fabric there is nothing to do** — Rinku ships inside the WebGUI jar, as MCEF did before it. - - **On NeoForge, replace the MCEF jar in your mods folder with Rinku.** WebGUI does not bundle it there, so without the swap there is no browser to draw. + - **On NeoForge, install [Rinku](https://modrinth.com/mod/rinku) alongside WebGUI** and remove MCEF. WebGUI now declares Rinku as a required dependency, so launchers install it for you; if you copy jars by hand you need to add it yourself. - Chromium is now version 151. The browser is no longer capped at 60 FPS, and browser windows can be muted. - **Minecraft 26.1 and 26.2 are out of beta.** Both ship as regular releases now instead of beta builds. - Updated NeoForge (26.2.0.82, 26.1.2.107, 21.11.45, 21.1.250), Fabric Loader 0.19.5, Fabric API, and Yarn for 1.21.11. @@ -13,6 +13,7 @@ ### Fixed - **Fixed a client crash on Minecraft 26.2** that hit after opening a web GUI a handful of times: `Texture view Sampler0 (MCEF Browser Texture 1x1) has been closed!`. If you worked around it by setting `browser-preload-enabled=false`, you can turn browser preloading back on. - **Fixed the browser refusing to start against current Chromium.** WebGUI passed `--use-gl=desktop`, a switch Chromium removed years ago. It went unnoticed while the browser library still shipped an older Chromium, and would have broken on any Chromium update. +- **Fixed the game crashing on startup on NeoForge when the browser library was missing.** WebGUI declared no dependency on it at all, so launchers installed nothing and the game died during mod loading with `NoClassDefFoundError: org/cef/handler/CefDisplayHandler` instead of telling you what was missing. Fabric was never affected, because the library ships inside the jar there. - Fixed the Minecraft 26 development server failing to boot, which made the mod impossible to test locally on those versions. Released servers were never affected. ## 1.6.2 - 2026-07-26 diff --git a/src/main/resources/META-INF/neoforge.mods.toml b/src/main/resources/META-INF/neoforge.mods.toml index 0c0e5ab..08cddd8 100644 --- a/src/main/resources/META-INF/neoforge.mods.toml +++ b/src/main/resources/META-INF/neoforge.mods.toml @@ -22,5 +22,17 @@ versionRange = "${minecraft_range}" ordering = "NONE" side = "BOTH" +# Rinku is the browser. Without it WebGUI does not merely fail to render a page +# — its mixins target Rinku classes, so the game dies during mod construction +# with NoClassDefFoundError. Declaring the dependency turns that crash into +# FML's "missing dependency" screen, and lets launchers install it. +# Fabric needs no equivalent: there Rinku ships inside the jar via jar-in-jar. +[[dependencies.webgui]] +modId = "rinku" +type = "required" +versionRange = "[3,)" +ordering = "BEFORE" +side = "CLIENT" + [[mixins]] config = "webgui.mixins.json"