Changelog¶
Format follows Keep a Changelog. Unreleased
changes accumulate under [Unreleased]; each release moves them into a
dated, versioned section below it (see
docs/release/releasing.md).
[Unreleased]¶
[1.9.0] - 2026-11-01¶
The second half of the menu overhaul, and a look over everything else: compatibility, migrations and how easy UniversalAdmin is to use.
Added¶
- Server, worlds and performance as hubs, like the player profile: what you see in a light blue row, what you do in rows behind coloured labels - green harmless, orange with an effect on players, red dangerous.
- Live values: the main menu, server, worlds, a world and performance update once a second while open.
- Traffic lights on the performance page: TPS, MSPT and memory in green, yellow or red, measured against your alert thresholds.
- A world's settings show their value: time (as a clock), weather, difficulty, spawn and world border show what they are now and change on a click.
- The audit log reads in the server's language: "Ban - Admin banned
Notch for 7d: Griefing" instead of
core:moderation.banand an English sentence. See ADR 0028. - Minecraft's names in your game's language: game modes, difficulties,
effects and entities ("Überlebensmodus", "Eile") instead of
SURVIVAL,SPEED. - New options reach your config.yml: an update writes options it brings into your existing file with their explanation; the console lists them.
- A hint for staff on their first join:
/adminopens the menu - and if your game runs in a language UniversalAdmin ships that the server does not use, one click switches it.
Fixed¶
- Heal, feed, extinguish, clear effects and the flight, glow, gravity and
collision switches wrote their raw input into the audit log
(
PlayerTargetInput[targetId=...]). - The used deprecated
getBedSpawnLocationand effectgetNameare replaced.
For extension developers¶
- API 1.8:
GuiRows,GuiButton#display,GuiPage#live(),ActionDefinition.Builder#auditArgs,Migration#namespace()(your own numbering inua_extension_migrations),MessageService#get(locale, ...). Everything has a default; extensions built against API 1.7 keep working. ./gradlew buildchecks the jar against the Paper API of 1.21.7, 1.21.11 and the version compiled against (binaryCompatibility).
[1.8.0] - 2026-10-28¶
The menus got a new look: a frame in every module's colour, glass panes that always mean the same, and hubs instead of long lists of buttons. See ADR 0027.
Added¶
- Main menu as a dashboard: players online, open reports, TPS, maintenance and the network at a glance, the modules in three groups, and a "My status" row to switch vanish, staff mode, godmode, staff chat and your nick on and off.
- Status line: staff see what they have switched on in the action bar,
e.g.
Vanish · Godmode · Nick Notch, while something is on. In staff mode the target display keeps the action bar. Off withgui.status-line: false. - A player's profile is the place for everything about them: who, where, playtime and condition on the left, then rows to moderate (warn, temp mute, kick, temp ban, freeze/unfreeze right there), view (inventory, ender chest, effects, history, reports, notes, alts), help (teleport, bring, heal, feed, flight, all actions) and more. The previous and next online player are one click away, the UUID can be copied.
- Player lists as tabs: the Players button opens the online players; Online, Offline and Recently seen switch in the header.
- A freeze is visible: the frozen player sees a title until they are
free (
moderation.freeze.show-title), and staff who may freeze see a marker above their head with who froze them and for how long (moderation.freeze.staff-marker). - Settings with readable names and descriptions in English and German; the config path is still shown below.
Changed¶
- Every page has the same frame: the header in the module's colour with back, main menu, the page's icon, refresh and close; tools and paging at the bottom; empty slots are black. Glass colours mean the same on every page - green on/confirm, red off/danger, orange warning, white "nothing here".
- Switching pages no longer closes and reopens the inventory - no flicker, the mouse stays where it is.
- Confirmations have a frame in the colour of what is at stake: red for a ban, orange for a kick or mute.
For extension developers¶
- API 1.7:
GuiGlass,MenuGroup,GuiWindow,GuiStyle,StatusToggle/StatusToggleRegistry/ActionStatusToggle,PlayerMenuGroup,PlayerMenuEntry#group(),GuiClickContext#page(),ModuleDescriptor#menuGroup/#color. Everything has a default - extensions built against API 1.6 keep working unchanged.
[1.7.2] - 2026-10-28¶
Fixed¶
- Godmode showed as "on" after every join on Paper 26, and the first click only switched it off. 1.7.1 read the player's invulnerability, which Paper also reports for the few seconds of protection after joining or a hit. Godmode is now kept as a mark in the player's data. A player with the godmode permission who still has godmode from an older version is recognised a few seconds after joining, once that protection is over.
[1.7.1] - 2026-10-28¶
Fixed¶
- Corrected texts now reach every server.
lang/<locale>.ymlholds only your own texts; every other text comes from the plugin and follows its updates. Up to 1.7.0 the whole built-in file was copied there once and hid every later correction - such a copy is cleaned once (your changes stay, the original is kept aslang/<locale>.yml.before-1.7.1). Every built-in text is inlang/reference/to copy from. See ADR 0026. - Godmode after a restart: a player who still had godmode on when the server restarted stayed invulnerable while the menu said "off", and needed two clicks to turn it off. Godmode now follows the player's real invulnerability.
- Empty lists show a white "Nothing here." instead of a grey pane that looked like filling.
[1.7.0] - 2026-10-28¶
Staff can play incognito now.
Added¶
- Nick system, a new built-in module:
/nick Notchshows you as Notch, with Notch's skin, in the chat, the tab list, above your head, in join, quit and death messages and to other plugins. Name and skin can be chosen separately (/nick Bob Notch), or both at random (/nick random);/unnickends it, also from the new Nick menu. Names of players who have played here, of online players and of other nicks are refused. Staff withuniversaladmin.nick.seesee the real name in the player list, on the profile and in/nick list; punishments, notes, reports and the audit log keep the real identity, and actions a nicked staff member takes run under their real name. A nick survives rejoining (nick.restore-on-rejoin) and follows its player through a network. See docs/user/modules/nick.md and ADR 0025. - UniversalAdmin TAB, an official extension for TAB: nicked players are shown in an ordinary TAB group, vanished staff and players in staff mode get a marker. Its own repository: universaladmin-mc/universaladmin-tab.
- Extension API 1.6: the
Nicksservice (dev.universaladmin.status),PlayerNames.real(...)andPlayerNames.revealedTo(...). - Vanish sets the
vanishedplayer metadata vanish plugins share, so TAB, map plugins and others recognise UniversalAdmin's vanish.
Fixed¶
- On Java 21 the update check and the anonymous statistics did not start ("No implementation of the random number generator algorithm").
- A player who left while vanished came back invisible when
moderation.vanish.restore-on-reconnectwas off.
[1.6.0] - 2026-10-28¶
Updates install themselves, the project has a home of its own - and a new look.
Added¶
- Automatic updates: a new release (of UniversalAdmin or of an
installed official extension) is downloaded, checked against its SHA-256
checksum and installed on the next restart. Only within the same major
version - a major update waits for
/admin update. Switch it off withupdate.auto-download: false. Staff withuniversaladmin.update.notifyget a notice on join; the console logs it on every check. See docs/user/configuration.md and ADR 0024. /admin updateinstalls every available update at once - the plugin and the extensions, a major update too.- The proxy plugins (Velocity, BungeeCord) check for updates as well and
log a notice (
update-check.enabledin theirconfig.yml). They never download anything. - Documentation website:
universaladmin-mc.github.io/UniversalAdmin,
built from
docs/whenever it changes onmain. - New logos for UniversalAdmin, its extensions and the proxy plugins.
- API 1.5:
ExtensionManager#updatesFrom(Plugin, String, String)- an extension names the GitHub repository it is released from, and UniversalAdmin keeps it up to date too.
Changed¶
- The project moved to the GitHub organization
universaladmin-mc. Old links to
nicki41/UniversalAdminredirect. JitPack coordinates are nowcom.github.universaladmin-mc.UniversalAdmin. - The official extensions have repositories of their own (Discord, Placeholders, Voice Chat), with their own releases. Their jars are no longer attached to the UniversalAdmin release; Modrinth is unchanged.
Fixed¶
/admin updatepicked the first jar of a release - the API javadoc - and refused it over the checksum. It now takes the plugin jar by name./admin updatereports every plugin on its own line: one whose release cannot be fetched (for example an extension without a published release) no longer stops the others. A missing release and a used-up GitHub request limit are named as such.- "The details are in the server log" now holds for every internal error an action reports - its reason is logged.
[1.5.0] - 2026-10-24¶
Mute in the chat, in the voice chat or both, forms instead of one prompt after another, the whole network in the player menu - and a new license.
Added¶
- Chat, voice and full mutes:
/chatmuteand/voicemute(each with an optional duration) next to/mute, which stays a full mute. With a voice chat connected the punish form asks where to mute, and templates takechatmute [duration]andvoicemute [duration]steps. The player's notice, the staff notification, the history and/checkbanname the kind;/unmutelifts every kind. In a network a mute takes effect on every server at once. See docs/user/modules/moderation.md and ADR 0022. - UniversalAdmin Voice Chat, an official extension for Simple Voice
Chat: voice and full mutes silence the microphone, vanished staff cannot
be heard (
voicechat.silence-vanished), a hidden, isolated staff voice group (/admin voicegroup), the voice status on the profile with a click to take a player out of a group. New permissionsuniversaladmin.voicechat.staffgroupand.manage. See docs/extensions/voicechat.md. - The whole network in the player menu: in network mode Online Players lists every player of the network with the server they are on, with a Server filter and sorting by name or by server. A profile says where the player is and offers Teleport to player, Bring to me and Send to server... - also for players on this server.
- API 1.4:
MuteKind,PlayerStatus#chatMute/#voiceMute, theVoiceChatservice andGuiForm.
Changed¶
- Forms instead of one prompt after another (ADR 0023): punishing (reason, duration and mute kind in one form), adding to the whitelist (reason, notes, expiry), title broadcasts (title and subtitle), chat broadcasts (multi-line), enabling maintenance (reason, the message for players, whether to kick), the world border (one field per value, a transition slider, "use my position"), health, food, XP and speeds as sliders, and teleporting to coordinates (X, Y, Z and a world list).
- Text prompts accept up to 255 characters - the dialog's own limit of 32 cut longer reasons short.
- The punishment list and detail pages show the localized type.
- License: GPL-3.0-or-later from this version on, for the plugin, the
proxy plugins and the official extensions. The extension API
(
universaladmin-api) and the example extension stay under Apache-2.0, so extensions can still use any license. Versions up to 1.4.0 remain available under Apache-2.0. Every jar now carries its license text underMETA-INF/- see docs/release/licensing.md.
Upgrading¶
- Replace the jars and restart; a database column is added automatically.
Every existing mute is a full mute. For voice mutes, add
universaladmin-voicechat-<version>.jarnext to Simple Voice Chat on every server where players talk.
[1.4.0] - 2026-10-22¶
Find second accounts of banned players - without storing addresses - and the proxy checks for BungeeCord networks too.
Added¶
- Alt detection (opt-in,
moderation.alts.enabled): every login stores a keyed hash of the address - never the address itself; IPv6 counts by its /64 network - and accounts that share one show up as possible alts:/admin alts <player>, the Possible Alts button on a profile, and a staff hint when a player joins who shares an address with a banned account (on every server of a network). Optionally refuse such players (moderation.alts.refuse-banned-alts, off by default;universaladmin.bypass.alt-checkexempts players). Addresses are forgotten aftermoderation.alts.retention(90 days);/admin alts <player> forgetdeletes a player's on request; viewing and forgetting are audited. New permissionsuniversaladmin.moderation.alts,.alts.forgetanduniversaladmin.bypass.alt-check. See docs/user/modules/moderation.md and ADR 0020. - UniversalAdmin BungeeCord: the proxy plugin for BungeeCord networks -
bans, network maintenance, the network whitelist and the server-list text,
the same
config.ymlas the Velocity plugin,/uabungee. On GitHub and as the-bungeeversion on Modrinth (ADR 0021).
Changed¶
- The proxy checks live in one place (
proxy/common) for both proxy plugins; UniversalAdmin Velocity behaves as before.
Upgrading¶
- Replace the jars and restart; a new table is created automatically. Alt
detection stays off until you switch it on. On a BungeeCord proxy, add
universaladmin-bungee-<version>.jarand fill in itsconfig.ymllike the Velocity plugin's.
[1.3.0] - 2026-10-22¶
Consistent punishments that escalate on their own, and a network that can be whitelisted and keeps vanished staff hidden.
Added¶
- Punishment templates with escalation:
/punish Steve spamwarns Steve the first time, mutes him for an hour the second time, and so on - steps per template inpunishment-templates.yml(five examples in the server's language), counting earlier uses of the same template within 90 days (per template;0= forever). Also as Punish with Template in the moderation menu and/punishin the Discord bot./punish <player>still opens the moderation menu (now with the templates first); from the console it lists what each template would do. Each step still needs its own permission. New permissionuniversaladmin.moderation.punish. See docs/user/modules/moderation.md and ADR 0018. - Network whitelist (
/admin whitelist network on|off|status|add|remove|list): one list for every server of the network, on top of each server's own, also enforced at the proxy by UniversalAdmin Velocity (whitelist-check). New permissionsuniversaladmin.whitelist.networkanduniversaladmin.bypass.network-whitelist. See docs/user/modules/whitelist.md. - Vanish across the network: vanished players are left out of the
network list,
/admin find, player counts, tab completion and the network placeholders for everyone withoutuniversaladmin.bypass.vanish, and stay vanished when switching servers even withvanish.restore-on-reconnect: false(ADR 0019). - Extension API 1.3:
NetworkService#vanished(UUID).
Fixed¶
- Whitelist metadata in network mode was one table for every server, so one server's expiry sweep could remove a player from another server's whitelist. Each server now keeps its own; temporary entries keep their expiry after the upgrade.
%universaladmin_network_online%and%universaladmin_network_online_<server>%no longer count vanished players.
Upgrading¶
- Replace the jars - the plugin on every server, UniversalAdmin Velocity on
the proxy, and the extensions (UniversalAdmin Placeholders 1.3 needs
UniversalAdmin 1.3) - and restart. New tables and columns are created
automatically,
punishment-templates.ymlis written on the first start. In a network, give the servers a minute to restart together: a server that is still on 1.2 keeps working, but does not know the network whitelist.
[1.2.1] - 2026-10-22¶
Maintenance done right in a network: one server or all of them.
Added¶
- Network maintenance:
/admin maintenance network on [kick] [reason]closes every server of the network at once (and the proxy with UniversalAdmin Velocity), with its own reason, message and allow-list;off,status,list,allowanddenylikewise. The GUI page has a second row for it. New permissionuniversaladmin.server.maintenance.network. See docs/user/network.md and ADR 0017. - UniversalAdmin Velocity refuses logins during network maintenance
(bypass permission on the proxy or the network allow-list still get in)
and shows "Maintenance" in the server list (
maintenance-checkin its config.yml)./uavelocityshows both checks. - A server in network mode without LuckPerms says at startup that OP and permissions count per server, and how to share them (network.md).
Fixed¶
- Maintenance in network mode was shared by accident: every server used the same stored state, so putting one server into maintenance closed the others after their next restart. Each server now keeps its own.
/admin maintenance statusand the dashboard tile also show the network's maintenance.- Import summary and allow-list audit entries no longer say "1 punishments" or "1 players".
- UniversalAdmin Velocity names its author in the proxy log, and no longer uses the deprecated event order API.
- Javadoc of the extension API: no more links to classes that only exist in the plugin.
Upgrading¶
- Replace the jars (plugin on every server, and UniversalAdmin Velocity on
the proxy if you use it) and restart. A new table (
ua_maintenance) is created and the old maintenance state copied into it - every server keeps the maintenance it had. In a network, give every server anetwork.server-nameif you have not yet.
[1.2.0] - 2026-10-21¶
Switching made easy: bring your bans over from LiteBans, AdvancedBan, the vanilla ban lists or EssentialsX. Plus PlaceholderAPI placeholders and a Discord bot, both as official extensions with their own Modrinth pages.
Added¶
- Import from other plugins:
/admin importbrings bans (also IP bans), mutes, warnings and kicks over from LiteBans (MySQL/MariaDB or its H2 file), AdvancedBan (MySQL or its HSQLDB files), the vanilla ban lists and EssentialsX (mutes; jails become staff notes) - with reasons, dates, who issued and who lifted them, and what is still in force. A preview shows everything before anything is written; running it again only imports what is new; players who never joined become known by name. Vanilla entries are moved so unbans work. See docs/user/import.md. - UniversalAdmin Placeholders - a new official extension for
PlaceholderAPI:
%universaladmin_vanished%,_muted,_mute_remaining,_warnings,_online(without vanished players),_staff_online,_open_reports,_maintenance, network counts and more. See docs/extensions/placeholderapi.md. - Discord bot mode for UniversalAdmin Discord: two-way staff chat,
slash commands (
/ban,/mute,/kick,/warn,/unban,/unmute,/history,/check,/find,/ua <any admin command>) with player autocomplete, and reports with Claim / Resolve / Reject buttons. Discord roles map to permission nodes; everything is checked and audited like in game. Webhook mode stays the default. See docs/extensions/discord.md. - Extension API 1.2:
Actor.remote/ActorType.REMOTE,RemoteCommandsandStaffChat(ADR 0016). - Extension API 1.2: status services for displays
(
dev.universaladmin.status:PlayerStatus,ReportOverview,MaintenanceStatus) andPlayerDirectory#addIfAbsent. - New permission
universaladmin.moderation.import.
Upgrading¶
- Replace the jar and restart. A new table (
ua_import_records) is created automatically; nothing else changes.
[1.1.0] - 2026-10-20¶
The first stable release: commands for everything, a report system, chat tools, staff notes, an in-game settings editor, network mode for Velocity and BungeeCord networks, a public extension API and the first official extensions. One jar runs on Paper 1.21.7 - 1.21.11 and 26.1.1 - 26.3. Upgrading from an alpha is automatic - see "Upgrading" below. (1.0.0 was never published on its own; everything planned for it is part of 1.1.0.)
Added¶
- Commands for every module under
/admin(aliases/ua,/uadmin), with tab completion and a clickable/admin help. Optional top-level shortcuts such as/kick,/ban,/mute,/warn,/history,/vanish,/heal,/gm,/invsee,/broadcast,/maintenance,/lag(commands.shortcuts.enabled/disabled;/universaladmin:<label>always works). Full list: docs/user/commands.md. - Moderation: kick, ban/tempban/ipban (
/ban Steve 7d griefing), mute/tempmute, warn/unwarn, freeze/unfreeze, unban/unmute, history, check, moderate, vanish, godmode, nocollision, staff [recover], notes. - Players: player, info (
/seen), heal, feed, extinguish, fly, gamemode, speed, tp, tphere, invsee, endersee, clearinv. - Server: broadcast, server status/title/actionbar/shutdown/restart/cancel, maintenance on/off/allow/deny/list.
- Worlds: worlds, world info/tp/time/weather/difficulty/setspawn/border/ gamerule. Whitelist: on/off/add (also players who never joined, with an optional duration)/remove/list. Performance: perf, entities [clear]. Audit: audit [player] [page]. Core: status, modules, reload, update.
- Reports module:
/report <player> <reason>for everyone (cooldown and open-report limit), instant clickable staff notifications with a sound, a reminder on join, a reports GUI with claim/resolve/reject and teleports to the player or the report location, a "Reports" button on every profile, and a thank-you to the reporter. See docs/user/modules/reports.md. - Chat module: staff chat (
/sc, toggle mode, or a#prefix), chat lock, slow mode and clear chat - commands, GUI page, audited. See docs/user/modules/chat.md. - Staff notes about players (profile button,
/admin notes, a hint for staff when a player with notes joins). - Team notifications: every punishment and pardon is announced to online
staff with
universaladmin.moderation.notify. - Settings editor: every setting of the core, every module and every
extension is editable in-game (
/admin→ Settings,/admin settings get|set|reset|list); changes are validated, written toconfig.ymlwith its comments intact, applied live where possible and audited. Sensitive values (database password) stay file-only. - Extension API (
universaladmin-api): extensions are ordinary Paper plugins withdepend: [UniversalAdmin]that register modules throughUniversalAdminProvider.get().extensions()- with commands, GUI pages, main-menu entries, profile buttons, audited actions, permissions, settings, translations, database migrations and audit/action listeners, all cleaned up automatically when the extension is disabled. Published with every release (binary, sources, javadoc). Developer guide: docs/extensions/getting-started.md. - UniversalAdmin Discord - the first official extension: posts punishments, reports, restarts, maintenance and whitelist changes to a Discord channel via a webhook (docs/extensions/discord.md).
- Live inventory and ender chest editor: changes show up in both directions within a tick - what the player does appears in the editor, what staff change lands in the player's inventory. Laid out like the player's own screen (storage, hotbar, armor, offhand); one audit entry per editing session with the number of changed slots.
- Network mode for servers behind a Velocity or BungeeCord proxy that
share one MySQL database (
network.enabled): a Network menu with every server - also those without UniversalAdmin - and its players,/admin network,/admin find <player>, cross-server/admin tpandtphere(you land right next to the player),/admin send,/admin connect, network-wide/admin alert; bans, mutes, warnings, kicks and freezes take effect on every server at once; staff chat, staff notifications and report alerts across all servers, tagged with the server; one audit log for the network, every entry tagged with its server; commands find every player of the network. See docs/user/network.md. - UniversalAdmin Velocity - an optional proxy plugin that turns banned
players away at the proxy, before they reach any server (also servers
without UniversalAdmin), with a configurable ban screen and
/uavelocity. Published next to the plugin on GitHub and Modrinth. - One jar for many Minecraft versions: Paper (and Purpur) 1.21.7 - 1.21.11 and 26.1.1 - 26.3, Java 21 or newer. Every listed version is compiled against and boot-tested; a weekly check tests new Paper versions and adds them to the newest release on Modrinth as soon as they pass (docs/user/installation.md).
- An example extension (
examples/example-extension) as a template. - Ready-made LuckPerms role templates (helper, moderator, admin) in docs/user/permissions.md.
- The main menu and module names are translated (
menu.*keys).
Changed¶
- The shipped
config.ymllists every setting of every module, with comments, grouped by module (moderation.vanish.*,server.countdown.*,players.gui.*, ...). - The audit log shows player names instead of UUIDs in targets and summaries.
- Precise feedback everywhere: every command and every GUI button says
exactly what it did - "Spawn of world set to 120 64 -30", "Banned Steve
for 7d. Reason: griefing #42", "Removed 37 entities in world" - instead
of "Done.", and command and GUI show the same text. Extensions get the
same through
ActionResult.success(value, key, args...). - Every reply carries a short
[UA]prefix (command.prefix, may be set to"") - in chat after a GUI click too. - Staff mode tools work at range: aim at a player and right-click - the
tool acts on the player the actionbar names, up to
moderation.staffmode.target-range-blocks(now 128 blocks, through walls), not only on someone close enough to click directly. - Menu item texts are consistently styled (white names, gray lore - no more purple italic lore lines).
- The update check and
/admin updatecompare versions properly and never offer an older release. - Releases on GitHub additionally carry the Discord extension, the Velocity plugin and the API jars; Modrinth versions are marked as release instead of alpha and list every supported Minecraft version.
- Built for Java 21 (was 25), so servers on 1.21.x run it too.
- Extensions:
UniversalAdmin.network()(servers, players, messages between servers, moving players), thePlayerDirectoryservice (find a player by name or UUID network-wide) andCommandInvocation.withPlayerfor commands that take any known player.
Fixed¶
- Settings of modules (vanish, freeze, staff mode, reports, ...) were only
read from
config.ymlafter an/admin reload; they now apply from the start. - New texts added in an update showed up as
[missing: ...]because the copied language files were never updated; the bundled texts are now the fallback for every key. - A typo in a language file stopped the whole plugin from starting; the file is now skipped with an error in the log.
- Staff notifications (performance alerts, update notices, frozen-player
disconnects) showed raw
<yellow>tags instead of colours. - Confirmation dialogs went back to the wrong page on "cancel", and a quick double click could confirm a dangerous action twice; GUI buttons now ignore double clicks.
/admin updatecould "update" to an older version on a server running a newer build.- The inventory editor did not show the player's own changes while it was open and could overwrite them with its outdated copy; it also wrote an audit entry for every single click.
- Punishing a player who was offline stored their UUID instead of their name in the history.
- The Freeze Tool could freeze and immediately unfreeze with one click.
- Several servers starting at the same time on a new MySQL database could run the same schema migration twice; migrations now run under a database lock, and a migration added later is applied even if newer ones already ran.
- Console commands sent over RCON got no reply.
- "Could not save lang/en_US.yml ... already exists" warnings on every start.
Security¶
- Text typed by players and staff (reasons, report texts, chat, notes, filters) can no longer inject MiniMessage formatting, hover or click events into messages other players see - every message argument is escaped (ADR 0012).
- Discord messages never ping anyone (
allowed_mentionsdisabled), and the webhook URL is never logged.
Upgrading from 0.1.0-alpha¶
- Replace the jar and restart.
config.ymlis migrated automatically (config version 3): module settings move under their module's section and the new sections (commands,network, ...) are added. Nothing you set is lost or overwritten. - New database tables (
ua_reports,ua_player_notes) are created automatically. - Your
lang/*.ymlfiles are kept; every text you did not change now comes from the plugin itself, so new texts appear automatically. - New permissions to hand out:
universaladmin.reports.*,universaladmin.chat.*,universaladmin.moderation.notes,universaladmin.moderation.notify,universaladmin.status,universaladmin.network.*- anduniversaladmin.reports.createis granted to everyone by default. - Running a network? See docs/user/network.md.
[0.1.0-alpha.5] - 2026-08-26¶
Added¶
- Every GitHub release is now automatically mirrored to the
universaladminModrinth project as a new version (Kira-NT/mc-publish, the newMODRINTH_TOKENrepository secret). See docs/release/releasing.md. - The
universaladminModrinth project page is now filled in: icon (assets/icon.png), title, summary, description, categories, license, and links - content lives in docs/release/modrinth.md.
[0.1.0-alpha.4] - 2026-08-26¶
[0.1.0-alpha.3] - 2026-08-26¶
Changed¶
- Releases are no longer flagged "Pre-release" on GitHub,
-alpha/-beta/-rcversions included - the project's status is already communicated by the version string and the README, not GitHub's separate prerelease flag. See docs/release/releasing.md. - The telemetry endpoint is no longer a config value:
telemetry.endpointhas been removed fromconfig.ymlandCoreSettings, and every heartbeat now goes to the officialnicki41-telemetryinstance, fixed inTelemetryBootstrap.telemetry.enabledremains the only switch. This also means an oldconfig.ymlwith a leftovertelemetry.endpointvalue (e.g. an explicit empty string) no longer has any effect.
Fixed¶
auto-release.yml's version-bump commit/tag push had no recovery ifmainmoved between checkout and push (hit for real when a backlog of delayed Actions runs raced each other) - now retries up to 5 times, each attempt recomputing from a freshgit fetch/reset --hard origin/main.
[0.1.0-alpha.2] - 2026-08-26¶
Added¶
- Automatic update checks:
dev.universaladmin.updatepolls the GitHub releases API on a jittered interval (default every 6 hours) and, when a newer version exists, notifies staff (universaladmin.update.notify) and logs a console warning./admin update(universaladmin.update.apply) downloads the latest release's jar into Bukkit's ownplugins/update/folder (verified against its published SHA-256 checksum) - applied at the next restart, never a live hot-swap.update.check-for-updates(defaulttrue) is the on/off switch;update.check-intervalconfigures the background timer. - Empty-slot placeholders in the Players module's and Staff-Mode's
inventory/ender-chest editors: a dyed-gray leather armor piece per slot
(helmet/chestplate/leggings/boots), a grayed-out shield for offhand, and a
visibly distinct hotbar row -
EmptySlotPlaceholder, stripped back tonullbefore ever writing to a real inventory.
Changed¶
TelemetryPayload's wire format gained apluginIdfield (fixed:"universaladmin") and renameduniversalAdminVersiontopluginVersion- the shape a shared, multi-plugin telemetry backend expects (see docs/user/telemetry.md). What's collected and the opt-out are unchanged.telemetry.endpointnow defaults tohttps://telemetry.0nicki.de/v1/telemetry(the officialnicki41-telemetryinstance) instead of empty - a fresh install now reports anonymous usage statistics out of the box;telemetry.enabled: false(or clearingtelemetry.endpoint) still turns it off entirely. See docs/user/telemetry.md.
Fixed¶
- Closing a live inventory/ender-chest editor (Players module or the Staff-Mode Inspector tools) showed the "Done." confirmation twice - both halves of the split main-storage/equipment write notified independently; now only the first does.
[0.1.0-alpha.1] - 2026-08-26¶
Added¶
- Anonymous usage statistics (
dev.universaladmin.telemetry):InstallationIdentity/InstallationIdentityStore(128 bits fromSecureRandom, derived from nothing, persisted ininstallation-id.yml),TelemetryPayload(exactly six fields),TelemetryClientwithHttpTelemetryClient(JDK HTTP client, short timeouts, no redirects, response discarded) andNoOpTelemetryClient,TelemetryService(enabled check on every heartbeat, player counts read on the main thread, the request runs in the background, failures are swallowed and only warned once per run),TelemetryScheduler(a delayed start, an interval with jitter, its own daemon thread), andTelemetryBootstrap(wiring with three outcomes: off / no endpoint / active). New settingstelemetry.enabled(switchable live),telemetry.endpoint(empty by default - nothing is sent by default), andtelemetry.interval. Full documentation including everything that's never collected: docs/user/telemetry.md. - Tests for telemetry with zero real network requests: id generation/persistence, "disabled means zero requests", a payload without player identities, serialization, an endpoint failure with no side effect, jitter bounds, scheduler cleanup.
LICENSE(Apache-2.0, unmodified official license text) and a licensing decision with rationale in docs/release/licensing.md.docs/development/architecture-rules.md- the binding architecture, package, threading, settings, module, security, dependency, and test rules as regular developer documentation.docs/release/releasing.md- a reproducible release process (version → CHANGELOG → build → commit → tag → push)..github/workflows/release.yml- av*tag automatically produces a GitHub release: a tag-vs-project-version check, a build including tests, the shaded jar plus a SHA-256 file as assets, prerelease detection for-alpha/-beta/-rc.contents: write, no additional secrets. Also callable directly (workflow_call, with ataginput) byauto-release.ymlbelow, not only by its own tag-push trigger..github/workflows/auto-release.yml- releases automatically after any push tomainthat changes more than docs/CI metadata: bumps the-alpha.Ncounter inbuild.gradle.kts, moves theCHANGELOG.md[Unreleased]section into a dated one, commits, tags, and callsrelease.yml. Skips a beta/rc/stable version bump (a bigger decision than this workflow makes on its own) and its own release commits (no loop). See docs/release/releasing.md.- A
printVersionGradle task as the single source of the project version for CI (the tag check, artifact naming). -
Issue forms (
bug_report.yml,feature_request.yml) andISSUE_TEMPLATE/config.ymlpointing at private vulnerability reporting instead of public security issues. -
Project architecture defined and documented (see ARCHITECTURE.md, docs/architecture/, ADRs under docs/architecture/decisions/).
- Gradle setup (Java 25 toolchain, Shadow plugin, SQLite/MySQL drivers, JUnit 5).
- Plugin bootstrap (
UniversalAdminPlugin) with manual dependency wiring. - Core abstractions:
Module,Action/ActionResult,GuiPage,Repository/Migration,PermissionRegistry,ServiceRegistry,AuditService,MessageService,NotificationService,TaskScheduler. - Eight module skeletons: Players, Moderation, Server, Worlds, Whitelist,
Performance, Audit Log, Settings.
Playersas the complete reference implementation (repository → service → action, including a migration). -
Test infrastructure with example tests (registry, permission validation, a service with an in-memory fake, migrations against a real SQLite database).
-
Core bootstrap lifecycle (
onLoad/onEnable/onDisable) with a clear separation between critical core components (config, scheduler, storage/migrations, shared registries - failures abort the whole plugin start) and individual modules (failures are isolated, seeModuleManager). - Internal module lifecycle system:
ModuleDescriptor(metadata including dependencies, settings/permission namespace, an optional GUI icon placeholder),ModuleState(DISCOVERED/LOADED/ENABLED/DISABLED/FAILED),ModuleRegistry(state tracking),ModuleManager(dependency ordering, failure isolation),ModuleResources(automatic release of listeners/tasks/registry entries on disable). See docs/architecture/modules.md. PluginStatus/ComponentStatus- a live snapshot of version, uptime, active/failed modules, coarse database/web status./admincommand (aliases/ua,/uadmin) as a status placeholder, renderingPluginStatus.unregister(...)onActionRegistry/GuiRegistry/PermissionRegistryfor module-disable cleanup.-
Tests for the module lifecycle system (
ModuleManagerTest): registration, a duplicate module id, lifecycle ordering, failure isolation, disable cleanup, dependency ordering, cycle detection, a missing dependency. -
Typed settings system (
dev.universaladmin.settings):SettingKey<T>/SettingType<T>/SettingValidator<T>/SettingDefinition<T>/SettingValue<T>/SettingRegistry/SettingsService, with built-in types for string/boolean/int/long/double/Duration/enum/string list, validators for min/max/regex/multiple-of, and namespacing between core settings and future module/extension settings. See docs/development/settings.md and ADR 0007. CoreSettings- registers the completeconfig.ymltree (general,database,gui,audit,modules,performance,maintenance,web);modules.*now actually controls whether a built-in module gets registered at all.config.ymlversioning:config-version,ConfigMigration/ConfigMigrationRunner(dev.universaladmin.config) - an existing user config gets migrated on future schema changes instead of overwritten.- A safe
/admin reload(ReloadConfigAction, permissionuniversaladmin.reload) - only ever reloads UniversalAdmin's ownconfig.yml, never Bukkit's global/reload. Restart-required settings are reported as "pending restart" on change instead of applied live. - Multi-language message system:
lang/en_US.yml(default/fallback) andlang/de_DE.yml,YamlLocaleMessageServicewith a fallback chain (active locale →en_US→ a visible[missing: ...]marker with a one-time debug log),ComponentMessagesas a MiniMessage renderer for in-game output, kept separate from plain string resolution (for web reuse). - Tests for the settings/localization system: defaults, invalid values (parse/validation errors → fallback instead of a crash), enum parsing, duration parsing, duplicate setting keys, locale fallback, parameter interpolation.
DatabaseHealth(DISCONNECTED/CONNECTING/READY/FAILED), tracked byStorageServiceand mapped onto the existingComponentStatusinUniversalAdmin#status()- see docs/architecture/storage.md#health.- SQLite pragmas in
DataSourceFactory(journal_mode=WAL,synchronous=NORMAL,foreign_keys=on,busy_timeout=5000). Transactions(dev.universaladmin.storage) - a helper for repository methods that need to run multiple statements atomically (commit/ rollback) over one connection.StorageException- a generic unchecked wrapper forSQLExceptionin the storage foundation itself (StorageService,Transactions).docs/user/database.md- user documentation for SQLite vs. MySQL/MariaDB (when which, creating a MySQL user, behavior on startup failure, backups).- Tests:
StorageServiceTest(SQLite init, health states, failure on an invalid path),TransactionsTest(commit, rollback on error),JdbcPlayerProfileRepositoryTest(the repository foundation end to end against a real SQLite database), database-type parsing inSettingTypesTest, an explicit "no duplicate migration run" assertion inMigrationRunnerTest. - A reusable in-game GUI framework (
dev.universaladmin.gui):AbstractGuiPage/AbstractListGuiPage(navigation, permission-driven visibility, pagination, async loading with loading/empty/error state),GuiSession/GuiSessionManager(per-player state with no heldPlayerreferences, cleanup on disconnect/a real inventory close),GuiListeneras the single, centrally registered click/drag handler,GuiLayout/Paginationas pure, tested slot/pagination logic,IconProvider/MaterialIconProvideras the central material resolution,ConfirmationDialog(danger level NORMAL/WARNING/ DANGEROUS),SelectionDialog,GuiTextInput(a free-text/search flow via the Paper dialog API instead of packet hacks/anvil/sign) - see docs/development/gui-framework.md. MainMenuPage: the/adminmain menu with one button per built-in module, filtered by actualModuleState.ENABLEDand permission;PlaceholderGuiPageas the "not built yet" target page per module. A new permissionuniversaladmin.menu.open.ModuleDescriptor.icon()set for all eight built-in modules (previouslynulleverywhere, seeGuiIcon).- Tests for the GUI framework:
PaginationTest,GuiLayoutTest,GuiSessionTest,GuiSessionManagerTest,GuiButtonVisibilityTest,GuiClickContextTest(the navigation stack),GuiListenerTest(click cancel/dispatch, close/quit cleanup). GuiView#onChange- a live-sync hookGuiListenerruns one tick after a click/drag it let through, on top of the existingonClose. Used by the Players module's inventory/ender chest editors and the new Staff-Mode Ender Chest Inspector tool so an edit mirrors onto the real target immediately instead of only when the viewer closes the GUI.- Staff-Mode Ender Chest Inspector tool (
EnderChestInspectorPage), alongside a now-live (not read-only) Inventory Inspector - both gated on the Players module's own...inventory.edit/...enderchest.editpermissions. See docs/user/modules/staff-tools.md. - Staff-Mode Teleport Picker tool: opens a player list, then a
"teleport to them"/"bring them" choice, routed through the Players
module's own audited
TeleportPlayerAction- replaces Random Teleport. - Staff-Mode target tracking now searches by look direction within a
configurable range (
moderation.staffmode.target-range-blocks, default 40) instead of a 6-block, wall-blocked ray trace, and sends a persistent actionbar naming the current target. The Player Inspector tool's head always shows the current target now, regardless of which tool is held. - Per-action success messages in the Moderation GUI (
ModerationGuiActions) - naming the target, reason, and duration where relevant - instead of a single generic "Done." for every action.
- Audit log GUI filtering by actor, module, and time range
(
AuditLogFilterPage), on top of the existing success/failure toggle - the query layer already supported all of this, only the GUI was missing. See docs/user/audit-log.md.
Changed¶
- The binding development rules now live under
docs/development/architecture-rules.md; every reference in source code and documentation points there. README.mdbrought to a public-facing state (status, features, requirements, quick start, database, permissions, anonymous statistics, extensions/web app explicitly marked as planned, license).CONTRIBUTING.mdandSECURITY.mdreworked: setup, architecture, tests, style, PR flow and private vulnerability reporting respectively, a telemetry/privacy section, and supported versions..github/workflows/build.ymlruns onpush/pull_requestagainstmain, sets up JDK 25 (build toolchain) and 21 (runs Gradle), uploads the jar under a versioned artifact name.- The PR template gained checklist items for secrets, localization, threading, and telemetry documentation.
.gitignoregained build/IDE/runtime/database/secret patterns as well as purely local notes files.- Target Paper version set to 26.2 (
paper-api:26.2.build.115-stable). Paper 26.2 is only published for JVM 25+, so the Java toolchain was raised from the originally planned version 21 to 25 - seebuild.gradle.ktsandplugin.yml(api-version: '26.2'). Moduleis now two-phase (onLoadthenonEnable) instead of justonEnable;id()/displayName()are replaced bydescriptor()(ModuleDescriptor). All eight built-in modules updated.UniversalAdminCommandnow shows aPluginStatusreport instead of just the list of loaded modules, and is registered under/admininstead of/ua.ConfigService/YamlConfigServiceremoved, replaced bySettingsService/YamlSettingsService(see above).UniversalAdmin.config()is nowUniversalAdmin.settings()(SettingsService) plusUniversalAdmin.settingRegistry()(SettingRegistry).messages_en_US.yml(a file at the plugin root) replaced bylang/en_US.yml/lang/de_DE.yml;YamlMessageService(one fixed locale) replaced byYamlLocaleMessageService(multiple locales, a fallback chain, reads the active locale live fromgeneral.language).-
/admin(no argument) now opensMainMenuPagefor a player instead of the text status report; for console/command blocks, the status report is unchanged./admin reloadunchanged.UniversalAdmin's constructor gained a new required field,GuiFramework(UniversalAdmin#guiFramework()). -
Players complete: a player browser GUI (online/offline/last-seen/ search), a profile page, ~20 actions (teleport, heal, effects, gamemode, inventory/ender chest editor), fine-grained permissions, audit. See docs/user/modules/players.md.
- Moderation complete: a punishment repository/service (kick/ban/
tempban/ipban/mute/tempmute/warn/freeze/unban/unmute/removewarn/
unfreeze), join/chat enforcement, a GUI wizard, fine-grained
permissions, audit, plus vanish/godmode/no-collision/staff mode
(crash-safe snapshot/recovery,
/admin staff recover). See docs/user/modules/moderation.md and docs/user/modules/staff-tools.md. - Server complete: a live dashboard (version/uptime/players/memory/
CPU/DB status/modules), broadcast (message/title/actionbar), its own
maintenance-mode system (repository/service, join enforcement,
allow-list), shutdown/restart with dangerous-action confirmation and a
configurable countdown + cancel,
/admin server ..., fine-grained permissions, audit. See docs/user/modules/server.md. - Worlds complete: a world browser/profile (environment/seed/spawn/
border/players/chunks/entities/time/weather/difficulty),
teleport/spawn/time/weather/difficulty actions, world border
management, a dynamic gamerule GUI (reads
World#getGameRules()at runtime), fine-grained permissions (seed kept separate), audit. See docs/user/modules/worlds.md. - Whitelist complete: native whitelist wrapping (enable/disable/ list/add/remove) plus its own metadata (added-by/at, reason, notes, expiration), time-limited entries with a join check and an hourly sweep, a strict ownership model, fine-grained permissions, audit. See docs/user/modules/whitelist.md.
- Audit Log complete (GUI side): a full
AuditEvent(actor/action/ module/target/source/success/reason/old-new value/world position/ metadata/correlation id), automatically populated byActionExecutor, a filtered, paginatedAuditService#query, a working GUI (a list with a success/failure filter, a detail page), hourly configurable retention./admin auditcommands still open. See docs/user/audit-log.md. - Performance complete: cached TPS/MSPT/memory/world/entity sampling on a configurable interval (never recomputed per GUI render), a dashboard, a per-world performance view, an entity overview (grouped by type/world), a short in-memory history, staff alerts on TPS/MSPT/ memory thresholds, a narrowly scoped Entity Clear (never players, configurable protected types, preview, confirmation, audit), fine-grained permissions. See docs/user/modules/performance.md.
Fixed¶
- Critical: every module-owned table was never created
(
player_profiles,punishments,server_maintenance_state,whitelist_entries,vanish_state, ...) -UniversalAdminPluginonly calledstorage.migrations().runPending()once, before any module was even enabled. But every module only registers its own migration(s) during its ownonEnable- without a secondrunPending()call afterward, those migrations never ran. Affected every module except Audit Log (whose two migrations were registered before that one, single call) - among other things, the cause of "storage error" in the Moderation/punishment module, "unexpected error" in maintenance mode, and broken whitelist/vanish state on join. See docs/architecture/threading.md. worlds.gui.actionexisted twice as a sibling key inlang/en_US.yml/de_DE.yml(once for success/error messages, once for button labels) - YAML doesn't really allow that, Bukkit's loader silently takes the last entry (a "duplicate keys found" warning at startup). Button labels renamed toworlds.gui.buttons.- Player lists (Players, Whitelist, audit log detail) showed the generic
Steve head everywhere instead of the real player skin -
GuiItemgained a newplayerHead(OfflinePlayer, ...)factory method (SkullMeta#setOwningPlayer), now used by every player-representing tile. - Critical: the plugin didn't start at all on a real Paper server
(
HikariConfig: Failed to load driver class dev.universaladmin.libs.sqlite.JDBC). Two interacting Shadow-plugin issues, neither detectable by the test suite (which runs against unshaded, unminimized dependencies) - only surfaced by an actual server start: shadowJar { minimize() }stripped both JDBC drivers from the jar entirely, because they're only loaded reflectively viaHikariConfig#setDriverClassName- invisible tominimize()'s static-reachability analysis. Both drivers are now explicitly excluded from minimization.org.sqlitewas being relocated like any other dependency - which breaks the JNI linking of the bundled native SQLite library (ClassNotFoundException: org/sqlite/core/NativeDB), since its compiled binary hardcodes the original class name.org.sqliteis now deliberately no longer relocated (but still ships bundled) - see docs/development/architecture-rules.md andbuild.gradle.kts.- A new Gradle task,
verifyShadedJarDrivers(runs as part ofcheck/build): opens a real SQLite connection exclusively through the finished, built jar (no other classpath entry), so this exact class of failure can never come back unnoticed again. SeeShadedJarDriverSmokeTestMain. PlayerProfileIndexMigration/ModerationPunishmentIndexMigrationusedCREATE INDEX IF NOT EXISTS, which real MySQL (unlike SQLite/MariaDB) doesn't support - removed, sinceMigrationRunneralready guarantees every migration runs at most once. See docs/architecture/storage.md#dialect-differences.- Freezing a player sent the target two notices for one click of the
Freeze Tool -
PlayerInteractEntityEventfires once per hand for a single physical right-click, andStaffModeGuardListenerdispatched on both. Now only the main-hand firing is handled. core:performance.clear-entitiesfailed to record its audit event on every run (IllegalArgumentException: Unsupported audit metadata value type ... ListN) - its metadata put the selected entity types in directly as aList, which the audit metadata codec only ever accepted String/Number/Boolean/null for. Now joined into a single string, same as the value already shown in the audit summary.