|  | 
|  | 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