Example Plugin Walkthrough
Example Plugin Walkthrough
Section titled “Example Plugin Walkthrough”This page mirrors the current DaisyConfig example-plugin instead of inventing a second docs-only example.
Use it when you want to see the full Phase 3 shape in one place:
- managed YAML files
- module bundles
config_version- migrations
reloadAll()andmigrateAll()- DaisyCore text consumption through
textSource
What the example plugin contains
Section titled “What the example plugin contains”The example plugin loads three managed module bundles:
modules/commands/spawnmodules/commands/warpmodules/guis/store
Each module has:
settings.ymllang.yml
That means the runtime shape is:
modules/ commands/ spawn/ settings.yml lang.yml warp/ settings.yml lang.yml guis/ store/ settings.yml lang.ymlThe registry
Section titled “The registry”The example plugin builds one registry and lets DaisyConfig own module lifecycle from there.
class ExampleModuleRegistry( plugin: JavaPlugin,) { private val registry: DaisyModuleRegistry = DaisyModules.load(plugin) { module( DaisyModuleDefinition( category = "commands", module = "spawn", settings = DaisyManagedYamlFile( id = "commands/spawn/settings", path = "modules/commands/spawn/settings.yml", codec = spawnModuleConfigCodec, currentVersion = 2, migrations = listOf( DaisyYamlMigrations.move(1, 2, "spawn-delay", "spawn.delay"), ), ), lang = langFile("commands", "spawn"), ), ) module( DaisyModuleDefinition( category = "commands", module = "warp", settings = DaisyManagedYamlFile( id = "commands/warp/settings", path = "modules/commands/warp/settings.yml", codec = warpModuleConfigCodec, currentVersion = 2, ), lang = langFile("commands", "warp"), ), ) module( DaisyModuleDefinition( category = "guis", module = "store", settings = DaisyManagedYamlFile( id = "guis/store/settings", path = "modules/guis/store/settings.yml", codec = storeModuleConfigCodec, ), lang = langFile("guis", "store"), ), ) }}Why these three modules exist
Section titled “Why these three modules exist”They demonstrate three different Phase 3 cases:
commands/spawn: migration from an old key shapecommands/warp: managed versioning without a renameguis/store: DaisySeries-backed values in a nested GUI config
The migration example
Section titled “The migration example”commands/spawn/settings.yml is the migration case.
Current bundled shape:
config_version: 2spawn: delay: 3 bypass_permission: example.spawn.bypassThe managed file definition still accepts the old spawn-delay key through an ordered migration:
DaisyManagedYamlFile( id = "commands/spawn/settings", path = "modules/commands/spawn/settings.yml", codec = spawnModuleConfigCodec, currentVersion = 2, migrations = listOf( DaisyYamlMigrations.move(1, 2, "spawn-delay", "spawn.delay"), ),)If an older disk file still has:
config_version: 1spawn-delay: 3then managed load will:
- treat the disk file as version
1 - move
spawn-delaytospawn.delay - merge missing defaults such as
spawn.bypass_permission - write
config_version: 2 - decode the new typed runtime value
What one module handle gives you
Section titled “What one module handle gives you”val spawn = registry.require<SpawnModuleConfig>("commands", "spawn")
val settings = spawn.current.settingsval lang = spawn.current.langval textSource = spawn.textSourceThat gives one handle for:
- typed settings data
- optional lang data
- DaisyCore-facing text access
- one reload/migrate boundary
How the plugin uses it at runtime
Section titled “How the plugin uses it at runtime”The plugin logs settings from all three modules during startup:
logger.info( "Loaded DaisyConfig managed modules: " + "spawn=${modules.spawn.current.settings.delaySeconds}s, " + "warp=${modules.warp.current.settings.maxWarps} max, " + "storeRows=${modules.store.current.settings.rows}",)The command example also renders lang data through textSource instead of making DaisyConfig own rendering:
val settings = plugin.modules.spawn.current.settingsval message = plugin.modules.spawn.textSource.text("messages.spawn.ready") ?.replace("%delay%", settings.delaySeconds.toString()) ?: "<green>Spawn ready in ${settings.delaySeconds}s.</green>"reply(message)That boundary matters:
- DaisyConfig stores and reloads the lang file
- DaisyCore still owns the runtime reply/render path
Reload and migrate
Section titled “Reload and migrate”The example plugin exposes both flows.
when (val result = plugin.modules.reloadAll()) { is DaisyManagedBundleReloadResult.Success -> { reply("<green>Reloaded ${result.value.size} managed module bundles.</green>") result.reports.forEachIndexed { index, report -> reply("<gray>#${index + 1} ${formatReport(report)}</gray>") } } is DaisyManagedBundleReloadResult.Failure -> { reply("<red>Reload failed.</red>") result.errors.forEach { error -> reply("<gray>${error.path}: ${error.message}</gray>") } }}when (val result = plugin.modules.migrateAll()) { is DaisyManagedBundleReloadResult.Success -> { reply("<green>Managed migration completed.</green>") result.reports.forEachIndexed { index, report -> reply("<gray>#${index + 1} ${formatReport(report)}</gray>") } } is DaisyManagedBundleReloadResult.Failure -> { reply("<red>Migration failed.</red>") result.errors.forEach { error -> reply("<gray>${error.path}: ${error.message}</gray>") } }}What this walkthrough is trying to teach
Section titled “What this walkthrough is trying to teach”Start simple when one file is enough. Move to this shape when your plugin really has:
- many config files
- versioning pressure
settings.ymlpluslang.ymlpairs- runtime reload requirements
- migration concerns you do not want to reimplement locally
Where to go next
Section titled “Where to go next”- Start from the lower-level guide: Managed YAML Files
- Learn the bundle model: Module Bundles
- See the handle/result surface: Module Registry
- Migrate existing services: From Plugin-Local Config Loaders