-
Notifications
You must be signed in to change notification settings - Fork 0
v5 Config System
The standalone half lives in standalone-utils; the Spigot half (KamiConfig) lives in spigot-utils.
Both sit on the same abstractions, so using one is much like using the other.
KamiCommon's configs are YAML (snakeyaml, YAML 1.1) and aim to be a drop-in replacement for Bukkit's
YamlConfiguration: the same getString / getInt / getStringList / getConfigurationSection
surface, plus getItemStack and friends on the Spigot side.
The thing Bukkit does not do is preserve comments. Comments in your bundled resource file are copied into the server-side config and kept across reloads, and comments an admin writes against keys of their own survive too.
KamiPlugin creates config.yml for you; reach it with getKamiConfig(). For anything else:
KamiConfig lang = new KamiConfig(this, new File(getDataFolder(), "lang.yml"));That form loads defaults from the jar resource with the same name. To supply them from somewhere else, or to disable them:
// defaults from a differently-named resource
new KamiConfig(this, file, () -> getResource("defaults/lang.yml"));
// no defaults at all
new KamiConfig(this, file, null);KamiConfigExt is the same thing with extra helpers, and takes the same constructors.
⚠️ Migrating from v4: v4 had two three-argument forms, one taking aboolean addDefaultsand one already taking aSupplier<InputStream>. If you passed a supplier, nothing changes. The boolean is now a@Nullable Supplier<InputStream>, and "add defaults" now means "the supplier is not null".new KamiConfig(plugin, file, false)becomesnew KamiConfig(plugin, file, null), andnew KamiConfig(plugin, file, true)becomesnew KamiConfig(plugin, file).
StandaloneConfig config = new StandaloneConfig(logger, new File("config.yml"));A LoggerService is required. Create one if you have none.
⚠️ The logger you pass decides which jar your defaults come from. Bundled defaults are resolved throughlogger.getClass().getResourceAsStream(...), so aLoggerServicefrom a different jar reads that jar's resources, or none. Pass a logger from the jar that carries your default config.
config.save(); // writes current data to the source
config.reload(); // re-reads, discarding in-memory changes, and notifies observerssave() buffers the whole document and only marks the config clean if the write succeeds, so a failed
write no longer leaves a dirty config looking saved.
A config no longer has to be a file. ConfigSource is the abstraction:
| implementation | writable | use |
|---|---|---|
FileConfigSource |
yes | the normal case, a file on disk |
StringConfigSource |
no | a YAML document held in memory |
ConfigSource source = new StringConfigSource("greeting: hello", "in-memory");
StandaloneConfig config = new StandaloneConfig(logger, source, null);save() on a read-only source is a no-op returning false rather than an exception. A source with no
content starts from an empty document rather than failing.
Implement ConfigObserver and register it against anything that is an ObservableConfig, which
includes KamiConfig, KamiConfigExt, KamiPlugin, AbstractSubsystem and StandaloneConfig.
config.registerConfigObserver(cfg -> {
// called immediately on registration, and on every reload afterwards
});CachedConfig is the framework version of that pattern: subclass it, put your parsing in
loadCache(T config), and call register(). Your cached fields are then refreshed on every reload.
public class Lang extends CachedConfig<KamiConfig> {
public static String prefix;
public Lang(KamiConfig config) { super(config); }
@Override
public void loadCache(KamiConfig config) {
prefix = config.getString("prefix");
}
}
⚠️ Migrating from v4:registerObserver()is nowregister(), andloadConfig(T)is nowloadCache(T).ICachedConfigandCachedModuleConfigwere removed;CachedConfigis an abstract class implementingConfigObserver.
defaultCommentsOverwrite on AbstractConfig defaults to true, meaning a comment already in the
server-side file wins over the one in your bundled resource. Set it to false and the bundled comment
takes priority instead.
The flag is read while defaults are applied, which happens inside the constructor, so setting it on a
config you already built only affects later reload() calls.
Support for YAML sequences is early. Saving is unaffected, but reading a sequence whose entries are
mappings through get, getString and friends throws IllegalStateException. Use
getConfigurationSequence for those. Sequences of plain scalars read normally.
Because defaults are applied in the constructor, building a config with defaults enabled over a file containing a sequence of mappings throws out of the constructor.
If you subclassed AbstractYamlHandler or YamlHandlerStandalone, several things moved. The two that
break quietly:
-
AbstractYamlHandlergained an abstractwarn(String), so every existing subclass stops compiling until it implements it. -
AbstractConfig#getFile()becamegetSource(), returning aConfigSource, andMemorySectionMethods#save(File)becamesave(ConfigSource). Neither is on the handler itself; what breaks a handler subclass isnewConfig's second parameter changing type, and the constructor arity.
loadConfig(boolean, Supplier) is now loadConfig() with no arguments, the defaults supplier having
moved to the constructor, and getIS() is gone. loadConfig also no longer calls System.exit(0) when
it cannot create the file.
Setup
Spigot
Text
Data
Migration
Other versions