layer-groupModule Integration

PhantomDungeons is built around a first-class module system. Every built-in feature, such as economy, enchants, wands, withdraw, crystals, has its own PhantomModule, with no special internal privileges over modules you write yourself.

The same API is open to external plugins. You can write a module that lives inside your own .jar but runs inside the PhantomDungeons plugin lifecycle:

This mirrors patterns used by frameworks like PaperMC's lifecycle APIarrow-up-right and is conceptually similar to how Guice modulesarrow-up-right or Spring @Configurationarrow-up-right scope and do their dependencies, if you've used those, it will feel familiar.


How the System Works

DungeonManager (constructor)
  └─ ModuleRegistry.register(module)     <- your module registered here

DungeonManager.initialize()
  └─ ModuleRegistry (topological sort by dependencies + priority)
       └─ for each module in order:
            module.onEnable(ModuleContext)
              └─ checkConfigEnabled()    <- reads modules.yml
              └─ enable()               <- YOUR code runs here
            register module's Listeners

/dungeonsadmin reload
  └─ ModuleRegistry.reloadAll()
       └─ module.onReload(ModuleContext)reload()

plugin disable
  └─ ModuleRegistry.shutdownAll() (reverse order)
       └─ module.onDisable(ModuleContext)disable()

The ModuleContext passed into every lifecycle method gives you access to:

Field
Type
What it is

plugin

JavaPlugin

The PhantomDungeons plugin instance

configManager

ConfigurationManager

Loads/caches YAML files from the data folder

moduleRegistry

ModuleRegistry

Retrieve sibling modules by class or ID

playerRepository

PlayerRepository

Async database access for player data, this is the "cache" layer and NOT the database layer.

playerManager

PlayerManager

In-memory online player state


Quick Start

Step 1 - Extend AbstractPhantomModule

Always extend AbstractPhantomModule rather than implementing PhantomModule directly. It handles the boilerplate (context assignment, logger setup, enabled flag, isEnabled()) and exposes clean enable() / reload() / disable() hooks.

Step 2 - Register it with the ModuleRegistry

Get the registry from DungeonManager and call register() before DungeonManager.initialize() runs. The right place is your plugin's onEnable(), immediately after PhantomDungeons is confirmed to be loaded.

Declare the hard dependency in plugin.yml so Bukkit guarantees load order:

That's it, your module now runs inside the PhantomDungeons lifecycle.


Full Module Reference

AbstractPhantomModule fields available inside enable() / reload() / disable()

Field
Type
Description

plugin

JavaPlugin

The PhantomDungeons plugin instance.

context

ModuleContext

Full lifecycle context (see table above).

logger

Logger

Pre-prefixed logger: [MyModule] …

enabled

boolean

Set by checkConfigEnabled(). Read before doing real work.

Lifecycle methods

Method
When called
Notes

checkConfigEnabled()

Before enable(), always

Override to set enabled = false if modules.yml says off.

enable()

Once on startup if enabled

Throw ModuleInitException for critical failures.

reload()

On /pd reload

Flush caches, re-read config.

disable()

On plugin shutdown

Cancel tasks, release resources, clear API singletons.

getListeners()

After enable()

Return Bukkit listeners; they are auto-registered.

Logging helpers (use these instead of Bukkit.getLogger())


Common Patterns

Toggle with modules.yml

Follow the same pattern all built-in sub-modules use, the read the flag in checkConfigEnabled() so the registry skips enable() automatically:

Add the corresponding key to your copy of modules.yml (or instruct server admins to add it):

Declare dependencies

If your module depends on, say, EconomyModule and ZoneModule, declare their IDs. The registry performs a topological sort ,your enable() is guaranteed to run after both dependencies are ready:

Control initialisation priority

Among modules with no unsatisfied dependencies, lower priority numbers initialise first (default is 100):

Access a sibling module

Or use the static helper from AbstractPhantomModule:

All built-in modules (Economy, Wands, Withdraw, Leveling) expose a typed API via a singleton provider. Reproduce the same three-file pattern:

Then hook it up inside the module:

External callers then use:


Example


Throwing ModuleInitException

Use ModuleInitException to signal that your module cannot start. By default the exception is critical, meaning PhantomDungeons will log the error and continue without your module (it will not crash the whole server). Pass false as the third argument only if failure should abort startup entirely.


Accessing PhantomDungeons APIs from Your Module

Once inside enable(), context exposes everything you need without touching PhantomDungeons.getInstance():


Last updated