-
Notifications
You must be signed in to change notification settings - Fork 7
Configuration
EverNifeCore reads and writes YAML configuration through the
EveryConfig library
(br.com.finalcraft.everyconfig.*). A config file is a Config handle: you open it, seed defaults with
inline comments, read and write typed values by path, and save. The framework wraps that library with
ConfigFactory, which bakes in a shared, type-aware codec so that framework types
(ItemStack, Location, FancyText, cooldowns, the vector families, ...) cross to and from config for
free. This page covers the framework-side surface; for the library itself (binding model, comment
engine, async back-store), the EveryConfig wiki is the
recommended deep dive.
ConfigManager holds the two core files (config.yml and Cooldowns.yml). Your own plugin opens its
files through ConfigFactory. The plugin-scoped overload resolves the file under your plugin's data
folder and stamps the standard FinalCraft header:
import br.com.finalcraft.evernifecore.config.ConfigFactory;
import br.com.finalcraft.everyconfig.config.Config;
// Resolved under this plugin's data folder, header seeded automatically.
Config config = ConfigFactory.open(ecPluginData, "config.yml");There are also plain overloads that take a File, Path or file-name String (no data-folder
relocation, no header), plus overloads that accept an explicit Codec or a BackStore.Durability. The
codec is inferred from the file extension - .yml, .toml, .json and .jsonc are all supported by
the shared type-aware registry.
Values are addressed by a dotted path. The most useful method is getOrSetValueIfAbsent: it returns
the stored value, or writes your default (with optional comment lines) the first time the key is missing.
This is how every core setting is declared - see ECSettings for real examples:
String zoneId = config.getOrSetValueIfAbsent(
"Settings.Time.ZONE_ID_OF_DAY_OF_TODAY",
ZoneId.systemDefault().getId(),
"The timezone used for EverNifeCore and its sub-plugins!",
"You can use GMT zones, for example 'GMT-3' or 'GMT+8'."
);
int refresh = config.getOrSetValueIfAbsent("Settings.PageViewers.REFRESH_TIME", 5);
boolean warn = config.getOrSetValueIfAbsent("Settings.Warn.enabled", true);Other common accessors mirror the usual config surface:
| Call | Purpose |
|---|---|
getValue(path, Type.class) |
Read a typed value (any registered type). |
setValue(path, value) |
Write a value; null removes the key. |
getInt / getBoolean / getString(path[, default])
|
Primitive/string reads with optional default. |
getKeys(path) |
The child keys under a section (for iterating a map-shaped node). |
getFile() |
The backing File, or null for an in-memory config. |
Because the codec is type-aware, a registered type works everywhere the same way - as a solo value, a field of another POJO, a map value, or a list element:
config.setValue("spawn.icon", myItemStack); // ItemStack -> config
ItemStack icon = config.getValue("spawn.icon", ItemStack.class);Comments come from two places. Inline comments are the trailing varargs on getOrSetValueIfAbsent
(shown above). The banner at the top of the file is the header; the plugin-scoped open overload
seeds the standard FinalCraft banner, and you can override it with config.setHeader(String[] lines).
Saving is explicit. There is no periodic auto-save thread on this branch - a config is written when you ask it to be:
-
config.save()- save synchronously. -
config.saveAsync()- hand the write to EveryConfig's async back-store and return immediately. This is what the framework uses for hot paths (for example the cooldown file writes withsaveAsync()).
A common seed-then-save pattern, taken from ECSettings.initialize(), only writes when defaults were
actually added:
// ... declare every setting with getOrSetValueIfAbsent ...
if (config.hasNewSeededDefaults()) {
config.save();
config.clearNewSeededDefaults();
}hasNewSeededDefaults() reports whether any getOrSetValueIfAbsent call inserted a missing default
during this pass, so a first boot (or a version that adds new keys) writes the file exactly once and an
unchanged config is never rewritten.
EveryConfig is Jackson-first, so "teaching" the framework a new type is one serializer/deserializer pair
registered on ConfigFactory. Register it once during your bootstrap and the whole config surface
composes for free. There are three registration styles plus a list-element add-on:
import br.com.finalcraft.evernifecore.config.ConfigFactory;
// Object-shaped: the value becomes a nested map (nested registered types recurse automatically).
ConfigFactory.register(MyData.class).asMap(MyData::toMap, MyData::fromMap);
// Scalar-shaped: the value becomes a single string, and is usable as a map key.
ConfigFactory.register(MyId.class).asString(MyId::toString, MyId::parse);
// Full control: hand Jackson the serializer/deserializer directly.
ConfigFactory.register(MyThing.class).jackson(mySerializer, myDeserializer);Registration is allowed at any time. A late registration invalidates the shared codec, which is rebuilt
lazily on the next open (copy-on-write): configs opened afterwards see the new type; a config already
open keeps the codec it captured. The framework registers its built-ins this way - the position/vector
family, Cooldown/GenericCooldown and FancyText in ECBuiltinTypes, and the Bukkit types
(ItemStack, Location, GUI LayoutIcon) during the platform's registerConfigTypes() hook.
For a value you own end to end, the simplest path is a plain Jackson bean (no-arg constructor, accessors)
that optionally implements ConfigLifecycle (br.com.finalcraft.everyconfig.binding.ConfigLifecycle)
to run postLoad / postSave logic; FCPlayerInventory is a live example. Combined with @JsonAnyGetter, nested bound entities
compose without any central registration.
The same type authority also crosses into the EveryDatabase storage layer, so a type that serializes to a config file also serializes into a player-data section unchanged - see PlayerData and PDSections and Storage Backends.
-
Scheduler and Threading -
saveAsyncruns on EveryConfig's back-store; the framework's own scheduling primitives live here. -
Localization - locale files are configs too (
@FCLocale). -
Items and NBT -
ItemStackis one of the registered config types. - PlayerData and PDSections - the same Jackson type authority, persisted via EveryDatabase.
-
Storage Backends -
storage.yml, the one config file the framework generates for the admin rather than for a plugin.
EverNifeCore · Home · made by Petrus Pradella
Getting Started
Commands & Text
Player Data & Storage
- PlayerData & PDSections
- Accounts
- Storage Backends
- Inline Backends for Plugins
- Legacy Data Migration
- Cooldowns
Config & Minecraft Systems
- Configuration
- Scheduler & Threading
- Items & NBT
- GUI Framework
- Integrations
- Economy
- Version Compatibility
Architecture & Reference