Modules - litjisz/Astra GitHub Wiki
Module System
The module system is the core of Astra, enabling a modular and extensible architecture for your plugin.
What is a Module?
A module in Astra is an independent unit that encapsulates specific functionality. Modules have their own lifecycle (enable, disable, reload) and can depend on other modules.
Creating a Module
Implementing the Module Interface
The most basic way to create a module is by implementing the Module
interface:
import lol.jisz.astra.api.Module;
public class MyModule implements Module {
@Override
public void enable() {
// Code to enable the module
}
@Override
public void disable() {
// Code to disable the module
}
@Override
public void reload() {
// Code to reload the module
}
}
Extending AbstractModule
For more complex modules, you can extend the AbstractModule
class which provides additional functionality:
import lol.jisz.astra.api.AbstractModule;
public class AdvancedModule extends AbstractModule {
@Override
public void onEnable() {
getLogger().info("Advanced module enabled");
// Initialize resources, register events, etc.
}
@Override
public void onDisable() {
getLogger().info("Advanced module disabled");
// Release resources, cancel tasks, etc.
}
@Override
public void onReload() {
getLogger().info("Advanced module reloaded");
// Reload configuration, reinitialize resources, etc.
}
}
Registering Modules
Manual Registration
You can register modules manually using the Implements
system:
// Register a simple module
Implements.register(new MyModule());
// Register and store a reference
MyModule myModule = Implements.register(new MyModule());
Automatic Registration with Annotations
You can use the @AutoRegisterModule
annotation to automatically register modules:
import lol.jisz.astra.api.AutoRegisterModule;
import lol.jisz.astra.api.AbstractModule;
@AutoRegisterModule
public class AutoModule extends AbstractModule {
// Module implementation
}
To enable automatic registration, you need to call the module scanner in your main plugin class:
@Override
protected void onInitialize() {
// Scan for modules with @AutoRegisterModule annotation
getModuleScanner().scanAndRegister();
}
Module Dependencies
Using @DependsOn Annotation
You can declare module dependencies using the @DependsOn
annotation:
import lol.jisz.astra.api.DependsOn;
import lol.jisz.astra.api.AbstractModule;
@DependsOn({DatabaseModule.class, ConfigModule.class})
public class UserModule extends AbstractModule {
// Module implementation
}
Manual Dependency Registration
You can also register modules with dependencies manually:
// Register with dependencies
Implements.registerWithDependencies(new UserModule(), DatabaseModule.class, ConfigModule.class);
Module Prioritization
You can control the initialization order of modules by implementing the PrioritizedModule
interface:
import lol.jisz.astra.api.PrioritizedModule;
import lol.jisz.astra.api.AbstractModule;
public class CoreModule extends AbstractModule implements PrioritizedModule {
@Override
public int getPriority() {
return 100; // Higher priority, initialized earlier
}
}
Accessing Registered Modules
Use the Implements
system to access registered modules:
// Get a module by its class
DatabaseModule dbModule = Implements.fetch(DatabaseModule.class);
if (dbModule != null) {
dbModule.executeQuery("SELECT * FROM users");
}
// Get a module with a specific identifier
FileConfiguration config = Implements.fetch(ConfigManager.class, "settings");
// Check if a module is registered
boolean hasModule = Implements.has(DatabaseModule.class);
Module Lifecycle
Modules in Astra have a well-defined lifecycle:
- Registration: The module is registered in the
Implements
system - Dependency Resolution: Dependencies are checked and resolved
- Enabling: The
enable()
method is called - Usage: The module is available for use by other components
- Reloading: The
reload()
method is called when needed - Disabling: The
disable()
method is called when the plugin shuts down
Listening to Module Lifecycle Events
You can register listeners for module lifecycle events:
import lol.jisz.astra.api.Module;
import lol.jisz.astra.api.ModuleLifecycleListener;
Implements.addLifecycleListener(new ModuleLifecycleListener() {
@Override
public void onModuleRegistered(Module module) {
System.out.println("Module registered: " + module.getClass().getSimpleName());
}
@Override
public void onModuleEnabled(Module module) {
System.out.println("Module enabled: " + module.getClass().getSimpleName());
}
@Override
public void onModuleDisabled(Module module) {
System.out.println("Module disabled: " + module.getClass().getSimpleName());
}
@Override
public void onModuleReloaded(Module module) {
System.out.println("Module reloaded: " + module.getClass().getSimpleName());
}
});
Best Practices
- Single Responsibility: Each module should focus on a single aspect of functionality
- Proper Lifecycle Management: Ensure resources are properly initialized and cleaned up
- Dependency Declaration: Clearly declare module dependencies to ensure proper initialization order
- Error Handling: Handle exceptions gracefully within module methods
- Configuration: Use the configuration system for module settings
- Documentation: Document the purpose and usage of each module
Example: Complete Module
Here's an example of a complete module with dependencies, configuration, and event handling:
import lol.jisz.astra.api.AbstractModule;
import lol.jisz.astra.api.DependsOn;
import lol.jisz.astra.api.Implements;
import lol.jisz.astra.api.PrioritizedModule;
import lol.jisz.astra.utils.ConfigManager;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerJoinEvent;
@DependsOn(ConfigManager.class)
public class WelcomeModule extends AbstractModule implements Listener, PrioritizedModule {
private String welcomeMessage;
private boolean enableWelcome;
@Override
public void onEnable() {
// Load configuration
ConfigManager configManager = Implements.fetch(ConfigManager.class);
FileConfiguration config = configManager.getConfig("welcome");
// Set default values if they don't exist
welcomeMessage = configManager.getString("welcome", "message", "Welcome to the server, %player%!");
enableWelcome = configManager.get("welcome", "enabled", true);
// Register events
getPlugin().getServer().getPluginManager().registerEvents(this, getPlugin());
getLogger().info("Welcome module enabled with message: " + welcomeMessage);
}
@Override
public void onDisable() {
// Save any pending changes
Implements.fetch(ConfigManager.class).saveConfig("welcome");
getLogger().info("Welcome module disabled");
}
@Override
public void onReload() {
// Reload configuration
ConfigManager configManager = Implements.fetch(ConfigManager.class);
configManager.reloadConfig("welcome");
welcomeMessage = configManager.getString("welcome", "message", "Welcome to the server, %player%!");
enableWelcome = configManager.get("welcome", "enabled", true);
getLogger().info("Welcome module reloaded");
}
@EventHandler
public void onPlayerJoin(PlayerJoinEvent event) {
if (enableWelcome) {
String personalizedMessage = welcomeMessage.replace("%player%", event.getPlayer().getName());
event.getPlayer().sendMessage(personalizedMessage);
}
}
@Override
public int getPriority() {
return 50; // Medium priority
}
}