fix(cloudnet): load the bridge as a Minestom extension in game and setup - #148
Merged
Merged
Conversation
The game loaded CloudNet's MinestomBridgeExtension directly via Aerogel DI inside a try-with-resources on InjectionLayer.ext(). That layer is an UncloseableInjectionLayer whose close() always throws, so the catch block logged "Failed to initialize CloudNet MinestomBridgeExtension" on every single start - even when it had succeeded - and onDisable() was never reached. The setup service had no CloudNet integration at all beyond the bind address. Both services now bootstrap minestom-ce-extensions and load the bridge from extensions/ like Titan does, so no CloudNet artifact is bundled any more: the wrapper provides the driver at runtime and the bridge arrives as an extension. cygnus.jar and setup.jar contain zero eu/cloudnetservice classes now. Permissions went through CloudNet's default checker, which only inspects player.getPermissionLevel(). That is always 0 on a LuckPerms-managed server, so maintenance bypass and task-level requiredPermission checks rejected everyone, staff included. Players now carry Adventure's PermissionChecker pointer backed by LuckPerms (PermissionAwarePlayer, the shared base of CygnusPlayer and SetupPlayer), which is the pointer LuckPerms' own sender factory reads. The new :bridge extension registers a checker reading that same pointer as the CloudNet registry default, and /stop resolves its permission through it instead of calling LuckPerms directly. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GsZ8rsCxv99T6SfC1zBpip
Contributor
Test results147 files 147 suites 1m 17s ⏱️ Results for commit dee2d65. ♻️ This comment has been updated with latest results. |
Covers the artifacts, how the node launches a service, the service directory layout for both services, why both extension jars are required, and how to verify a deployment. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GsZ8rsCxv99T6SfC1zBpip
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What was broken
Game loaded CloudNet's
MinestomBridgeExtensiondirectly through Aerogel DI, inside atry-with-resourcesonInjectionLayer.ext(). That layer is anUncloseableInjectionLayerwhoseclose()always throwsUnsupportedOperationException, so thecatch (Throwable)loggedFailed to initialize CloudNet MinestomBridgeExtension (standalone dev mode?)on every start — even when initialization had succeeded — andonDisable()was never reached. On top of that the full CloudNet set (driver-impl,bridge-impl,wrapper-jvm-api,platform-inject) was declaredimplementationand shaded intocygnus.jar, although the CloudNet wrapper provides the driver at runtime itself.Setup had no CloudNet integration at all beyond the bind address.
Both: permission checks went through CloudNet's default
MinestomPermissionChecker, which only inspectsplayer.getPermissionLevel() > 0. On a LuckPerms-managed server that is always0, so maintenance bypass and task-levelrequiredPermissionchecks would have rejected every player, staff included.What changes
Extension-based loading (mirroring Titan)
gameandsetupboot throughExtensionBootstrap.init()/bootstrap.start(host, port)and load extensions fromextensions/. Running standalone simply loads none.cygnus.jarandsetup.jarcontain zeroeu/cloudnetserviceclasses.:bridgemodule — a thin extension jar (~2 KB) withextension.json(dependencies: ["CloudNet_Bridge"], version stamped byprocessResources), published ascygnus-bridge.Permissions through Adventure
PermissionAwarePlayerincommoninstalls Adventure'sPermissionChecker.POINTERand resolves it through LuckPerms (includingQueryOptionsfrom the context manager). Neither Minestom nor LuckPerms ever installs that pointer — LuckPerms' ownMinestomSenderFactoryonly reads it — so without it every permission check silently resolves tofalse.InstanceSwitchChunkPlayernow extends it, soCygnusPlayerandSetupPlayerboth get the pointer from one implementation.:bridgeextension registers aMinestomPermissionCheckerreading that same pointer as the CloudNet registry default./stopresolves its permission through the pointer instead of callingLuckPermsProviderdirectly.Smaller fixes
setup'sshadowJarwas missingmergeServiceFiles().module-info(signed and multi-release jars break a relocation-free fat jar).Testing
./gradlew buildpasses (common/game/setup, all tests). Verified on the built artifacts: noeu/cloudnetserviceclasses in either fat jar, the 43META-INF/servicesentries still present,extension.jsonstamped with2.6.6.Both services were also booted standalone, without CloudNet anywhere:
cygnus.jarcame up on-Dservice.bind.host=127.0.0.1 -Dservice.bind.port=30566, was confirmed listening on that port, andstopwritten to its stdin (exactly what a CloudNet node does) shut it down cleanly — Minestom, the Falco anvil loader and LuckPerms all closed in order and the process exited on its own.setup.jarbehaved the same way. Note it needs a lobby map with aregion/folder to start at all, which is unrelated to this PR.bridge.jardropped intoextensions/but noCloudNet_Bridgepresent, the extension manager logsCygnusCloudNetPermissions requires an extension called CloudNet_Bridge ... will not be loadedand the server starts normally. Local development is unaffected by the new module.What is not covered by a live test is the permission pointer itself, since that needs a connected client plus a CloudNet node; it rests on
PermissionAwarePlayersupplying the pointer that LuckPerms'MinestomSenderFactoryreads.Follow-up (outside this repo)
The CloudNet template needs an
extensions/directory containingCloudNet_Bridgeandbridge.jar— without it the LuckPerms-backed permission checker is not registered.🤖 Generated with Claude Code
https://claude.ai/code/session_01GsZ8rsCxv99T6SfC1zBpip