abstract
- question
- Can a real web page (Ko-fi, with payments) open inside Minecraft, on the new versions, without depending on MCEF?
- method
- Write my own library on top of java-cef in off-screen mode: Chromium paints into a buffer, the game uploads it to a texture, and mouse and keyboard go in as GLFW codes
- result
- pane v0.1.1 (MIT, on JitPack). Tested on all three Minecraft versions jukz supports with Ko-fi, text fields, scrolling, links and back
- limits
- Chromium weighs about 120 MB and is downloaded the first time. The builds are CinemaMod's, copied unchanged; I don't build Chromium
I wanted jukz's Ko-fi button to open the page inside the game, not in the system browser. What existed for that is MCEF, and the prototype worked with it on Minecraft 1.21.1. But MCEF stops at 1.21.4 (jukz supports 1.21.11 and 26.2) and it's LGPL. So I wrote my own library: pane
The idea: Chromium without a window
A normal browser draws into its own window. In a game that doesn't work: the page has to be one more texture, drawn wherever the game wants. CEF (the Chromium Embedded Framework) has a mode for this, off-screen: Chromium opens nothing, it just paints the page into a memory buffer and says when it changed
Pane.open("https://…")
│
▼
Chromium off-screen (java-cef)
│ onPaint(dirty rectangles, BGRA buffer)
▼
pane copies only those rectangles ──► version++
│
▼
the game: if the version changed ──► upload the texturepane copies into its own buffer only the rectangles Chromium says changed, and bumps a version number. That way the game doesn't have to compare pixels: every frame it checks whether the version is different, and only then uploads the texture again
Pane.install(progress); // the first time: downloads Chromium
Pane.start();
PaneView page = Pane.open("https://ko-fi.com/…");
// every frame
Pane.pump();
if (page.version() != last) upload(page.pixels()); // BGRADropdown menus (a <select>) come separately, as a second buffer with its position, so they can be drawn on top of the page
Input, in the game's language
Minecraft and most Java games read the keyboard and mouse through GLFW. CinemaMod's fork of java-cef already understands those codes, so pane takes them as they are: mouseDown(x, y, button, mods), typed(c, mods), wheel(x, y, notches, mods). pane fills in what's missing: counting double and triple clicks, keeping buttons held while dragging, and the usual shortcuts (Ctrl+R reloads, Alt+← and → navigate, Ctrl with + and - zooms)
And the cursor: if you hand it the GLFW window, pane turns it into a hand over links and a text bar over fields, like any browser
What the native side doesn't tell you
The hardest part wasn't the big pieces, but three details on java-cef's native side that fail silently:
- New windows vanish. In off-screen mode the native side cancels every new window before telling Java, so a link with
target="_blank"just did nothing. pane injects a script that replaceswindow.openand opens those links in the same page - The buttons are swapped. GLFW numbers left 0, right 1 and middle 2; the native side reads 1 as middle and 2 as right. pane swaps them before sending
- Typed text goes a different way. A typed letter isn't the same as a key going down: the native side expects characters marked as «repeat», and otherwise the text field gets nothing
window.open = (url) => { if (url) location.href = new URL(url, location.href).href; return window; };I also chose not to turn off the same-origin policy, which MCEF turns off. It's the rule that keeps one page from reading another's data, and people pay on these pages
Its own installer
Chromium doesn't fit in a mod jar. The first time, pane downloads the build for your system (Linux, Windows or Mac, x64 or ARM), checks it against its SHA-256 and unpacks it with its own tar.gz reader, no dependencies. It keeps the hash, and later runs don't even download
At first the files came from someone else's server. I copied all six to my own R2, at chromium.nuulm.com, and copying them uncovered a bug: the Windows .sha256 doesn't come as hash name, but as PowerShell's table, and pane couldn't read it. Now it looks for the first run of 64 hex characters, whatever the format. That shipped as v0.1.1
ideaIn a library that wraps native code, the hard part isn't the API but what the native side does without telling you. Every one of those details was found by testing by hand inside the game, not by reading the docs
Limits
- The first run has to download about 120 MB
- The Chromium builds are CinemaMod's, copied unchanged: I don't build Chromium, and updates depend on them
- It needs Java 21 and LWJGL on the classpath, because the native side reads GLFW codes
Frequently asked questions
How do you open a web page inside Minecraft?
With a browser in off-screen mode: Chromium paints the page into a pixel buffer and the mod draws it as a texture. pane does that on top of java-cef, without opening windows
What is CEF's off-screen rendering?
A mode where Chromium has no window: it paints the page in memory and reports which rectangles changed, so the app can draw it wherever it wants
Why not use MCEF?
It stops at Minecraft 1.21.4 and it's LGPL. pane is its own code under MIT and works on 1.21.11 and 26.2
Why does a link with target="_blank" do nothing in off-screen java-cef?
The native side cancels new windows before telling Java. The way out is opening those links in the same page, replacing window.open with a script
References
- Chromium Embedded Framework (CEF) · off-screen rendering · bitbucket.org/chromiumembedded/cef
- java-cef · Java binding for CEF · github.com/chromiumembedded/java-cef
- CinemaMod/java-cef · fork that reads GLFW input · github.com/CinemaMod/java-cef
- GLFW · input reference (keys, mouse buttons, modifiers) · glfw.org/docs/latest/group__input.html
- Nuulz/pane · source code and v0.1.1 · github.com/Nuulz/pane