-
Notifications
You must be signed in to change notification settings - Fork 1
docs(config): typed binding, shape mismatches, dotted map keys and language-key comments, as of v6.3.0 #105
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: alpha
Are you sure you want to change the base?
Changes from all commits
9acc200
d0ccc3d
c0d1d03
94f0ca4
786d305
c8129a8
a3f2b1e
610b67b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -75,6 +75,8 @@ configuration item in the configuration file. | |
|
|
||
| The `comment` attribute is used to specify the comment of this configuration item. | ||
|
|
||
| As of v6.3.0, a `comment` that is exactly one language key, such as `comment = "{config.limit}"`, is resolved from your module's language catalogue (`lang/en.json`, `lang/zh.json`, or the `.yml` equivalents) in the server's current `language`, so one entry can ship its comment in every language the module supports. The framework writes that text on every write of the file, keys the operator already has included: the first-boot defaults write, a save, the shutdown save, a panel write, and the first start after an upgrade or a `language` switch. An operator's hand-written comment on such an entry is replaced, values keep their meaning, and a start whose comments already match writes nothing. The rewrite changes more than the comment lines: like every framework write, it renders the whole file again, so quotes the operator added are dropped, an inline list `[a, b]` becomes a multi-line list, `yes` becomes `true`, `1.50` becomes `1.5`, and a comment written beside a list item is lost. Values keep their meaning. This happens once on the first start after an upgrade and once after a `language` switch. A catalogue text with line breaks becomes several comment lines. A key missing from the catalogue is written as the token itself, with one warning naming the module, file, entry and key. Any other comment, including one that only contains a placeholder such as `{player}` inside other text, is written as it is, and only when the key is first added. | ||
|
|
||
| The `parser` attribute is used to specify the parser of this configuration item. The parser is used to convert the | ||
| object in the configuration file to the type of the configuration item. The default parser is `DefaultConfigParser` | ||
| , it can handle most of the case but not all. If you need to parse a more complex object, you can create a class that | ||
|
|
@@ -90,7 +92,15 @@ The snippet below only illustrates its logic, import the framework class shown a | |
|
|
||
| YAML stores a whole number such as `1800` as an integer. As of v6.3.0, a boxed `Long`, `Float` or `Double` field loads such a value, the same way a primitive `long`, `float` or `double` field does. Earlier versions could not set an integer into a boxed field of another numeric type, so such a field loaded on the first boot, when its default was written, and failed on every later boot and reload. Only widening conversions are applied, so a boxed field accepts exactly what its primitive type accepts. | ||
|
|
||
| A decimal such as `0.5` is read as a `Double`, and narrowing it into a `float` or `Float` field is not supported ([#534](https://github.com/UltiKits/UltiTools-Reborn/issues/534)). Use `double` or `Double` for a value that may contain a decimal point. | ||
| A decimal such as `0.5` is read as a `Double`. As of v6.3.0, a `float` or `Float` field (or a `Float` list element) accepts it when the float nearest to it prints back as the same decimal, so `0.1`, `0.3` and `1.5` load, and a float the framework wrote always reads back ([#534](https://github.com/UltiKits/UltiTools-Reborn/issues/534)). A value with more digits than a float holds, such as `0.123456789`, keeps the default with a warning; use `double` or `Double` when you need that precision. | ||
|
|
||
| #### Collections, maps and wrongly shaped values | ||
|
|
||
| As of v6.3.0, a value is bound to the type its field declares. A `List<Integer>` receives `Integer`s, and a `Set`, `Long`, `Double`, `Boolean` or enum element type converts the same way; map keys and values are converted to the map's declared types, and a map whose values are your own class still receives the raw maps, as before. A quoted number that an earlier version wrote into the file, such as `'30'`, loads as the number. An element that cannot be converted, such as `abc` in a `List<Integer>`, is skipped with one warning naming the file, the key with the element's position, the value and the declared type, and the rest of the configuration loads; an empty list item is skipped the same way. `List`, `Set`, `SortedSet`, `Queue`, `EnumSet`, `Map`, `SortedMap`, `ConcurrentMap` and `EnumMap` fields are all supported, and a custom `parser`, including one that extends `DefaultConfigParser`, reads and writes exactly as before. Earlier versions bound every list element as its text, so a typed lookup such as `contains(30)` never matched. When a map or an object holds a `null` value, `save()` now leaves that value out of the file and logs one warning naming the file, the entry and the value's nested path; earlier versions stopped the save with a `NullPointerException` and left the file unchanged. | ||
|
|
||
| A value whose shape does not fit its field, such as a list or a plain value where a `Map` is declared, text in a number field, or a date such as `2024-01-01` in a text field, leaves the field at its declared default and logs one warning naming the file, the key, the declared type and what the file holds. The module still loads, and the operator's file is not rewritten. Earlier versions threw from the configuration load and the module did not start. A warning never prints a value when a key in it, or its own key, suggests a secret, such as `password` or `token`. | ||
|
|
||
| **Do not put a dot in a map key.** The configuration file uses `.` as its path separator, so in a map the file stores as a section - a map field, a map nested in one, or a map inside an object stored in one - a key such as `my.rule` is split into nested levels (`my` → `rule`) when it is written and when the file is loaded, and quoting it in YAML (`"my.rule":`) does not help. This is unchanged from earlier versions, and the framework neither refuses such a key nor warns about it, so use `-` or `_` in these keys, both in your declared defaults and in keys your module builds at run time, and tell operators the same. A map that is an element of a list is plain data the file keeps whole, so a dotted key there is kept. | ||
|
|
||
| As of v6.3.0, an `int`, `long`, `Integer` or `Long` field can also drive a task interval or a command cooldown: see [Config-Bound Timing](/guide/advanced/scheduled-tasks#config-bound-timing) for `@Scheduled` and [Binding the cooldown to a config key](/guide/essentials/cmd-executor#binding-the-cooldown-to-a-config-key) for `@CmdCD`. The config class must be registered exactly once for the module, so a directory `@ConfigEntity` cannot be bound. Do not also put a [`@Range`](/guide/advanced/config-validation) on a bound field: the binding enforces its own range, and a `@Range` violation during `/ul reload` aborts the rest of the module's reload ([#509](https://github.com/UltiKits/UltiTools-Reborn/issues/509)). | ||
|
|
||
|
|
@@ -174,7 +184,7 @@ You don't need to worry about the loading and saving of configuration files, Ult | |
| automatically. | ||
|
|
||
| ::: info Comments, as of v6.3.0 | ||
| Bukkit preserves existing comments across a save, and UltiTools sets `options().parseComments(true)` explicitly rather than relying on the default. A key added for the first time also gets its `@ConfigEntry(comment)` written alongside it; a key the operator already has is left untouched. | ||
| Bukkit preserves existing comments across a save, and UltiTools sets `options().parseComments(true)` explicitly rather than relying on the default. A key added for the first time also gets its `@ConfigEntry(comment)` written alongside it; a key the operator already has keeps its own comment unless that comment is a language key (see `@ConfigEntry` above). | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
When a key already exists, replacement is controlled by whether its declared Useful? React with 👍 / 👎. |
||
| ::: | ||
|
|
||
| One cosmetic side effect: SnakeYAML re-emits a double-quoted string value as single-quoted on save. The value itself does not change, only its quoting style. As of v6.3.0 this happens only when a file is actually saved: a configuration nothing changed is not rewritten on disable. | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When the file contains a double-quoted string, this sentence says the operator-added quotes are dropped, but the unchanged saving section at line 190 says SnakeYAML re-emits the value with single quotes. Those descriptions are mutually inconsistent and give operators conflicting expectations about the whole-file diff caused by translating a comment; clarify whether quoting is removed or merely changed.
Useful? React with 👍 / 👎.