Class UltiTools
- All Implemented Interfaces:
Localized,io.papermc.paper.plugin.lifecycle.event.LifecycleEventOwner,net.kyori.adventure.key.Namespaced,org.bukkit.command.CommandExecutor,org.bukkit.command.TabCompleter,org.bukkit.command.TabExecutor,org.bukkit.plugin.Plugin
- Version:
- 6.0.7
- Author:
- wisdommen, qianmo
-
Nested Class Summary
Nested ClassesModifier and TypeClassDescription(package private) static interface -
Field Summary
FieldsModifier and TypeFieldDescriptionprivate CloudFeaturesThe inbound UltiCloud features of this enable (plan 22-21): created once byonEnable()before any module loads, so a module can set its player-link handler while it enables, and stopped byonDisable()after every module has been unloaded.private CommandExecutionManagerprivate final CommandManagerprivate ConfigManagerprivate DataStoreD-03: the ten current blocked commands, shipped as the operator-editable blocklist's default.D-15: the default editable-root set — plugin configs and historical logs.private DependenceManagersprivate ErrorReportCollectorprivate EventBusprivate FileOperationManagerprivate Languageprivate final ListenerManagerprivate LogStreamManagerprivate static final Loggerprivate ModuleFileTransactionsThe module update and removal transactions of this start (#505): applied before the module class loader is built, decided after the modules load.private PanelResponderRegistryThe single-owner registry a module uses to claim a request/response responder for a panel message type the framework itself does not own (WIRE-16, D-26/D-27), reached viagetPanelResponderRegistry()(generated byGetterabove) from both the protocol v2module.requesthandler andPluginManager's two module-unload sites.private PlayerEventManagerprivate PluginManagerprivate RemoteActionLogThe single accessorPluginInitiationUtils's capability gate and every WebSocket manager use to append action-log lines —getRemoteActionLog()(generated byGetterabove).private final SensitiveCommandRegistryThe table of commands whose arguments never reach UltiCloud, reached viagetSensitiveCommandRegistry()(generated byGetterabove).private ServerMonitorManagerprivate ServerPropertiesManagerprivate static final String[]The official languages the framework ships (lang/<code>.jsonin its jar), the codes a custom language name may be based on (#608).private static UltiToolsprivate URLClassLoaderprivate UpdateManagerprivate static final PatternFields inherited from interface com.ultikits.ultitools.interfaces.Localized
LANGUAGE_EXTENSIONS -
Constructor Summary
Constructors -
Method Summary
Modifier and TypeMethodDescriptionprivate static voidaddIfAbsent(ConfigDocument document, org.bukkit.configuration.file.YamlConfiguration server, Map<List<String>, Object> missing, Map<List<String>, List<String>> comments, String dottedPath, Object value, List<String> commentLines) RecordsdottedPath(framework keys, no key contains a dot) as missing when neither the document nor the server's Bukkit reading of the same bytes holds it (a flat dotted key is present only in the latter).private booleanprivate static StringbStatsFileRefusal(File shared) Whysharedmust not be handed to bStats, ornull; reads only.private static org.bukkit.configuration.file.YamlConfigurationbukkitReading(File configFile, String fingerprint) The file as the server reads it - Bukkit'sYamlConfigurationover the same bytes the document was loaded from - ornullwhen those bytes cannot be read again unchanged or Bukkit cannot parse them (nothing is written then; the next start tries again).collectModuleJarUrls(File pluginDir) Scan a directory for module plugin JARs and collect the URLs of the ones that passSecurityPolicy.isValidModuleJar(File)— a JAR is validated before its URL is added, never after.The UltiCloud player-link API (as of 6.3.1): one object for the whole time the framework is enabled.static org.bukkit.configuration.file.YamlConfigurationgetEnv()Retrieves the YAML configuration object containing environment variables.private InputStreamgetFileResource(String filename) Retrieves the input stream for the specified file resource.static UltiToolsReturns the instance of the UltiTools.static ClassLoaderGet the JavaPlugin class loader.private URL[]Get URLs for module plugin JARs in the UltiTools/plugins directory.static intGets the version of UltiTools.Get server jar file URL.Internationalization method that translates the given string based on the current language.private voidprivate booleanprivate voidBuilds the framework's language from thelanguagesetting ofconfig.yml(#608).private voidCreates the panel responder registry, once per enable, before any module loads.private voidprivate voidprivate voidprivate static StringkeyBukkitWouldReadDifferently(File configFile, String fingerprint, org.bukkit.configuration.file.YamlConfiguration before, Consumer<ConfigDocument> insert) Rendersinserton a fresh copy of the file and reads the result the way the server does: the first key whose value Bukkit would read differently thanbeforedoes, ornullwhen every existing key reads the same.(package private) static voidloadModulesThenObserve(ModuleFileTransactions transactions, UltiTools.ModuleLoad load, Supplier<List<UltiToolsPlugin>> loaded) Loads the modules, then decides every update applied at this start from what loaded (#505).private voidAdds the panel keys the framework's ownconfig.ymlis missing, invoked fromonEnable()afterJavaPlugin.saveDefaultConfig()(called inonLoad()) and beforeinitWebSocketManagers().private static booleanmigrateCapabilitiesConfig(File configFile, Logger logger) The migration itself, taking explicit inputs rather than readingthis- the same test-seam shapeparsePluginVersion(String)already uses, so it is callable from a plain JUnit test with no live server.(package private) static URL[]moduleClassPath(ModuleFileTransactions transactions, File modulesFolder, URL serverJar) The module class path of this start: first the recorded module updates are put in place (#505), then the modules folder is listed -- in that order, so the class loader is built over the new JARs and never opens a JAR an update replaces.voidvoidonEnable()voidonLoad()(package private) static intparsePluginVersion(String versionString) private LanguagereadBundledLanguage(String code) Reads the official cataloguelang/<code>.jsonfrom the framework jar as UTF-8.private voidprivate voidreleaseEarlyLogCapture(boolean loginSuccess) Releases the early log capture (#487) when the panel's log stream cannot start soon: without a cloud login no connection opens until an operator logs in, which may never happen.voidReloads the UltiTools plugins by calling the reload method in the PluginManager.Reloads the framework configuration and language, then every module, and returns the summary/ul reloadshows its sender (#509).private void(package private) static MetricsstartMetrics(org.bukkit.plugin.java.JavaPlugin plugin, int serviceId, Logger logger) Starts bStats metrics only when its shared file allows it without being written over.Returns the supported language codes.private static StringtextIfUnchanged(File configFile, String fingerprint) The file's text when its bytes still havefingerprint, elsenull.Methods inherited from class org.bukkit.plugin.java.JavaPlugin
getClassLoader, getCommand, getConfig, getDataFolder, getDefaultBiomeProvider, getDefaultWorldGenerator, getDescription, getFile, getLifecycleManager, getLogger, getPlugin, getPluginLoader, getPluginMeta, getProvidingPlugin, getResource, getServer, getTextResource, init, init, isEnabled, isNaggable, onCommand, onTabComplete, registerCommand, registerCommand, registerCommand, registerCommand, reloadConfig, saveConfig, saveDefaultConfig, saveResource, setEnabled, setNaggable, toStringMethods inherited from class org.bukkit.plugin.PluginBase
equals, getName, hashCode, namespaceMethods inherited from class java.lang.Object
clone, finalize, getClass, notify, notifyAll, wait, wait, waitMethods inherited from interface org.bukkit.plugin.Plugin
getComponentLogger, getDataPath, getLog4JLogger, getSLF4JLogger
-
Field Details
-
VERSION_PATTERN
-
MODULE_SCAN_LOGGER
-
DEFAULT_COMMAND_BLOCKLIST
D-03: the ten current blocked commands, shipped as the operator-editable blocklist's default. -
COMMAND_BLOCKLIST_COMMENT
-
DEFAULT_EDITABLE_ROOTS
D-15: the default editable-root set — plugin configs and historical logs. -
EDITABLE_ROOTS_COMMENT
-
ACTION_LOG_SIZE_COMMENT
-
ACTION_LOG_FILES_COMMENT
-
ultiTools
-
listenerManager
-
commandManager
-
dependenceManagers
-
ultiToolsClassLoader
-
moduleFileTransactions
The module update and removal transactions of this start (#505): applied before the module class loader is built, decided after the modules load. -
SHIPPED_LANGUAGES
The official languages the framework ships (lang/<code>.jsonin its jar), the codes a custom language name may be based on (#608).supported()returns them. -
language
-
pluginManager
-
configManager
-
dataStore
-
serverMonitorManager
-
commandExecutionManager
-
fileOperationManager
-
logStreamManager
-
playerEventManager
-
serverPropertiesManager
-
updateManager
-
eventBus
-
errorReportCollector
-
remoteActionLog
The single accessorPluginInitiationUtils's capability gate and every WebSocket manager use to append action-log lines —getRemoteActionLog()(generated byGetterabove). -
sensitiveCommandRegistry
The table of commands whose arguments never reach UltiCloud, reached viagetSensitiveCommandRegistry()(generated byGetterabove). A module registers its secret-carrying commands and their aliases here from its enable hook; the log stream, error reports and player events consult it on their way to the cloud (seeSensitiveCommandRegistry). Final and created with the plugin instance, before the early log capture and before any module loads, so it exists for every caller whether or not the server is linked; it starts empty and is never replaced, so no registration is lost across a reload.- Since:
- 6.3.1
-
panelResponderRegistry
The single-owner registry a module uses to claim a request/response responder for a panel message type the framework itself does not own (WIRE-16, D-26/D-27), reached viagetPanelResponderRegistry()(generated byGetterabove) from both the protocol v2module.requesthandler andPluginManager's two module-unload sites. Created exactly once per enable, byinitPanelResponderRegistry(), whichonEnable()calls immediately before module loading: a module may claim a responder fromregisterSelf()or a@PostConstructmethod, andPluginManagerattributes that claim to the loading module only while this registry exists. It is never replaced while the framework is enabled, so no claim made during start-up is lost;onDisable()shuts it down. Not gated by anyCapability, since the registry itself holds no data and starts no collection; what flows through it is already gated at the dispatch table it sits behind. -
cloudFeatures
The inbound UltiCloud features of this enable (plan 22-21): created once byonEnable()before any module loads, so a module can set its player-link handler while it enables, and stopped byonDisable()after every module has been unloaded. Reached throughgetCloudPlayerLinks().
-
-
Constructor Details
-
UltiTools
public UltiTools()
-
-
Method Details
-
getCloudPlayerLinks
The UltiCloud player-link API (as of 6.3.1): one object for the whole time the framework is enabled. It works before, during and between connections -- a create while the server is not connected completes withCloudPlayerLinks.Failure.NOT_CONNECTED-- and the handler a module sets on it stays registered across every reconnect until the module removes it or is unloaded.- Returns:
- the player-link API; never
nullonce the framework has enabled - Since:
- 6.3.1
-
getInstance
Returns the instance of the UltiTools.- Returns:
- the instance of the UltiTools
-
getPluginVersion
public static int getPluginVersion()Gets the version of UltiTools.- Returns:
- the version of the UltiTools
-
parsePluginVersion
-
getEnv
public static org.bukkit.configuration.file.YamlConfiguration getEnv()Retrieves the YAML configuration object containing environment variables.- Returns:
- the YAML configuration object
-
onLoad
public void onLoad()- Specified by:
onLoadin interfaceorg.bukkit.plugin.Plugin- Overrides:
onLoadin classorg.bukkit.plugin.java.JavaPlugin
-
onEnable
public void onEnable()- Specified by:
onEnablein interfaceorg.bukkit.plugin.Plugin- Overrides:
onEnablein classorg.bukkit.plugin.java.JavaPlugin
-
releaseEarlyLogCapture
private void releaseEarlyLogCapture(boolean loginSuccess) Releases the early log capture (#487) when the panel's log stream cannot start soon: without a cloud login no connection opens until an operator logs in, which may never happen. Otherwise it is released once its time is up, even on a server that logs nothing more. -
initDependencies
private boolean initDependencies() -
initLanguage
private void initLanguage()Builds the framework's language from thelanguagesetting ofconfig.yml(#608).First writes the official files
lang/en.jsonandlang/zh.jsonto the data folder so an operator can copy them, restoring any that differ from the bundled version (official language files are framework-owned, maintainer decision 2026-10-04; seeOfficialLanguageFiles.syncFrameworkFiles(java.io.File, java.util.List<java.lang.String>, java.lang.ClassLoader, java.util.logging.Logger)). Then resolves the setting: an official code is used as it is; a custom name such aszh-myserverreads the operator'slang/zh-myserver.jsonfrom the data folder when it exists, and takes every message it lacks from the official language its name starts with (zh); a name that starts with no official code uses English for those messages and logs one warning. The official texts always come from the jar, read as UTF-8. The custom file is only ever read. -
readBundledLanguage
Reads the official cataloguelang/<code>.jsonfrom the framework jar as UTF-8.- Returns:
- the catalogue, or an empty language when it is missing or unreadable (the reason is logged)
-
initDataStore
private void initDataStore() -
initPanelResponderRegistry
private void initPanelResponderRegistry()Creates the panel responder registry, once per enable, before any module loads. Nothing else assignspanelResponderRegistry; see the field javadoc. -
initPluginModules
private void initPluginModules() -
loadModulesThenObserve
static void loadModulesThenObserve(ModuleFileTransactions transactions, UltiTools.ModuleLoad load, Supplier<List<UltiToolsPlugin>> loaded) throws IOException Loads the modules, then decides every update applied at this start from what loaded (#505). A load that throws counts as "not loaded" for every one of them.- Parameters:
transactions- this start's transactionsload- the module loading steploaded- the modules loaded, read afterloadreturns- Throws:
IOException- whenloaddoes
-
migrateCapabilitiesConfig
private void migrateCapabilitiesConfig()Adds the panel keys the framework's ownconfig.ymlis missing, invoked fromonEnable()afterJavaPlugin.saveDefaultConfig()(called inonLoad()) and beforeinitWebSocketManagers(). ReloadsJavaPlugin.getConfig()only when the file was written, so every manager constructed afterward sees the merged file; when the write is refused nothing is reloaded and the jar'sconfig.yml, whichJavaPlugin.getConfig()uses as its defaults, answers for the missing keys. -
migrateCapabilitiesConfig
The migration itself, taking explicit inputs rather than readingthis- the same test-seam shapeparsePluginVersion(String)already uses, so it is callable from a plain JUnit test with no live server.Why it cannot overwrite operator content (maintainer decisions of 2026-10-04: "operator-written configuration is never overwritten" and "what code may write, by file type" - a shipped file gets missing keys and the framework's own comments, insert only; UltiKits/UltiTools-Reborn#605). The file is read through
ConfigDocument.load(java.nio.file.Path); only keys absent from it are inserted, each with its comment, and the write goes through the config write gate (OperatorFileWriter) owning exactly those keys: every other byte of the file - values, hex numbers, dotted keys, quoting, layout, comments - must come out byte-identical, and the file must still hold the bytes read. Otherwise nothing is written and the gate logs one WARNING naming the keys and the reason. A key the operator set, even to an explicit null, is never touched. A file that cannot be read or parsed is never written (logged at SEVERE, as before).The server reads this file through Bukkit's
YamlConfiguration, which splits a flat dotted key (ultipanel.capabilities.logs: false) into a path, while the document layer keeps it one whole key. So a key counts as present when either reading holds it, and the edit is first rendered and read the way Bukkit reads it: if any value Bukkit reads for an existing key would change - a nested section added after a flat dotted key replaces it - nothing is written, and one WARNING names the file and that key, never a value (PR #611 local Codex run 2).- Parameters:
configFile- theconfig.ymlfile to migratelogger- where to log a load or write failure- Returns:
trueif the file was written
-
addIfAbsent
private static void addIfAbsent(ConfigDocument document, org.bukkit.configuration.file.YamlConfiguration server, Map<List<String>, Object> missing, Map<List<String>, List<String>> comments, String dottedPath, Object value, List<String> commentLines) RecordsdottedPath(framework keys, no key contains a dot) as missing when neither the document nor the server's Bukkit reading of the same bytes holds it (a flat dotted key is present only in the latter). -
bukkitReading
private static org.bukkit.configuration.file.YamlConfiguration bukkitReading(File configFile, String fingerprint) The file as the server reads it - Bukkit'sYamlConfigurationover the same bytes the document was loaded from - ornullwhen those bytes cannot be read again unchanged or Bukkit cannot parse them (nothing is written then; the next start tries again). -
textIfUnchanged
The file's text when its bytes still havefingerprint, elsenull. -
keyBukkitWouldReadDifferently
private static String keyBukkitWouldReadDifferently(File configFile, String fingerprint, org.bukkit.configuration.file.YamlConfiguration before, Consumer<ConfigDocument> insert) Rendersinserton a fresh copy of the file and reads the result the way the server does: the first key whose value Bukkit would read differently thanbeforedoes, ornullwhen every existing key reads the same. A file that changed meanwhile, or a rendering Bukkit cannot read, names the whole file. -
startMetrics
Starts bStats metrics only when its shared file allows it without being written over.Why it cannot overwrite operator content (maintainer foundational rule of 2026-10-04: a file an operator may edit is never overwritten automatically; inventory B3, UltiKits/UltiTools-Reborn#606). The vendored
Metrics- not edited - saves its defaults overplugins/bStats/config.yml, a file shared by every bStats plugin on the server, whenever it cannot readserverUuid; on a server an unparseable file loads as empty, so its content would be replaced. This guard reads the file without writing it and constructsMetricsonly when the file is absent (bStats then creates it) or already holdsserverUuid(bStats then writes nothing). A file that cannot be read or parsed, or one withoutserverUuid, is left byte-identical: one line names the file and the reason, never a value, and UltiTools' metrics stay off for this run.- Parameters:
plugin- the plugin bStats reports for; its data folder's parent holdsbStats/config.ymlserviceId- the bStats service idlogger- where the refusal line goes- Returns:
- the started metrics, or
nullwhen the shared file was left alone
-
bStatsFileRefusal
Whysharedmust not be handed to bStats, ornull; reads only. -
initWebSocketManagers
private void initWebSocketManagers() -
attemptCloudLogin
private boolean attemptCloudLogin() -
initWebSocket
private void initWebSocket() -
registerCommands
private void registerCommands() -
scheduleStartupMessages
private void scheduleStartupMessages() -
onDisable
public void onDisable()- Specified by:
onDisablein interfaceorg.bukkit.plugin.Plugin- Overrides:
onDisablein classorg.bukkit.plugin.java.JavaPlugin
-
reloadPlugins
Reloads the UltiTools plugins by calling the reload method in the PluginManager.- Throws:
IOException- if an I/O error occurs during the reloading process
-
reloadPluginsAndReport
Reloads the framework configuration and language, then every module, and returns the summary/ul reloadshows its sender (#509). Each module is reloaded in isolation; seePluginManager#reloadAllAndReport(). A changed operator setting the panel mirrors (power actions) is reported to an open UltiCloud connection once, assettings.changed(6.3.1).- Returns:
- the summary lines, already localized
- Throws:
IOException- if an I/O error occurs during the reloading process- Since:
- 6.3.0
-
supported
Returns the supported language codes. -
i18n
Internationalization method that translates the given string based on the current language. If the string is not found in the dictionary, the original string is returned.- Parameters:
str- the string to be translated- Returns:
- the translated string or the original string if not found in the dictionary
-
getFileResource
Retrieves the input stream for the specified file resource.- Parameters:
filename- the name of the file resource- Returns:
- the input stream for the file resource, or null if an I/O error occurs
-
getServerJar
Get server jar file URL.- Returns:
- Server jar file URL
-
getModuleUrls
Get URLs for module plugin JARs in the UltiTools/plugins directory.- Returns:
- Array of URLs for module plugin JARs
-
moduleClassPath
static URL[] moduleClassPath(ModuleFileTransactions transactions, File modulesFolder, URL serverJar) The module class path of this start: first the recorded module updates are put in place (#505), then the modules folder is listed -- in that order, so the class loader is built over the new JARs and never opens a JAR an update replaces.- Parameters:
transactions- this start's transactionsmodulesFolder- the modules folderserverJar- the server JAR, ornull- Returns:
- the URLs to build the module class loader from
-
collectModuleJarUrls
Scan a directory for module plugin JARs and collect the URLs of the ones that passSecurityPolicy.isValidModuleJar(File)— a JAR is validated before its URL is added, never after. A failing JAR is skipped and named in a WARNING; the scan continues and never throws (D-05: module-granularity skip, not a bootstrap abort).Package-private and static so it can be exercised directly by a test against a
@TempDir, without standing up the whole plugin.- Parameters:
pluginDir- directory to scan for module JARs- Returns:
- collected URLs of the JARs that passed validation, empty if
pluginDirisnullor does not exist
-
getJavaPluginClassLoader
Get the JavaPlugin class loader. This ensures all class loading operations use the correct parent class loader.- Returns:
- JavaPlugin class loader
-