Typed YAML-first configuration for modern Paper plugins.
DaisyConfig gives you:
- explicit codecs instead of ad hoc
getString(...)trees - reload-safe handles that keep the last good runtime value live
- DaisySeries-backed config parsing for materials, sounds, item flags, enchantments, and potions
- DaisyCore-ready text config adapters through
DaisyTextSource - managed YAML files with versioning, missing-key merge, and ordered migrations
- first-class module bundles for
settings.yml+lang.yml
Phase 3 ships these modules:
config-baseconfig-yamlconfig-managedconfig-modulesconfig-daisycoreconfig-allexample-plugin
repositories {
mavenCentral()
maven("https://repo.papermc.io/repository/maven-public/")
maven("https://jitpack.io")
}
dependencies {
implementation("cat.daisy:config-all:0.2.0")
}Use config-all as the normal install unless you deliberately want a smaller module surface.
The low-level YAML API remains available for simple files:
val handle = plugin.yamlConfigHandle("settings.yml", settingsCodec)
val result = DaisyYaml.load(File(dataFolder, "settings.yml"), settingsCodec)Bundle handles also still exist:
val bundle =
plugin.yamlConfigBundleHandle {
FeatureBundle(
settings = file("settings.yml", settingsCodec),
lang = file("lang.yml", daisyTextConfigCodec()),
)
}Managed YAML adds file lifecycle, version-aware migration, and missing-key merge on top of the unmanaged layer.
val settingsFile =
DaisyManagedYamlFile(
id = "settings",
path = "settings.yml",
codec = settingsCodec,
currentVersion = 2,
migrations = listOf(
DaisyYamlMigrations.move(1, 2, "spawn-delay", "spawn.delay"),
),
)
val handle = plugin.managedYamlConfigHandle(settingsFile)
val config = handle.currentManaged lifecycle:
- create missing files from bundled defaults
- merge missing default keys without overwriting user values
- treat missing
config_versionas version1 - run ordered migrations up to
currentVersion - write changes to disk only when something actually changed
- decode into a typed runtime value
If reload fails, the previous typed value stays live.
Managed handles return a report that explains what changed:
when (val result = handle.migrate()) {
is DaisyManagedReloadResult.Success -> {
logger.info("Merged keys: ${result.report.mergedMissingKeys}")
logger.info("Renamed keys: ${result.report.renamedKeys}")
}
is DaisyManagedReloadResult.Failure -> {
result.errors.forEach { error ->
logger.warning("${error.path}: ${error.message}")
}
}
}Phase 3 includes helper migrations for common YAML changes:
DaisyYamlMigrations.rename(...)DaisyYamlMigrations.move(...)DaisyYamlMigrations.remove(...)DaisyYamlMigrations.setDefault(...)
Migration chains must be ordered one version at a time. Skipped or duplicate chains are rejected.
config-modules gives you first-class module conventions for:
modules/<category>/<module>/settings.ymlmodules/<category>/<module>/lang.yml
val registry =
DaisyModules.load(plugin) {
module(
DaisyModuleDefinition(
category = "commands",
module = "spawn",
settings =
DaisyManagedYamlFile(
id = "commands/spawn/settings",
path = "modules/commands/spawn/settings.yml",
codec = spawnSettingsCodec,
currentVersion = 2,
migrations = listOf(
DaisyYamlMigrations.move(1, 2, "spawn-delay", "spawn.delay"),
),
),
lang =
DaisyManagedYamlFile(
id = "commands/spawn/lang",
path = "modules/commands/spawn/lang.yml",
codec = daisyTextConfigCodec(),
),
),
)
}
val spawn = registry.require<SpawnSettings>("commands", "spawn")
val delay = spawn.current.settings.delaySeconds
val textSource = spawn.textSourceModule handles preserve bundle safety:
- typed settings and lang reload together
- the last good module value stays live if reload fails
- migration reports are aggregated across both files
config-daisycore stays focused on the text bridge:
DaisyTextConfigdaisyTextConfigCodec()asDaisyTextSource()
DaisyConfig stores and loads data. DaisyCore still owns runtime rendering.
DaisyConfig does not execute placeholders directly.
Phase 3 is designed to replace plugin-local config services that manually manage:
settings.ymllang.ymlmodules/<category>/<module>/...saveResource(...)- version bumps
- missing-key merge logic
That workflow now lives in DaisyConfig itself.
See example-plugin for a managed module example with:
commands/spawncommands/warpguis/store- startup creation from bundled defaults
- managed reload and migration reporting
- DaisySeries-backed typed settings
- DaisyCore-facing module text sources
Recommended JDK: Java 21
Useful commands:
./gradlew.bat --no-daemon test
./gradlew.bat --no-daemon :example-plugin:compileKotlinMIT