Plugin Basics
Write handlers with decorators and register them in your entrypoint
EcmaCraft plugins are regular TypeScript modules with a default export function.
At runtime, EcmaCraft loads your compiled module and calls the default export with a PluginContext.
Minimal Plugin
import { Autocomplete, Command, Event, type PluginContext, type SpigotEventType } from 'ecmacraft';
import { CommandSender } from 'ecmacraft/spigot';
class GameplayHandlers {
@Event('PlayerJoinEvent')
onPlayerJoin(event: SpigotEventType<'PlayerJoinEvent'>) {
const player = event.getPlayer();
player.sendMessage('Welcome to the server!');
}
@Command('hello')
onHelloCommand(sender: CommandSender, args: string[], label: string) {
sender.sendMessage(`Hello from /${label} with ${args.length} arg(s)!`);
return true;
}
@Autocomplete('hello')
onHelloAutocomplete(sender: CommandSender, args: string[]) {
const options = ['world', 'ecmacraft', 'plugin'];
const lastArg = args[args.length - 1] ?? '';
return options.filter((option) => option.startsWith(String(lastArg).toLowerCase()));
}
}
export default function main(ctx: PluginContext) {
ctx.registerHandlers(new GameplayHandlers());
}How Handler Registration Works
- Decorators like
@Event(...)attach metadata. ctx.registerHandlers(...)scans methods on your handler instance.- Matching Java-side listeners/command bindings are wired at runtime.
If a method is not decorated, it is ignored during registration.
For commands, the method decorated with @Command(...) is the executor.
Use @Autocomplete('same-command-name') to attach tab completion to that command.
Project Shape
For a standard plugin project:
src/main.tsis your entrypoint,export default function main(ctx)is required,ctx.registerHandlers(...)connects your decorated classes.
Best Practices
- Keep event handlers small and focused.
- Split large features into multiple handler classes.
- Use TypeScript types (
SpigotEventType<...>) for safer API usage. - Put gameplay constants/config in separate modules, not inside handler methods.
- Return
truefrom command handlers when the command is handled successfully. - Keep tab completion fast and side-effect free.
For detailed runtime behavior (cleanup callbacks, command normalization/collisions,
timer execution order, and chalk styling), see Runtime API.