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: opapi-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
onEnablefailures: catch, log,getServer().getPluginManager().disablePlugin(this)— a half-enabled plugin is worse than a dead one. onLoadexists for early inter-plugin ordering; you almost never need it.