EasyLibrary modules are plugin-owned runtime units discovered from
module.yml or module.yaml manifests. They allow a plugin to expose
independent features with:
- ordered module loading based on dependencies.
- isolated module lifecycle management.
- shared services and capability providers.
- module-scoped configuration and data.
- automatic cleanup of runtime resources.
- diagnostics and runtime state reporting.
When PocketMine disables an owner plugin, EasyLibrary disables modules owned by that plugin and does not try to enable pending modules during the same disable event. PocketMine can still report the owner plugin as enabled while its disable callback is running, so skipping that pending pass avoids accidental re-enable attempts and misleading enabled lifecycle logs during server shutdown.
- EasyLibrary 2.0.0+
- PocketMine-MP API 5.0.0+
- PHP 8.2+
The owner plugin must depend on EasyLibrary:
depend:
- EasyLibraryAdd easylibrary-modules to the owner plugin's plugin.yml:
easylibrary-modules:
- path: modules
namespace: vendor\plugin\modulespathis relative to the plugin root unless it is absolute.namespaceis optional when each manifest provides its own namespace or whenloaderis a fully qualified class name.
A single path may be declared directly:
easylibrary-modules: modulesYou can declare multiple sources:
easylibrary-modules:
- path: modules
namespace: vendor\plugin\modules
- path: custom-modules
namespace: vendor\plugin\customMyPlugin/
|-- plugin.yml
|-- modules/
| `-- farm/
| |-- module.yml
| |-- FarmModule.php
| |-- command/
| |-- listener/
| `-- service/
`-- src/
`-- vendor/plugin/Main.phpEach module is described by a YAML manifest.
id: vendor:farm
version: 1.0.0
loader: FarmModule
namespace: vendor\plugin\modules\farmid: vendor:farm
name: Farm System
version: 1.0.0
api-version: 1
loader: FarmModule
namespace: vendor\plugin\modules\farm
load: POSTWORLD
enabled: true
dependencies:
vendor:economy: ">=1.0.0 <2.0.0"
soft-dependencies:
- vendor:ranking
plugin-dependencies:
- EconomyAPI
load-before:
- vendor:farm-ui
provides:
services:
- vendor:farm-service
capabilities:
- vendor.farm
requires:
services:
- vendor:economy-service
capabilities:
- economy| Field | Purpose |
|---|---|
id |
Stable module identifier |
version |
Module version used for dependency checks |
loader |
Module loader class name |
name |
Human-readable module name |
namespace |
Namespace prefix for loader class resolution |
load |
STARTUP or POSTWORLD |
enabled |
Initial enabled state |
api-version |
Metadata describing the module API |
dependencies |
Required module dependencies |
soft-dependencies |
Optional modules that affect load order |
plugin-dependencies |
Required PocketMine plugins |
load-before |
Modules that should load after this one |
provides |
Services and capabilities published by the module |
requires |
Services and capabilities required by the module |
- If
namespaceis defined inplugin.yml,loader: FarmModuleis resolved relative to that namespace. - If
loaderis a fully qualified class name,namespaceis not required. - The module class must extend
imperazim\module\BaseModule.
Most modules extend BaseModule:
use imperazim\module\BaseModule;
use imperazim\module\ModuleManager;
final class FarmModule extends BaseModule {
protected function onEnable(ModuleManager $manager): void {
$this->logger()->info('Farm module enabled.');
}
protected function onDisable(ModuleManager $manager): void {
$this->logger()->info('Farm module disabled.');
}
}- module metadata and dependency information.
- module-scoped logging.
- access to the owning plugin and module manager.
- module-specific data folder and YAML config support.
- helpers for commands, listeners, events, tasks, async jobs, and cleanup.
- service and capability helpers.
- access to other module APIs.
- health reporting.
While the module is enabled, the runtime context provides:
$context = $this->context();
$plugin = $context->getPlugin();
$manager = $context->getManager();
$services = $context->getServices();
$logger = $context->getLogger();
$folder = $context->getDataFolder();
$config = $context->getConfig('config.yml', [
'enabled' => true,
]);getDataFolder()returnsplugin_data/<OwnerPlugin>/modules/<module-id>/.getPath($relativePath)resolves paths inside the module storage.getConfig($file, $defaults)loads YAML config for the module.
Register runtime resources with module helpers:
$this->addCommand(FarmCommand::class);
$this->addListener(FarmListener::class);
$this->addEvent(PlayerJoinEvent::class, function(PlayerJoinEvent $event): void {
// Handle the event.
});
$this->scheduleDelayedTask(new FarmTask(), 20);
$this->scheduleRepeatingTask(new FarmTask(), 20);
$this->submitAsyncJob(new FarmAsyncJob(), function($result, $job): void {
// Handle job completion.
});
$this->addCleanup(function(): void {
// Release custom state.
});EasyLibrary tracks and cleans module-owned resources automatically:
- commands.
- listener instances.
- event callbacks.
- scheduled tasks.
- async jobs and completion callbacks.
- plugin components.
- manual cleanup callbacks.
Resources are removed when a module is disabled, reloaded, or forgotten.
Services are shared objects published by one module and consumed by others.
$this->provideService(
'vendor:farm-service',
new FarmService(),
FarmServiceInterface::class,
'1.0.0'
);/** @var FarmServiceInterface $service */
$service = $this->getTypedService(
'vendor:farm-service',
FarmServiceInterface::class
);$service = $this->getOptionalService('vendor:optional-service');provides:
services:
- vendor:farm-service
requires:
services:
- vendor:economy-serviceServices are resolved at runtime, and required services are checked before
onEnable().
Capabilities let a module depend on a feature rather than a specific provider.
provides:
capabilities:
- economyrequires:
capabilities:
- economy$provider = $this->getCapabilityProvider('economy');If multiple enabled modules provide the same capability, EasyLibrary can use a
preferred provider setting. Use /easymodule provider to inspect or change it.
A module can expose an API object by overriding getApi().
public function getApi(): object {
return new FarmApi($this);
}Consumers can obtain it by ID:
$api = $this->getApi('vendor:farm');
$optionalApi = $this->getOptionalApi('vendor:ranking');Override getHealth() to report module diagnostics:
use imperazim\module\health\ModuleHealthReport;
public function getHealth(): ModuleHealthReport {
if (!$this->hasService('vendor:farm-service')) {
return ModuleHealthReport::error([
'Farm service is unavailable.'
]);
}
return ModuleHealthReport::ok();
}Health is displayed with:
/easymodule health <id>
/easymodule doctor| State | Meaning |
|---|---|
discovered |
Manifest loaded, lifecycle not started |
waiting_owner |
Owner plugin unavailable or disabled |
waiting_dependency |
Required module dependency unavailable |
waiting_service |
Required service or capability unavailable |
enabling |
Module is entering its lifecycle |
enabled |
Module is active |
disabling |
Module is releasing resources |
disabled |
Module is inactive |
reloading |
Module is being recreated |
failed |
Module lifecycle execution failed |
failed_dependency |
Dependency or version validation failed |
If a required dependency disappears, dependent modules are disabled until the dependency returns.
EasyLibrary exposes /easymodule with aliases /emodule and /modules.
/easymodule list
/easymodule info <id>
/easymodule why <id>
/easymodule resources <id>
/easymodule health <id>
/easymodule failures
/easymodule services
/easymodule capabilities
/easymodule provider <capability> [module|clear]
/easymodule enable <id>
/easymodule disable <id>
/easymodule disable-runtime <id>
/easymodule reload <id>
/easymodule refresh
/easymodule doctor
/easymodule graph
/easymodule dependents <id>
/easymodule debug <id> <on|off>disablepersists the disabled state inmodules.yml.disable-runtimeonly affects the current process.reloadrecreates a module instance and restarts its lifecycle.refreshrediscovers module manifests from declared sources.doctorshows missing dependencies, services, capabilities, plugins, and health.
Required permission:
easylibrary.command.modulesEasyLibrary modules let you:
- organize a plugin into independent runtime features.
- enable, disable, and reload features at runtime.
- declare strong and soft module dependencies.
- publish and consume shared services.
- provide and consume capability-based features.
- expose typed module APIs.
- use plugin dependencies for external PocketMine plugins.
- track commands, listeners, events, tasks, and async jobs.
- store per-module configuration and data.
- surface module health and dependency diagnostics.
module.yml:
id: examples:hello
name: Hello Module
version: 1.0.0
loader: HelloModule
namespace: easylibraryexamples\hello\modules\hello
load: POSTWORLD
enabled: trueHelloModule.php:
final class HelloModule extends BaseModule {
protected function onEnable(ModuleManager $manager): void {
$this->logger()->info('Hello module is enabled!');
$this->addCommand(HelloCommand::class);
}
protected function onDisable(ModuleManager $manager): void {
$this->logger()->info('Hello module is disabled.');
}
}This example demonstrates the simplest module lifecycle.
module.yml:
id: examples:economy
name: Economy Provider Example
version: 1.0.0
loader: EconomyModule
namespace: easylibraryexamples\economy\modules\economy
load: POSTWORLD
enabled: true
provides:
services:
- examples:economy-service
capabilities:
- economy
- examples.economy
requires:
services: []
capabilities: []EconomyModule.php:
final class EconomyModule extends BaseModule {
protected function onEnable(ModuleManager $manager): void {
$config = $this->getModuleConfig('config.yml', [
'starting-balance' => 100.0
]);
$service = new InMemoryEconomyService((float) $config->get('starting-balance', 100.0));
$this->provideService('examples:economy-service', $service, EconomyApi::class, '1.0.0');
$this->addCommand(EconomyCommand::class);
$this->logger()->info('Economy service examples:economy-service is available.');
}
public function getApi(): EconomyApi {
return $this->service;
}
}This module publishes a service and capability for others to use.
module.yml:
id: examples:farm
name: Farm Consumer Example
version: 1.0.0
loader: FarmModule
namespace: easylibraryexamples\farm\modules\farm
load: POSTWORLD
enabled: true
provides:
services: []
capabilities:
- examples.farm
requires:
services:
- examples:economy-service
capabilities:
- economy
soft-dependencies:
- examples:hello
plugin-dependencies:
- EasyModuleEconomyProviderExampleFarmModule.php:
final class FarmModule extends BaseModule {
private EconomyApi $economy;
private int $harvests = 0;
protected function onEnable(ModuleManager $manager): void {
$this->economy = $this->getTypedService('examples:economy-service', EconomyApi::class);
$this->addCommand(FarmCommand::class);
$provider = $this->getCapabilityProvider('economy');
$this->logger()->info('Economy provider: ' . ($provider?->getId() ?? 'none'));
}
public function getHealth(): ModuleHealthReport {
return ModuleHealthReport::ok([
'harvests' => $this->harvests,
'economyProvider' => $this->getCapabilityProvider('economy')?->getId() ?? 'none'
]);
}
}This example shows typed service consumption, capability lookup, and optional module dependencies.
If multiple modules provide the same capability, inspect providers with:
/easymodule provider economySelect a preferred provider:
/easymodule provider economy examples:economyClear the preference:
/easymodule provider economy clear/easymodule reload <id>reloads a module and restarts its lifecycle./easymodule refreshrediscovers module sources and newly added manifests.
Use reload after changing module code or configuration for an existing
module. Use refresh after adding or updating manifests in a declared source.
load: STARTUPmodules are initialized beforePOSTWORLDmodules.soft-dependenciesinfluence load order but do not block enabling.load-beforeforces other modules to load later.plugin-dependenciesensure required external PocketMine plugins are enabled.getModuleConfig()stores files in the module's private data folder.addComponent()is available when the owner plugin supportsPluginComponent.registerModule()allows programmatic module registration.
Real examples are located in:
src/imperazim/module/examples/They cover:
- basic module lifecycle and configuration.
- commands, listeners, and tasks.
- service and capability usage.
- module APIs and health checks.
- diagnostic and dependency failure scenarios.
See the examples guide for setup instructions.