Skip to content

Modules

Christian W. edited this page Dec 30, 2025 · 9 revisions

Main class

Main class for your module

package dev.cwhead.GravesX.modules.example;

import dev.cwhead.GravesX.module.GravesXModule;
import dev.cwhead.GravesX.module.ModuleContext;

public final class ExampleModule extends GravesXModule {
    private ModuleContext ctx;
    
    // Only if you register a GraveProvider
    // private ExampleGraveProvider provider;

    @Override
    public void onModuleLoad(ModuleContext ctx) {
        this.ctx = ctx;

        // Will pull any yaml files that would be in resources into "/plugins/GravesX/modules/<module-name>" where <module-name> will be the name of your module.
        ctx.saveDefaultConfig();

        // perform tasks on module load
    }

    @Override
    public void onModuleEnable(ModuleContext ctx) {
        this.ctx = ctx;

        if (!ctx.getConfig().getBoolean("enabled", true)) return;

        ctx.registerListener(new ExampleJoinListener(ctx));

        // For use if you register a GraveProvider
        // provider = new ExampleGraveProvider(ctx);
        // ctx.registerService(GraveProvider.class, provider, ServicePriority.Normal);
        
        // Handle loading the module
    }

    @Override
    public void onModuleDisable(ModuleContext ctx) {
        // ModuleContext auto-cleans listeners/services
        // You can handle your own disable tasks
    }

    // Any other code you want to add
}

Making a Module Command:

You do not have to register this command in your main class as it will be loaded in module.yml

package dev.cwhead.GravesX.modules.example.command;

import dev.cwhead.GravesX.module.ModuleContext;
import dev.cwhead.GravesX.module.command.GravesXModuleCommand;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;

public final class GxCommand implements GravesXModuleCommand {
    private final ModuleContext ctx;

    public GxCommand(ModuleContext ctx) {
        this.ctx = ctx;
    }

    @Override 
    public String getName() { 
        return "example";
    }

    @Override 
    public String getDescription() {
        return "describes what your module does."; 
    }

    @Override 
    public String getUsage() {
        return "/example <args>"; 
    }

    @Override public String getPermission() { 
        return "graves.example.permission"; 
    }

    @Override
    public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
        // Handle like you would with making a command
        return true;
    }
}

Making a Module Command Tab Completer:

package dev.cwhead.GravesX.modules.example.command;

import dev.cwhead.GravesX.module.ModuleContext;
import dev.cwhead.GravesX.module.command.GravesXModuleTabCompleter;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;

import java.util.Collections;
import java.util.List;

public final class GxTab implements GravesXModuleTabCompleter {
    
    public GxTab() {}

    public GxTab(ModuleContext ctx) {
        this.ctx = ctx;
    }

    @Override
    public List<String> onTabComplete(CommandSender sender, Command command, String alias, String[] args) {
        // Handle like you would with TabCompleter
        return Collections.emptyList();
    }
}

Creating your module.yml

# Name of the module
name: Example
# Main Class for your Module
main: dev.cwhead.GravesX.modules.example.ExampleModule
# Module Version
version: 1.0.0
# Module Description
description: "Description for my module"
# Determines how the module loads. Valid options (STARTUP|POSTWORLD|COMPLETED). If not set, will be COMPLETED.
load: POSTWORLD
# Determines module loads on Folia. This by default is false. Only really need to add this to modules that support Folia. This acts as a failsafe.
supportsFolia: true

libraries:
  # Simple coordinate (defaults apply)
  - com.squareup.okhttp3:okhttp:4.9.0

  # Full configuration example
  - coordinates: old.loc.library:library:1.0.0
    relocateFrom: "old.loc.library"
    relocateTo: "new.loc.library"
    isIsolated: false # Recommended to keep this false unless you know what you are doing
    useTransitive: false
    repo: "https://repo1.maven.org/maven2"
    id: "example-lib"

# List Author(s) (optional)
authors:
    - Joe

# Website (Can be for plugin page, support, etc.)
website: https://website.ext/

# Plugins that are required or else the module will not load
pluginDepends:
    - RequiredPlugin

# Plugin Soft Dependency. This will allow the module to load even if the plugin isn't enabled or installed
pluginSoftDepends:
    - SoftDepPlugin

# Plugin Load Before. This will allow this module to load before any plugin defined. This is not recommended as it could have undesired effects
pluginLoadBefore: 
    - SomeOtherPlugin

# Modules that are required or else the module will not load
moduleDepends:
    - RequiredModule

# Module Soft Dependency. This will allow the module to load even if the module isn't enabled or installed
moduleSoftDepend:
    - SoftDepModule

# Module Load Before. This will allow this module to load before any module defined.
moduleLoadBefore:
    - SomeOtherModule

# Handle Permissions
permissions:
  # Permission Node
  graves.example.permission:
    # Permission Description
    description: "Allows use of example action"
    # Should players have it by default [true, false, op, not op]
    default: true

# Handle Commands
commands:
  # Name of the command
  example:
    # Description for the command
    description: "Runs example command"
    # Usage
    usage: "/example [arg]"
    # Permission used (optional)
    permission: "graves.example.command"
    # Aliases
    aliases: ["ex"]
    # Executor that will be handled by GravesX
    executor: dev.cwhead.GravesX.modules.example.command.GxCommand
    # Tab Completer that will be handled by GravesX
    tab-completer: dev.cwhead.GravesX.modules.example.command.GxTab

Making a Grave Provider:

package dev.cwhead.GravesX.modules.example.examplegraveprovider;

import com.ranull.graves.data.EntityData;
import com.ranull.graves.type.Grave;
import dev.cwhead.GravesX.api.provider.GraveProvider;
import dev.cwhead.GravesX.module.ModuleContext;
import org.bukkit.Location;

public class ExampleGraveProvider implements GraveProvider {
    
    private final ModuleContext ctx;

    ExampleGraveProvider(ModuleContext ctx) {
        this.ctx = ctx;
    }

    @Override
    public String id() {
        return "example:provider";
    }

    @Override
    public int order() {
        return 0;
    }

    @Override
    public void place(Location location, Grave grave) throws Exception {
        // Place custom objects/entities for this grave
    }

    @Override
    public void remove(Grave grave) throws Exception {
        // Remove custom objects/entities for this grave
    }

    @Override
    public boolean isPlaced(Grave grave) {
        // Add your own detection to see if a grave is placed
        return true;
    }

    @Override
    public boolean supports(EntityData data) {
        // Return true if this provider recognizes the given EntityData
        return false;
    }

    @Override
    public boolean removeEntityData(EntityData data) throws Exception {
        // Remove specific custom entity data if supported
        return false;
    }
}

Can you use bukkit code and other plugins library code?

Yes you can. Once you finish, you will need to compile the plugin and put in /plugins/GravesX/modules and it will load the module that way. Any configurations will show up in /plugins/GravesX/modules/<module-name>

Clone this wiki locally