-
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.
Migration note. Older EverNifeCore (2.x) exposed a Simple-YAML
Configwrapper and a@Loadable/Salvableannotation contract for custom serialization. On 3.0.1 that wrapper has moved into the external EveryConfig library, and the annotation contract is gone. What replaces it is described in Custom types below - either a one-lineConfigFactorytype registration, or a plain POJO that implementsConfigLifecycle.
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. This is the pattern that replaced the old @Loadable / Salvable
contract; 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.
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