Skip to content

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); core is 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 ActionExecutor as 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#register must be called on the main thread after UniversalAdmin enabled (from the extension's onEnable). 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):