Skip to content

Lesson 2 of 7 · 18m

plugin.yml and the Lifecycle: onEnable, onDisable

What the server reads before it ever touches your code — and why resource registration happens in onEnable, not the constructor.

Before your code runs, Paper reads plugin.yml — the manifest that declares your main class, version, commands, and dependencies. A malformed plugin.yml is the most common "my plugin doesn't load" and the error is in the log, not your editor.

The manifest

name: SudoTools
version: 1.0.0
main: org.sudosecurity.sudotools.SudoToolsPlugin
api-version: "1.21"
commands:
  sudo:
    description: Admin utilities
    permission: sudotools.admin
permissions:
  sudotools.admin:
    default: op

api-version gates which server versions load you — set it honestly. main must be a fully-qualified class extending JavaPlugin. Commands declared here get tab-completion and /help entries.

The lifecycle

public class SudoToolsPlugin extends JavaPlugin {
    @Override public void onEnable() {
        saveDefaultConfig();
        getServer().getPluginManager().registerEvents(new JoinListener(), this);
        getCommand("sudo").setExecutor(new SudoCommand());
    }
    @Override public void onDisable() {
        // close DB connections, flush state
    }
}

Never touch the API in the constructor — the server isn't ready; fields like getServer() are null. onEnable is where you load config, register listeners/commands, and open connections. onDisable is where you close them — an unclosed database handle survives every /reload and piles up.

Field notes

  • Log enable/disable: getLogger().info("SudoTools enabled") — the log is how you debug a server you don't own.
  • Guard onEnable failures: catch, log, getServer().getPluginManager().disablePlugin(this) — a half-enabled plugin is worse than a dead one.
  • onLoad exists for early inter-plugin ordering; you almost never need it.