|
| 1 | +--- |
| 2 | +alwaysApply: false |
| 3 | +description: Connect Java injection + Netty event-loop compatibility playbook across Velocity/Bungee/Spigot |
| 4 | +--- |
| 5 | + |
| 6 | +# Connect Java Injection and Netty Event-Loop Playbook |
| 7 | + |
| 8 | +This rule captures our working patterns and pitfalls for platform injection (Velocity, Bungee, Spigot/Paper), Netty compatibility, and how we leverage Floodgate/Geyser learnings. Use this when touching injection code, LocalServerChannel/LocalChannel plumbing, or upgrading Netty/platforms. |
| 9 | + |
| 10 | +## Overview |
| 11 | + |
| 12 | +Minekube Connect is a global proxy network plugin forked from Floodgate that incorporates Geyser's logic. It allows Minecraft servers to be accessible worldwide through edge proxies using local channels and tunneling. |
| 13 | + |
| 14 | +## Core Concepts |
| 15 | + |
| 16 | +- Local server loopback is implemented with `LocalServerChannel`/`LocalChannel` wrappers: |
| 17 | + - [LocalServerChannelWrapper.java](mdc:core/src/main/java/com/minekube/connect/network/netty/LocalServerChannelWrapper.java) |
| 18 | + - [LocalChannelWrapper.java](mdc:core/src/main/java/com/minekube/connect/network/netty/LocalChannelWrapper.java) |
| 19 | + - Session orchestration: [LocalSession.java](mdc:core/src/main/java/com/minekube/connect/network/netty/LocalSession.java) |
| 20 | +- Injection entry points per platform: |
| 21 | + - Velocity: [VelocityInjector.java](mdc:velocity/src/main/java/com/minekube/connect/inject/velocity/VelocityInjector.java) |
| 22 | + - Bungee: [BungeeInjector.java](mdc:bungee/src/main/java/com/minekube/connect/inject/bungee/BungeeInjector.java) |
| 23 | + - Spigot/Paper: data injection/handshake tweaks in [SpigotDataHandler.java](mdc:spigot/src/main/java/com/minekube/connect/addon/data/SpigotDataHandler.java) |
| 24 | + |
| 25 | +## Netty Compatibility |
| 26 | + |
| 27 | +- LocalServerChannel is not compatible with native KQueue/Epoll handlers. Use LocalIoHandler-backed event loops. |
| 28 | +- Align Netty with platform when needed. Velocity uses Netty 4.2.x. Our repo tracks the version in [Versions.kt](mdc:build-logic/src/main/kotlin/Versions.kt). If you require `MultiThreadIoEventLoopGroup(LocalIoHandler.newFactory())`, ensure Netty is recent enough. |
| 29 | + |
| 30 | +## Velocity (Working Pattern) |
| 31 | + |
| 32 | +- Validate event loop type (KQueue/Epoll/NIO). Use LocalIoHandler-backed groups for LocalServerChannel. |
| 33 | +- Proven approach (inspired by Geyser): |
| 34 | + - Create `MultiThreadIoEventLoopGroup(LocalIoHandler.newFactory())` for LocalServerChannel bootstrap. |
| 35 | + - Delegate non-local channels to Velocity's native event loops when necessary (see Geyser's WatchedSingleThreadIoEventLoop pattern). Adjust to our Netty version constraints. |
| 36 | +- Keep injection into Velocity's channel initializers intact; do not replace core semantics. |
| 37 | + |
| 38 | +## Bungee (Working Pattern) |
| 39 | + |
| 40 | +- Symptom fixed: `IllegalStateException: channel not registered to an event loop` during upstream connect/close. |
| 41 | +- Root cause: `LocalSession` created its own `DefaultEventLoopGroup`, so its channels weren't registered with Bungee's event loops. |
| 42 | +- Fix (minimal and robust): |
| 43 | + 1. In [BungeeInjector.java](mdc:bungee/src/main/java/com/minekube/connect/inject/bungee/BungeeInjector.java), build a wrapper worker group: |
| 44 | + - Base group: Bungee/Waterfall worker `MultiThreadIoEventLoopGroup`. |
| 45 | + - Use `SingleThreadIoEventLoop(finalWorkerGroup, wrapperFactory)` where `wrapperFactory` is an IoHandler that delegates: |
| 46 | + - Local channels → LocalIoHandler |
| 47 | + - Native channels → NIO handler |
| 48 | + 2. Implement wrapper IoHandler: [ConnectIoHandlerWrapper.java](mdc:bungee/src/main/java/com/minekube/connect/inject/bungee/ConnectIoHandlerWrapper.java). |
| 49 | + 3. Share the platform event loop group with `LocalSession`: |
| 50 | + - Add `LocalSession.setPlatformEventLoopGroup(...)` and call it from Bungee injector before establishing the local channel. |
| 51 | + - `LocalSession` now uses the platform group if set; otherwise falls back to the default. |
| 52 | + |
| 53 | +This keeps LocalServerChannel compatible (LocalIoHandler) while ensuring all channels are managed by Bungee's event loops. |
| 54 | + |
| 55 | +## Spigot/Paper (Handshake Mutation on Newer Versions/Java 21) |
| 56 | + |
| 57 | +- Do not reflectively mutate final fields on modern Java/Paper (e.g., handshake hostname). Instead, construct a new handshake packet for 1.20.2+ using `HANDSHAKE_PACKET_CONSTRUCTOR` and existing fields in [ClassNames.java](mdc:spigot/src/main/java/com/minekube/connect/util/ClassNames.java). |
| 58 | +- Implementation lives in [SpigotDataHandler.java](mdc:spigot/src/main/java/com/minekube/connect/addon/data/SpigotDataHandler.java); mirrors Floodgate's approach for constructing new packets instead of bypassing final. |
| 59 | + |
| 60 | +## Diagnostics and Playbook |
| 61 | + |
| 62 | +1. Clone upstreams into `./tmp` to study internals: |
| 63 | + - Geyser: `tmp/Geyser` (Velocity/Bungee injectors and Netty helpers) |
| 64 | + - Floodgate: `tmp/Floodgate` (initializer wrapping and Spigot handshake strategies) |
| 65 | + - Velocity: `tmp/Velocity` |
| 66 | + - BungeeCord: `tmp/BungeeCord` |
| 67 | +2. Identify event loop types from logs (kqueue/epoll/nio). If LocalServerChannel present, ensure LocalIoHandler is used for that channel's loops. |
| 68 | +3. If you see: |
| 69 | + - `IoHandle ... not supported`: LocalServerChannel is being registered on a non-Local handler (fix with LocalIoHandler-backed event loop). |
| 70 | + - `channel not registered to an event loop`: your local channels are not on the platform's loop. Share platform ELG with `LocalSession.setPlatformEventLoopGroup(...)` (Bungee fix) or adopt a wrapper loop that delegates appropriately. |
| 71 | + - Phase errors (e.g., LoginSuccess in CONFIGURATION): often caused by event loop/channel mismatch or pipeline ordering; reassess Local vs native handler usage and initializer placement. |
| 72 | + |
| 73 | +## Architectural Principles |
| 74 | + |
| 75 | +- Maintain platform ownership of event loops. Connect should integrate with, not replace, a platform's loop model. |
| 76 | +- Only use LocalIoHandler for LocalServerChannel/LocalChannel compatibility; delegate everything else to the platform-native handlers. |
| 77 | +- Keep `LocalSession` platform-agnostic but allow platforms to inject their event loop group via a single optional hook. |
| 78 | +- Mirror Floodgate/Geyser patterns when possible; they've solved many injection edge cases. |
| 79 | + |
| 80 | +## Changes to Keep Minimal |
| 81 | + |
| 82 | +- Only add: |
| 83 | + - `LocalSession.setPlatformEventLoopGroup(...)` and selection in `connect()`. |
| 84 | + - Platform-specific wiring (Bungee) to set the platform ELG and wrapper IoHandler. |
| 85 | +- Avoid broad refactors; do not introduce separate, long-lived event loops per platform unless necessary. |
| 86 | + |
| 87 | +## Release/Verification Checklist |
| 88 | + |
| 89 | +- Build all modules: `./gradlew build` |
| 90 | +- Smoke tests: |
| 91 | + - Velocity: injects and accepts join through Connect; ensure no loop incompatibilities. |
| 92 | + - Bungee: no `channel not registered` or `IoHandle not supported`; join succeeds. |
| 93 | + - Spigot/Paper: no IllegalAccessException on handshake; new-packet path for 1.20.2+ works. |
| 94 | + |
| 95 | +## Commit Guidance |
| 96 | + |
| 97 | +- Title: "platform: fix LocalServerChannel/LocalSession compatibility via IoHandler delegation and platform ELG sharing" |
| 98 | +- Body: |
| 99 | + - Explain IoHandler wrapper delegation (Local vs native). |
| 100 | + - Note `LocalSession.setPlatformEventLoopGroup(...)` and platform usage (Bungee). |
| 101 | + - Reference files: [BungeeInjector.java](mdc:bungee/src/main/java/com/minekube/connect/inject/bungee/BungeeInjector.java), [ConnectIoHandlerWrapper.java](mdc:bungee/src/main/java/com/minekube/connect/inject/bungee/ConnectIoHandlerWrapper.java), [LocalSession.java](mdc:core/src/main/java/com/minekube/connect/network/netty/LocalSession.java). |
0 commit comments