Skip to content

UltiTools-API 6.5.0 Release Notes ​

6.5.0 strengthens the panel's remote-command checks, fixes UltiCloud replies and player-link delivery, and lets missing configuration defaults be appended after unchanged YAML block scalars. It also improves module error reports and linkage diagnostics, validates player-cache save declarations, and deprecates obsolete language declarations without removing them.

The full list for module authors is in COMPATIBILITY.md. The release page is v6.5.0 on GitHub.

Remote-command defaults change

The default ultipanel.commands.blocklist adds execute, return and function. Existing operator lists are not extended or rewritten; add these entries yourself if you want the same refusals. Stop the server, edit the list, then start it again: /ul reload does not re-read it.

Headline: remote-command boundaries and reliable replies ​

  • The shipped blocklist grows from ten to thirteen entries. New installations and configurations without the key refuse direct execute, return and function commands, including namespaced forms such as minecraft:execute. Remote commands remain disabled by default through ultipanel.capabilities.commands: false. (#912)
  • Immediately before dispatch on the server thread, matching checks the resolved command's Bukkit name, label and aliases in both directions, case-insensitively and without namespaces. Listing plugins blocks pl and bukkit:pl; listing pl also blocks plugins. Synchronous nested calls into BaseCommandExecutor repeat the check before help, validators or a command body. (#905)
  • An unwrapped Error escaping command dispatch after acknowledgement now returns command_failed instead of leaving the panel without a result. LinkageError is handled without rethrow; other errors are answered before propagating. On Paper 1.21.11, raw Bukkit command failures are wrapped in CommandException and already returned command_failed in 6.4.0; repository unit fixtures exercise the unwrapped-error paths. A reflected framework command-body failure remains ordinary command output. (#905)
  • An asynchronous player teleport has a 25-second reply deadline. A timeout returns internal_error with teleport did not complete in time, but does not cancel the teleport; check the player's position before retrying. A late completion sends no second reply. (#905)

Before upgrading ​

  • Supported servers remain Paper 1.19.2 build 163 through Paper 1.21.x. Spigot is not a supported target. The module requirements for older Paper in the 6.4.0 release notes still apply.
  • Review an existing remote-command blocklist manually. To allow the three new default commands, remove only their entries while the server is stopped, then restart. An empty list ([]) disables the configurable list, not the independent refusal of panel-issued UltiCloud login and logout.
  • Check every @PlayerCache(saveBeforeRemove = true) bean: it must implement PlayerCacheSaver, including when the field is inherited. Otherwise registration now fails with PluginModuleException. Implement savePlayerData(UUID), or set saveBeforeRemove = false when no save is intended. (#897)
  • Check query conditions for null and non-finite numbers. getAll, exist, page and conditional deletion now refuse them consistently across SQLite, MySQL and JSON. updateIf retains its documented EQUAL-null meaning (IS NULL), but refuses other null comparisons and non-finite condition values.
  • Replace obsolete language declarations with packaged lang/<code>.json, .yml or .yaml resources. @I18n and @UltiToolsModule(i18n = ...) still compile and load in 6.5.0; missing packaged resources now produce a warning.

Added ​

  • ErrorReportCollector#reportErrorFromClass(Throwable, Class<?>, TriggerContext) resolves an error's module from the concrete class's JAR. The existing reportError entry point remains available. ErrorReportCollector is marked @ApiStatus.Internal; these entry points are not intended for module code. (#900)

Fixed ​

  • Missing configuration defaults can be appended after an unchanged literal (|) or folded (>) YAML block scalar followed by blank lines. Its spelling, indentation indicator, chomping, indicator-line comments and trailing empty lines remain byte-for-byte unchanged, including consistent LF, CRLF or CR line endings. This fixes the default-insertion limitation listed in 6.4.0. (#895)
  • SQLite, MySQL and JSON now reject null or infinite/NaN condition values with DataAccessException and DATA_ENTITY_INVALID, instead of backend-dependent empty results, accidental matches or raw exceptions. JSON also rejects del(WhereCondition.empty()) with the typed exception; exist(entity) returns false for an entity without an ID on every backend. The updateIf exception described above is unchanged. (#873)
  • Player-link completion claims held behind a lost create answer are released on connection close or error for redelivery without restarting. Receipt checking and claiming are atomic; a held completion or expiry is delivered after the create future's callbacks return. Cancelling the caller's future no longer prevents transport cleanup. (#905)
  • Command and @ExceptionCatch errors are attributed to the loaded module whose JAR owns the concrete executor or bean class, including proxied beans. Missing, unmatched or ambiguous sources remain unattributed. Deduplication, sampling and merging are module-scoped, so identical exception shapes in two modules remain separate groups. A missing or invalid identify-string falls back to the normalized plugin.yml name. (#900)
  • Error text respects the gateway's UTF-16 length bounds without splitting surrogate pairs, and log timestamps are clamped to the protocol range so one invalid timestamp does not reject the whole batch. (#900)
  • REST device-login Retry-After accepts 1–86400 seconds without delaying expiry past the device code's deadline. Supported-protocol headers use the contract's 1–99 range, and an active login cooldown rounds up instead of displaying zero seconds. Missing or non-directory editable roots are omitted from remote file listings. (#886)
  • Telemetry snapshots rebuild only after a new monitor sample; unchanged rejected payloads are not retried every second. Buffered log text is bounded in memory, and credential-file integers remain integers when saved. (#886)
  • Linkage-audit field references use Owner.name:descriptor. Offline --own accepts dotted or slash-separated package roots and matches package boundaries; exit codes distinguish own-code findings (1), usage errors (2) and missing API paths or unreadable module JARs (3). Skipped-class summaries include the cause at the normal log level, and handler-isolation advice is omitted when no handler was lost. Which classes and beans load is unchanged. (#892)

Changed ​

  • plugin.yml libraries: now requests net.bytebuddy:byte-buddy:1.18.17 (was 1.18.15), downloaded by Paper on first start; offline servers that pre-fill libraries/ must include the new version. (#894)
  • Module capability versions use only the version core: 2.0-rc1 and 2.0+build.9 both declare 2.0.0, rather than taking digits from their suffixes. (#905)
  • Build and tooling: the binary-compatibility baseline is 6.4.0; offline runtime-API fixture checks have an explicit opt-out, and build helpers and release-asset checks are corrected. Dependency, CI and test-isolation maintenance are included without changing the framework's server floor. (#893, #896)

Known limitations ​

  • The remote-command blocklist is not a sandbox. Checks cover the outer command and synchronous nested framework executors, not every vanilla/Bukkit target reached through wrappers, commands.yml, datapack functions, third-party dispatchers or later ticks. schedule function <pack>:x … remains allowed unless you add schedule yourself. Keep ultipanel.capabilities.commands: false when those paths must be unavailable; do not grant remote commands to untrusted operators.
  • Alias matching follows Bukkit metadata, not a transitive alias graph or vanilla Brigadier redirects. List each spelling explicitly when required. A nested refusal reaches the panel's result only if the wrapper keeps the panel's sender; a wrapper that switches to the console or a player sends it there instead.
  • Command replies are not a secret-redaction boundary. Unregistered arguments and third-party exception text may appear in local records or replies; do not print another user's secrets in a command reply.
  • An expiry received before a player-link handler is registered is still consumed. Register the handler before creating links. Already-running handlers remain in flight across reconnects.
  • YAML defaults still refuse when the block scalar's own value or separators must change, input is invalid, anchors/aliases are present, or an unrelated layout cannot be preserved. The file stays unchanged; add the missing key by hand. The separate single-file crash-safe YAML limits from 6.4.0 remain.
  • PlayerCacheSaver failures still log a warning and remove the cache entry. The registration check adds no retry or persistence guarantee, and shutdown does not save still-online players through this hook.
  • The remaining file/configuration, cloud-client and shutdown limitations are listed in COMPATIBILITY.md.

Deprecated ​

@I18n and @UltiToolsModule(i18n = ...) are deprecated for removal since 6.5.0, not removed. They may be removed as early as 6.6.0 under the existing policy. Since 6.3.0, supported languages have come from resources in the module's own JAR, not these declarations. Remove the declarations and ship the language files instead. (#901, #915)

An accepted module declaring languages without corresponding JAR resources logs one warning listing all missing codes. Both declarations are combined, sorted and deduplicated; complete or empty declarations stay quiet. On-disk files and supported() overrides do not satisfy this resource check, but language selection and existing overrides are unchanged.

Removed APIs ​

6.5.0 removes the eight data-access members deprecated in 6.4.0 and announced for removal in 6.5.0:

  • DataStore#checkOwnership(File, Class)
  • DataStore#checkOwnership(UltiToolsPlugin, Class)
  • JsonStore#getOperator(UltiToolsPlugin, Class)
  • JsonStore#getOperator(File, Class)
  • MysqlDataStore#getOperator(UltiToolsPlugin, Class)
  • MysqlDataStore#getOperator(File, Class)
  • SQLiteDataStore#getOperator(UltiToolsPlugin, Class)
  • SQLiteDataStore#getOperator(File, Class)

Migrate to DataStore#getOperator(DataScope, Class); normal modules should use UltiToolsPlugin#getDataOperator(Class). Third-party callers must update their source and recompile. No shipped UltiKits module used these members.

Breaking changes ​

The eight data-access API removals listed above are binary-incompatible for third-party callers. This release also includes the following behavioral compatibility changes:

  • New or missing-key configurations block execute, return and function remotely by default. Existing lists stay unchanged; registered aliases and synchronous nested framework commands now obey the stricter checks.
  • A player-cache save declaration without a saver now refuses registration instead of silently removing entries without saving.
  • Invalid query condition values now use the same typed refusal across backends. Code relying on empty results, raw exceptions or accidental matches must be adjusted; updateIf's EQUAL-null contract is preserved.

These are behavior changes, not removals of @I18n or its module alias. The existing 6.4.0 API-removal migrations still apply when upgrading from 6.3.x.

Contributors

Released under the MIT License.