Skip to content

fix(cloudnet): load the bridge as a Minestom extension in game and setup - #148

Merged
theEvilReaper merged 2 commits into
mainfrom
feature/cloudnet-extension-bootstrap
Aug 4, 2026
Merged

fix(cloudnet): load the bridge as a Minestom extension in game and setup#148
theEvilReaper merged 2 commits into
mainfrom
feature/cloudnet-extension-bootstrap

Conversation

@TheMeinerLP

@TheMeinerLP TheMeinerLP commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

What was broken

Game loaded CloudNet's MinestomBridgeExtension directly through Aerogel DI, inside a try-with-resources on InjectionLayer.ext(). That layer is an UncloseableInjectionLayer whose close() always throws UnsupportedOperationException, so the catch (Throwable) logged Failed to initialize CloudNet MinestomBridgeExtension (standalone dev mode?) on every start — even when initialization had succeeded — and onDisable() was never reached. On top of that the full CloudNet set (driver-impl, bridge-impl, wrapper-jvm-api, platform-inject) was declared implementation and shaded into cygnus.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 inspects player.getPermissionLevel() > 0. On a LuckPerms-managed server that is always 0, so maintenance bypass and task-level requiredPermission checks would have rejected every player, staff included.

What changes

Extension-based loading (mirroring Titan)

  • game and setup boot through ExtensionBootstrap.init() / bootstrap.start(host, port) and load extensions from extensions/. Running standalone simply loads none.
  • No CloudNet artifact is bundled any more: cygnus.jar and setup.jar contain zero eu/cloudnetservice classes.
  • New :bridge module — a thin extension jar (~2 KB) with extension.json (dependencies: ["CloudNet_Bridge"], version stamped by processResources), published as cygnus-bridge.

Permissions through Adventure

  • New PermissionAwarePlayer in common installs Adventure's PermissionChecker.POINTER and resolves it through LuckPerms (including QueryOptions from the context manager). Neither Minestom nor LuckPerms ever installs that pointer — LuckPerms' own MinestomSenderFactory only reads it — so without it every permission check silently resolves to false.
  • InstanceSwitchChunkPlayer now extends it, so CygnusPlayer and SetupPlayer both get the pointer from one implementation.
  • The :bridge extension registers a MinestomPermissionChecker reading that same pointer as the CloudNet registry default.
  • /stop resolves its permission through the pointer instead of calling LuckPermsProvider directly.

Smaller fixes

  • setup's shadowJar was missing mergeServiceFiles().
  • Both fat jars now strip jar signatures and module-info (signed and multi-release jars break a relocation-free fat jar).
  • Guava used to arrive transitively through CloudNet and is now bundled explicitly (LuckPerms needs it unrelocated).

Testing

./gradlew build passes (common/game/setup, all tests). Verified on the built artifacts: no eu/cloudnetservice classes in either fat jar, the 43 META-INF/services entries still present, extension.json stamped with 2.6.6.

Both services were also booted standalone, without CloudNet anywhere:

  • cygnus.jar came up on -Dservice.bind.host=127.0.0.1 -Dservice.bind.port=30566, was confirmed listening on that port, and stop written 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.jar behaved the same way. Note it needs a lobby map with a region/ folder to start at all, which is unrelated to this PR.
  • With bridge.jar dropped into extensions/ but no CloudNet_Bridge present, the extension manager logs CygnusCloudNetPermissions requires an extension called CloudNet_Bridge ... will not be loaded and 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 PermissionAwarePlayer supplying the pointer that LuckPerms' MinestomSenderFactory reads.

Follow-up (outside this repo)

The CloudNet template needs an extensions/ directory containing CloudNet_Bridge and bridge.jar — without it the LuckPerms-backed permission checker is not registered.

🤖 Generated with Claude Code

https://claude.ai/code/session_01GsZ8rsCxv99T6SfC1zBpip

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
@github-actions

github-actions Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Test results

147 files  147 suites   1m 17s ⏱️
148 tests 144 ✅  4 💤 0 ❌
447 runs  435 ✅ 12 💤 0 ❌

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
@theEvilReaper
theEvilReaper merged commit 07e2f8c into main Aug 4, 2026
7 checks passed
@theEvilReaper
theEvilReaper deleted the feature/cloudnet-extension-bootstrap branch August 4, 2026 12:24
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