Extensions¶
Since 1.0, UniversalAdmin has a public, versioned extension API. An
extension is an ordinary Paper plugin that declares
depend: [UniversalAdmin] and registers one or more Modules - the exact
interface the built-in modules implement. Decision and rationale:
ADR 0010. How to write one:
docs/extensions/getting-started.md.
The API boundary¶
The API is the Gradle project universaladmin-api (api/). It contains the
interfaces and framework classes an extension compiles against; the core
(universaladmin-core, the repository root) contains the implementations
and the built-in modules. Package names are shared - the split is a jar
boundary, not a rename - and the plugin jar ships every API class
unrelocated.
From 1.0.0 on, everything in universaladmin-api follows semantic
versioning: no breaking change without a major version. Classes that are
only in the core jar (bootstrap, modules.*, *.jdbc, the Yaml*
implementations, ...) are internal and may change in any release.
UniversalAdminProvider.API_VERSION exposes the API level at runtime.
What an extension can contribute¶
| Extension point | API | Registered via ModuleContext |
|---|---|---|
| Modules (lifecycle, dependencies) | Module, ModuleDescriptor |
extensions().register(plugin, module) |
| Main-menu entries | ModuleDescriptor.Builder#menu(page, permission) |
descriptor |
| GUI pages | AbstractGuiPage, AbstractListGuiPage, dialogs, GuiTextInput |
registerPage |
| Player-profile buttons | PlayerMenuEntry |
registerPlayerMenuEntry |
/admin subcommands (+ shortcuts) |
AdminCommand, BaseAdminCommand, CommandInvocation |
registerCommand |
| Actions (authorized, validated, audited) | Action, ActionDefinition, ActionExecutor |
registerAction |
| Permissions | PermissionDefinition |
registerPermission |
| Settings (shown in the settings GUI) | SettingDefinition, SettingTypes, SettingValidators |
registerSetting |
| Translations | MessageBundles, MessageService#registerDefaults |
- |
| Database tables | Migration, StorageService, Repository, Transactions |
registerMigration (in onLoad) |
| Shared services | ServiceRegistry |
registerService |
| Bukkit listeners | - | registerListener |
| Observing every action | ActionEventListener |
subscribe |
| Observing every audit entry | AuditListener |
subscribeAudit |
| Notifications | NotificationService |
- |
| Threading | TaskScheduler (supplyAsync, runOnMainThread, supplyOnMainThread) |
- |
| Other servers of a network (1.1) | NetworkService (UniversalAdmin#network): servers, players, messages, moving players - a no-op without network mode |
subscribe/unsubscribe on the service |
| Any known player, network-wide (1.1) | PlayerDirectory service, CommandInvocation#withPlayer |
- |
Run /admin commands for someone outside the game (1.2) |
RemoteCommands service, Actor.remote - the same checks and audit as in game (ADR 0016) |
- |
| Staff chat bridges (1.2) | StaffChat service: send into and subscribe to the staff chat |
- |
| Status for displays (1.2) | PlayerStatus, ReportOverview, MaintenanceStatus services (dev.universaladmin.status) - from memory, main-thread safe |
- |
| Who is vanished elsewhere in the network (1.3) | NetworkService#vanished(UUID) - lists and counts shown to players leave vanished players out (ADR 0019) |
- |
Every register* call undoes itself when the module is disabled - which
happens automatically when the owning plugin is disabled.
Rules that keep extensions safe¶
- Own namespace. Extension ids use their own namespace
(
myextension:tickets);coreis reserved and refused by the loader. Permission nodes, lang keys and table names should carry the same prefix. - Same pipeline. An extension's action runs through the same
ActionExecutoras a built-in one: permission check, validation, audit. There is no shortcut for built-ins an extension could not use (ADR 0005). - Same process. An extension runs in the server's JVM like any plugin; there is no sandbox. Install extensions you trust, like any plugin.
- Main thread for registration.
ExtensionManager#registermust be called on the main thread after UniversalAdmin enabled (from the extension'sonEnable). Its migrations run synchronously right then.
Distribution¶
Extensions are distributed like any plugin (Modrinth, Hangar, GitHub
releases) and installed by dropping the jar into plugins/. A curated list
of extensions is planned - see ROADMAP.md.
Official extensions¶
Each has a repository of its own in the universaladmin-mc organization and builds against the published API like any third-party extension (ADR 0024):
- UniversalAdmin Discord (repository) - admin actions in a Discord channel, as a webhook or a bot. User docs: docs/extensions/discord.md.
- UniversalAdmin Placeholders (repository) - PlaceholderAPI placeholders. User docs: docs/extensions/placeholderapi.md.
- UniversalAdmin Voice Chat (repository) - Simple Voice Chat. User docs: docs/extensions/voicechat.md.
- UniversalAdmin TAB (repository) - TAB's tab list and name tags follow nicks, vanish and staff mode. User docs: docs/extensions/tab.md.
- Example extension (
examples/example-extension) - not for production; a complete, commented template that uses every extension point above.