Skip to content

fix: Modern Forge (FML2/FML3) proxy support and LoginPluginMessage codec#676

Closed
robinbraemer wants to merge 3 commits into
masterfrom
fix/forge-fml2-fml3-support
Closed

fix: Modern Forge (FML2/FML3) proxy support and LoginPluginMessage codec#676
robinbraemer wants to merge 3 commits into
masterfrom
fix/forge-fml2-fml3-support

Conversation

@robinbraemer
Copy link
Copy Markdown
Member

Summary

  • Fix LoginPluginMessage to use raw bytes instead of length-prefixed encoding, matching the Minecraft protocol spec (and LoginPluginResponse which was already correct). This also fixes Velocity forwarding version negotiation silently falling back to v1.
  • Add FML2/FML3 marker detection in handshake so Forge 1.13–1.20.1 clients are identified as ModernForge instead of Vanilla
  • Preserve FML2/FML3 tokens in ModernToken() when constructing the backend handshake address
  • Add BungeeForge extraData property to legacy and BungeeGuard forwarding so backend servers with BungeeForge can identify Forge clients
  • Add omitempty to Property.Signature JSON tag

Test plan

  • Unit tests confirm each bug: TestLoginPluginMessage_WireFormat, TestModernToken, TestHandshakeConnectionType all fail on old code, pass on fix
  • E2E test (TestForgeE2E_FML3ClientToBackend) simulates full flow: handshake detection → token preservation → forwarding address with extraDataLoginPluginMessage raw decode — fails on old code, passes on fix
  • TestForgeE2E_VanillaClientUnchanged confirms vanilla clients are unaffected
  • Existing TestPackets roundtrip suite passes

Closes #613

Fix several bugs preventing Forge 1.13-1.20.1 clients from connecting
through Gate to modded backend servers:

- Fix LoginPluginMessage to use raw bytes instead of length-prefixed
  encoding, matching the Minecraft protocol spec and LoginPluginResponse.
  This also fixes Velocity forwarding version negotiation which silently
  fell back to v1.
- Add FML2/FML3 marker detection in handshake connection type. Forge
  1.13-1.20.1 clients use these tokens instead of FORGE.
- Preserve FML2/FML3 tokens in ModernToken() when connecting to backend.
- Add BungeeForge extraData property to legacy and BungeeGuard forwarding
  so backend servers with BungeeForge can identify Forge clients.
- Add omitempty to Property.Signature JSON tag to avoid empty signatures
  in forwarded properties.

Closes #613
Add end-to-end tests that simulate the full Forge client connection
flow: handshake detection → backend token preservation → legacy
forwarding with BungeeForge extraData → LoginPluginMessage decoding.

These tests confirm that all bugs from #613 are caught (verified by
running against pre-fix code where they all fail) and fixed.
@cloudflare-workers-and-pages
Copy link
Copy Markdown

cloudflare-workers-and-pages Bot commented Mar 10, 2026

Deploying gate-minekube with  Cloudflare Pages  Cloudflare Pages

Latest commit: dbfe673
Status: ✅  Deploy successful!
Preview URL: https://9254b570.gate-minekube.pages.dev
Branch Preview URL: https://fix-forge-fml2-fml3-support.gate-minekube.pages.dev

View logs

…ctions

Verifies actual bytes on the wire using Gate's codec over real TCP:
- Backend receives correct FML3 token and extraData in forwarded handshake
- LoginPluginMessage uses raw bytes (not length-prefixed) on the wire
@robinbraemer
Copy link
Copy Markdown
Member Author

Closing — the fixes and tests are already merged to master (commits 2d602a2 and 9014a15). The remaining commit (network-level e2e test) is redundant with the already-merged test coverage.

@robinbraemer robinbraemer deleted the fix/forge-fml2-fml3-support branch March 10, 2026 21:47
@robinbraemer
Copy link
Copy Markdown
Member Author

Follow-up: Forge 1.20.1 + Velocity Modern Forwarding

A user reported that Forge 1.20.1 still doesn't work with Gate when using Velocity Modern Forwarding. After investigating both Gate and upstream Velocity, here's what we found:

The Error

Gate logs:

player disconnected from server while connecting
  reason: {multiplayer.disconnect.unexpected_query_response}

Forge server logs:

Received empty payload on channel fml:handshake

Root Cause

During the backend LOGIN phase, the Forge server sends fml:handshake LoginPluginMessages for mod negotiation. Gate (in session_backend_login.go) only understands velocity:player_info — everything else gets a LoginPluginResponse{Success: false}. The Forge backend interprets this rejection as a protocol failure and kicks the player.

Velocity Behaves Identically

After cloning and auditing Velocity's source (LoginSessionHandler.java), we confirmed that upstream Velocity has the exact same behavior:

  • Only handles velocity:player_info during backend login
  • All other LoginPluginMessages (including fml:handshake) get Success=false
  • No FML-specific handling in the backend login flow
  • No delayed ServerLoginSuccess mechanism

Velocity even explicitly classifies Forge 1.13–1.20.1 clients as VANILLA with a TODO comment:

// Note for future implementation: Forge 1.13+ identifies itself
// using a slightly different hostname token.
return ConnectionTypes.VANILLA;

Forge 1.13–1.20.1 + Velocity Modern Forwarding is an unsupported combination in both Gate and upstream Velocity.

Why It's Architecturally Hard

For Forge 1.20.2+, mod negotiation happens in the CONFIG phase (after LOGIN), which proxies can transparently forward. This works today.

For Forge 1.13–1.20.1, mod negotiation happens via LoginPluginMessages during LOGIN. By the time the proxy connects to the backend, the client has already completed login with the proxy and is no longer in the LOGIN state — so the proxy can't forward these messages back to the client.

What Works Today (with the merged fixes)

Setup Status
Forge 1.20.2+ with any forwarding mode ✅ Works (FML uses CONFIG phase)
Forge 1.13–1.20.1 with BungeeCord forwarding ✅ Works (mod list embedded in handshake)
Forge 1.13–1.20.1 with Velocity Modern Forwarding ❌ Unsupported (same as upstream Velocity)

Workarounds

  1. Switch to BungeeCord/BungeeGuard forwarding — configure both Gate and the backend for legacy forwarding
  2. Upgrade to Forge 1.20.2+ — where FML uses the CONFIG phase and works natively
  3. Use a ServerLoginPluginMessageEvent subscriber — Gate fires this event for unrecognized LoginPluginMessages, allowing a plugin to intercept fml:handshake and provide custom responses (complex, same escape hatch Velocity offers)

@robinbraemer
Copy link
Copy Markdown
Member Author

This has been resolved in v0.64.0 🎉

Gate now has built-in support for Forge 1.13–1.20.1 with Velocity modern forwarding — no Ambassador-like plugin needed.

Setup guide: https://gate.minekube.com/guide/modded-servers#forge-1-13-1-20-1-server-setup
Release: https://github.com/minekube/gate/releases/tag/v0.64.0
PR: #680

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.

1 participant