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:

  1. Registration: The module is registered in the Implements system
  2. Dependency Resolution: Dependencies are checked and resolved
  3. Enabling: The enable() method is called
  4. Usage: The module is available for use by other components
  5. Reloading: The reload() method is called when needed
  6. 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
    }
}