Skip to content

Handle cookie responses inside Limbo (Player#requestCookie support)#257

Open
SPYmesu wants to merge 4 commits into
Elytrium:masterfrom
SPYmesu:cookie-support
Open

Handle cookie responses inside Limbo (Player#requestCookie support)#257
SPYmesu wants to merge 4 commits into
Elytrium:masterfrom
SPYmesu:cookie-support

Conversation

@SPYmesu

@SPYmesu SPYmesu commented Jun 10, 2026

Copy link
Copy Markdown

Summary

While a player is inside a Limbo, the Velocity cookie flow never completes: Player#requestCookie(...) never resolves and CookieReceiveEvent is never fired. LimboSessionHandlerImpl doesn't handle ServerboundCookieResponsePacket, so the packet falls through to handleGeneric and the response is dropped.

This PR makes cookies work inside a Limbo. It also bundles the build changes that were required to compile the plugin and run data generation on a clean checkout (data generation deadlocked, and the newest version in the matrix — 26.1 — needs a newer JDK than the rest), plus a Gradle wrapper bump.

Changes

1. Cookie support inside Limbo (main change)

LimboSessionHandlerImpl now overrides handle(ServerboundCookieResponsePacket), mirroring Velocity's ClientPlaySessionHandler:

  • fires CookieReceiveEvent for the player;
  • if the result is allowed and the player currently has a backend connection, forwards the (event-rewritten, otherwise original) cookie response to that backend;
  • returns true so the packet is consumed instead of falling through to handleGeneric.

The cookie response already decodes correctly inside a Limbo (the Limbo registry overlays the PLAY registry); the only missing piece was the handler override, without which CookieReceiveEvent never fired and the request silently never completed. A player in a Limbo is usually not attached to a backend, so the forward is guarded by a getConnectedServer() != null check — exactly like the default play handler.

2. Fix data-generator hang (stdout pipe deadlock) — plugin/build.gradle

generateData launched the generator and waited via commandLine.execute(...).waitFor() without consuming its stdout/stderr. Once the child fills the OS pipe buffer it blocks on write() forever and Gradle blocks on waitFor() — a classic deadlock, reproducible on 1.18+ where the bundler prints one line per extracted library (a thread dump shows main stuck in FileOutputStream.writeBytes from net.minecraft.bundler.Main). Fixed by draining the streams with consumeProcessOutput() before waitFor().

3. Per-version JDK for data generation — plugin/build.gradle

The generator always ran on the JDK running Gradle. Newer versions need a newer runtime — 26.1 requires Java 25 (javaVersion.majorVersion in its manifest) — so running it under an older JDK throws UnsupportedClassVersionError, produces no reports, and fails the build later. Added selectJavaHome(requiredMajor): it reads each manifest's javaVersion.majorVersion and, when the current JDK is too old, picks an installed JDK that satisfies it by scanning sibling JDK directories' release files; otherwise it keeps the current JDK. This lets the build run on one base JDK while still generating data for versions that need a newer one.

4. Bump Gradle wrapper to 9.5.0

Updates the wrapper from 9.4.1 to 9.5.0 (the version the build was verified against).

Testing

  • ./gradlew build completes with BUILD SUCCESSFUL.
  • Data is generated for the full matrix (1.13 … 26.1), including 26.1 on Java 25 (> Using JDK 25 … for data generation).
  • The plugin artifact is produced at plugin/build/libs/limboapi-<version>-SNAPSHOT.jar.

Notes

  • The per-version JDK lookup (scanning sibling JDK dirs + reading release) is a pragmatic heuristic; I can switch it to Gradle Java toolchains (javaToolchains.launcherFor { ... }) if preferred.

SPYmesu and others added 2 commits June 10, 2026 17:52
Player#requestCookie never completed and CookieReceiveEvent never fired
while a player was inside a Limbo: LimboSessionHandlerImpl did not handle
ServerboundCookieResponsePacket, so it fell through to handleGeneric and
the response was dropped.

Override handle(ServerboundCookieResponsePacket) to mirror Velocity's
ClientPlaySessionHandler: fire CookieReceiveEvent and, when the result is
allowed and a backend connection exists, forward the (possibly rewritten)
response to that backend.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Drain the data-generator subprocess output (consumeProcessOutput) before
  waitFor(); otherwise it deadlocks once the child fills the stdout pipe
  buffer (reproducible on 1.18+ via the bundler's per-library extraction
  logging; a thread dump shows main stuck in FileOutputStream.writeBytes).
- Run each version's generator on a JDK that satisfies its manifest
  javaVersion.majorVersion (e.g. 26.1 requires Java 25), selecting an
  installed JDK by scanning sibling JDK dirs; fall back to the current JDK.
- Bump the Gradle wrapper to 9.5.0.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@UserNugget

UserNugget commented Jun 10, 2026

Copy link
Copy Markdown
Member

Is Claude auto-opened this PR?

If you need CookieReceiveEvent you have to stage cookie packets for later use like limbo already does with player settings and brand payloads, see this, this and this.

@SPYmesu

SPYmesu commented Jun 10, 2026

Copy link
Copy Markdown
Author

No, it wasn't auto-opened, claude helps me to make PR and its text because im not good enough in technical english.

Thanks for the references. I'll rework it to stage the ServerboundCookieResponsePackets the same way Limbo already buffers ClientSettings/brand, carry them forward in the handler, expose a getter and replay them in LoginTasksQueue when connecting to the backend, instead of firing CookieReceiveEvent directly in the Limbo handler.

SPYmesu added 2 commits June 10, 2026 19:38
A player in a Limbo usually isn't connected to a backend yet, so the cookie
response can't be handled right there. Store the packets like we already do
for client settings and brand, then replay them in LoginTasksQueue when the
player is sent to a server, so the cookie event fires at the right time.
Lets a Limbo handler read the cookie value while the player is still in the
Limbo, not only later through CookieReceiveEvent. handle() now also calls
onCookieResponse with the key and data when the client replies.
@SPYmesu

SPYmesu commented Jun 10, 2026

Copy link
Copy Markdown
Author

So I need this because I check for cookies while player is on auth server (fake server), this helps to enable trusted devices function of security plugin.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants