Configuration and messages
Keystone separates the file on disk from the object the rest of your plugin reads. ManagedConfig
owns a YAML file, your parser turns it into an immutable settings object, and Snapshot<T> swaps
that object only after a complete load succeeds.
Loading a managed YAML file
Put the default file in src/main/resources, then request it from the handle:
ManagedConfig source = keystone.config("config.yml");
String locale = source.string("locale", "en_US");
boolean debug = source.bool("debug", false);
int limit = source.integer("limits.entries", 100);
List<String> worlds = source.stringList("enabled-worlds");
ConfigurationSection storage = source.section("storage");
On first use, Keystone writes the bundled resource into the plugin data folder. On later loads it keeps the administrator's values and fills missing paths from the bundled defaults. New settings therefore receive their defaults without requiring the user to delete or replace the file.
reload()Re-read the file and merge missing bundled defaults. This changes the
ManagedConfig; it does not validate or replace your own parsed settings object.save()Write the current YAML to disk. Use this for commands that intentionally edit configuration.
yaml()Access Bukkit's
YamlConfigurationwhen the typed convenience methods are not enough.file()The resolved file inside the plugin's data folder, useful in error messages.
Publish a complete snapshot
private final Snapshot<Settings> settings = new Snapshot<>(Settings.defaults());
private boolean reloadSettings() {
ManagedConfig source = keystone.config("config.yml");
source.reload();
LoadReport report = new LoadReport();
Settings candidate = parseSettings(source, report);
report.print(getLogger(), "settings");
if (report.hasErrors()) {
return false;
}
settings.set(candidate);
return true;
}
Readers call settings.get(). Because Snapshot replaces one immutable value through a volatile
reference, concurrent readers see the old settings or the new settings—never half of each. Keep the
old snapshot active when validation reports errors.
LoadReport distinguishes three outcomes:
error: that definition cannot work and should block publishing the candidate.warn: it loaded, but the author should review what was changed or ignored.downgrade: the server lacks a capability and a documented fallback was chosen.
messages.yml
keystone.messages() manages messages.yml and creates MessageService lazily. A minimal file is:
prefix: "<dark_gray>[<gold>Example</gold>]</dark_gray> "
usage: "<gray>Use <yellow>/example help</yellow>."
item-renamed: "<gray>Renamed the item to <white><name></white>."
MessageService messages = keystone.messages();
messages.send(player, "item-renamed", MessageService.value("name", playerInput));
messages.sendActionBar(player, "working");
Component component = messages.get("usage");
send includes the configured prefix; get returns the message itself. Use prefixed when you
need the combined component without sending it.
Choose the right placeholder
value(key, text)safe textInsert text verbatim. MiniMessage tags inside the value are not parsed. This is the default for player input, names, ids and the
Map<String, String>overload.parsed(key, miniMessage)trusted markupParse the replacement as MiniMessage. Use only for text controlled by your plugin or by a trusted server administrator.
component(key, component)componentInsert a component already constructed by trusted code.
KeystoneText.parse() accepts trusted MiniMessage. parseUntrusted() uses the restricted safe tag
set, escape() neutralises markup characters, and plain() removes formatting from a component.
Reloading messages
messages.reload() re-reads messages.yml. If your plugin exposes one reload command, reload its
managed files, validate and publish its settings snapshot, then reload messages. Report a failure
without discarding the known-good runtime state.
Missing message keys render as <red>Missing message: <key> instead of disappearing. Keep that
visible marker during development, and test both trusted admin-authored markup and a hostile
player-supplied value such as a MiniMessage click tag to confirm value(...) remains unparsed.