Repository navigation
feat(modules): load/initialize split and guarded module commands - #3
Merged
Merged
Conversation
…alizeLoaded A host can now load module jars, act on the loaded modules (for example, let them declare commands during the server's registration window), and initialize them later. start() keeps its behaviour as load() followed by initializeLoaded(). stop() closes modules that were loaded but never initialized, without calling shutdown. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011DvdDwRoC7qHZ7yKUv1ufw
Paper only accepts commands during LifecycleEvents.COMMANDS, which fires before modules initialize, and cannot unregister them. A module that implements CommandDeclaringModule now declares its commands through ModuleLifecycleManager#declareCommands, between load() and initializeLoaded(), using a ModuleCommandRoot (subcommands under the host's root) and a ModuleCommandRegistrar (top-level commands). CommandGuard rebuilds each tree so executors and suggestion providers run module code only while the module is active; otherwise the host's ModuleUnavailableHandler replies. A failed start therefore leaves no command to clean up. Redirects are rejected because they would bypass the guard. Declared trees are kept and replayed when the commands event fires again on /minecraft:reload. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011DvdDwRoC7qHZ7yKUv1ufw
Every module can declare commands by overriding PluginModule#declareCommands, which does nothing by default. Removes the separate CommandDeclaringModule interface. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011DvdDwRoC7qHZ7yKUv1ufw
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.
Summary
Load, then initialize
ModuleLifecycleManager.load()loads module jars and creates the modules without callinginitialize.initializeLoaded()then initializes them.start()isload()+initializeLoaded(), unchanged in behaviour.stop()also closes loaded-but-never-initialized modules, withoutshutdown.Guarded module commands
PluginModule.declareCommands(ModuleCommandRoot root, ModuleCommandRegistrar registrar), a default no-op.root.then(...)adds a subcommand under the host's root, andregistrar.register(...)adds a top-level command.declareCommands(commands, root, unavailable)from itsLifecycleEvents.COMMANDShandler, betweenload()andinitializeLoaded(), every time the event fires.CommandGuardrebuilds every tree, so executors and suggestions run module code only while the module is active. Otherwise the host'sModuleUnavailableHandlerreplies, and tab completion is empty.declareCommandsthrows, nothing it declared is registered and the module is unloaded./minecraft:reload; the module isn't asked to declare a second time.PluginModule#initializeJavadoc now say that commands are declared, never unregistered, and need no cleanup after a failed start.Why
Paper only accepts commands during its lifecycle commands event, before modules initialize, and its API can't remove them. Modules need their own commands, and a module that fails to start must not run with uninitialized state.
Test plan
./gradlew buildModuleLifecycleManagerTest: 16/16, 5 of them new, for the load/initialize split.ModuleCommandDeclarationTest: 10/10, all new. Covers: runs when active; unavailable before initialize, after a failed initialize and after stop; nested executors guarded; suggestions guarded; top-level commands registered guarded; a failed declaration registers nothing and isn't initialized; redirects rejected; a later commands event replays without redeclaring.🤖 Generated with Claude Code
https://claude.ai/code/session_011DvdDwRoC7qHZ7yKUv1ufw