From 9acc200ffa7629a9a18635a80c5b8a1ae49740b0 Mon Sep 17 00:00:00 2001 From: Ling Bao Date: Tue, 29 Sep 2026 23:32:59 +1000 Subject: [PATCH 1/8] docs(config): typed binding, shape mismatches, dotted map keys and language-key comments, as of v6.3.0 Companion to UltiKits/UltiTools-Reborn fix/p17-fu-config (#523, #526, #553, #542). Prose only, both languages; no example reference to unreleased API. Co-Authored-By: Claude Opus 5.5 (1M context) --- docs/src/guide/essentials/config-file.md | 12 +++++++++++- docs/src/zh/guide/essentials/config-file.md | 12 +++++++++++- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/docs/src/guide/essentials/config-file.md b/docs/src/guide/essentials/config-file.md index 7be3e14..d83a6fa 100644 --- a/docs/src/guide/essentials/config-file.md +++ b/docs/src/guide/essentials/config-file.md @@ -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. Only those comment lines change; an operator's hand-written comment on such an entry is replaced, and a start whose comments already match writes nothing. 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 @@ -92,6 +94,14 @@ YAML stores a whole number such as `1800` as an integer. As of v6.3.0, a boxed ` 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. +#### Collections, maps and wrongly shaped values + +As of v6.3.0, a value is bound to the type its field declares. A `List` 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`, 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. Earlier versions bound every list element as its text, so a typed lookup such as `contains(30)` never matched. + +A value whose shape does not fit its field, such as a list or a plain value where a `Map` is declared, or text in a number 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 the value of a key whose name suggests a secret, such as `password` or `token`. + +A map key that contains a dot, such as `my.rule`, is saved and read back as one key. Earlier versions saved it as the nested path `my: {rule: ...}` and read it back as `my`. Paths you read through `getConfig()` resolve as before. + 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)). #### @Getter and @Setter @@ -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). ::: 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. diff --git a/docs/src/zh/guide/essentials/config-file.md b/docs/src/zh/guide/essentials/config-file.md index 81c2a98..028e476 100644 --- a/docs/src/zh/guide/essentials/config-file.md +++ b/docs/src/zh/guide/essentials/config-file.md @@ -64,6 +64,8 @@ TestConfig config = BasicFunctions.getInstance().getConfig("test/test1.yml", Tes `comment` 属性用于指定该配置项的注释; +自 v6.3.0 起,如果 `comment` 恰好是一个语言键,例如 `comment = "{config.limit}"`,框架会按服务器当前的 `language` 从模块的语言文件(`lang/en.json`、`lang/zh.json` 或对应的 `.yml`)中取出文字作为注释,这样同一个配置项就能以模块支持的每种语言提供注释。框架每次写这个配置文件时都会写入这段文字,包括服主文件里已有的键:首次启动写入默认值、保存、关服保存、面板写入,以及升级后或切换 `language` 后的第一次启动。只有这些注释行会变;服主在这类配置项上手写的注释会被替换,注释已经一致时启动不会写文件。语言文件里带换行的文字会写成多行注释。语言文件里没有这个键时,写入的就是这个键本身,并记一条警告,写明模块、文件、配置项和键名。其他注释(包括只是在文字中含有 `{player}` 这类占位符的注释)按原样写入,并且只在该键首次加入文件时写入。 + `parser` 属性用于指定该配置项的解析器。解析器用于将配置文件中的对象转换为配置项的类型。默认的解析器是 `DefaultConfigParser` , 它可以处理大多数情况,但并不是所有情况。如果你需要解析一个更复杂的对象,你可以创建一个继承 `ConfigParser` 类的类,并在 `parser` 属性中指定它。 @@ -78,6 +80,14 @@ YAML 会把 `1800` 这样的整数读成整数类型。自 v6.3.0 起,包装 `0.5` 这样的小数会被读成 `Double`,目前不支持把它收窄写入 `float` 或 `Float` 字段([#534](https://github.com/UltiKits/UltiTools-Reborn/issues/534))。可能包含小数的值请使用 `double` 或 `Double`。 +#### 集合、映射与形状不对的值 + +自 v6.3.0 起,配置值按字段声明的类型绑定。`List` 拿到的是 `Integer`,元素类型为 `Set`、`Long`、`Double`、`Boolean` 或枚举时同样会转换;映射的键和值会转换成映射声明的类型,值类型是你自己的类时仍然拿到原始的映射,与以前一致。旧版本写进文件的带引号数字(例如 `'30'`)会作为数字载入。无法转换的元素(例如 `List` 里的 `abc`)会被跳过,并记一条警告,写明文件、带元素位置的键、原值和声明类型,其余配置照常加载。在此之前,列表的每个元素都按文字绑定,因此 `contains(30)` 这类按类型查找永远匹配不上。 + +值的形状与字段不符时(例如声明为 `Map` 的地方写成了列表或单个值,或数字字段里写了文字),字段保持声明的默认值,并记一条警告,写明文件、键、声明类型和文件里实际的内容。模块照常加载,服主的文件也不会被改写。在此之前,配置加载会抛出异常,模块无法启动。键名像密钥时(例如 `password`、`token`),警告不会打印原值。 + +含点的映射键(例如 `my.rule`)保存后会作为一个键原样读回。在此之前,它会被保存成嵌套路径 `my: {rule: ...}`,读回时变成 `my`。通过 `getConfig()` 读取的路径与以前一致。 + 自 v6.3.0 起,`int`、`long`、`Integer` 或 `Long` 类型的字段还可以用来控制任务间隔或命令冷却:`@Scheduled` 见[绑定到配置项的时间](/zh/guide/advanced/scheduled-tasks#绑定到配置项的时间),`@CmdCD` 见[配置项绑定的冷却时间](/zh/guide/essentials/cmd-executor#配置项绑定的冷却时间)。该配置类必须为模块恰好注册一次,因此指向目录的 `@ConfigEntity` 不能用于绑定。被绑定的字段不要再加 [`@Range`](/zh/guide/advanced/config-validation):绑定自带范围检查,而 `/ul reload` 期间违反 `@Range` 会中止该模块其余的重载步骤([#509](https://github.com/UltiKits/UltiTools-Reborn/issues/509))。 #### @Getter 和 @Setter @@ -158,7 +168,7 @@ public List getAllConfigs() { 你无需担心配置文件的加载与保存等问题,UltiTools会自动为你做好一切。 ::: info 注释(v6.3.0 起) -Bukkit 在保存时会保留已有注释,UltiTools 显式设置了 `options().parseComments(true)`,不依赖默认值。首次新增的键也会连同其 `@ConfigEntry(comment)` 一并写入;服主已有的键则不会被改动。 +Bukkit 在保存时会保留已有注释,UltiTools 显式设置了 `options().parseComments(true)`,不依赖默认值。首次新增的键也会连同其 `@ConfigEntry(comment)` 一并写入;服主已有的键保留自己的注释,除非该注释是一个语言键(见上文 `@ConfigEntry`)。 ::: 一个纯粹外观上的副作用:SnakeYAML 保存时会把双引号字符串值重新写成单引号,值本身不变,只是引号风格变化。自 v6.3.0 起,这只在文件确实被保存时发生:没有任何改动的配置在插件关闭时不会被重写。 From d0ccc3df13c216d67d0af6b8b1fbce99274d8c88 Mon Sep 17 00:00:00 2001 From: Ling Bao Date: Wed, 30 Sep 2026 00:15:59 +1000 Subject: [PATCH 2/8] docs(config): align with the framework's gate-1 fixes (empty items, dates, collection types, re-rendering), as of v6.3.0 Co-Authored-By: Claude Opus 5.5 (1M context) --- docs/src/guide/essentials/config-file.md | 8 ++++---- docs/src/zh/guide/essentials/config-file.md | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/src/guide/essentials/config-file.md b/docs/src/guide/essentials/config-file.md index d83a6fa..96d55c3 100644 --- a/docs/src/guide/essentials/config-file.md +++ b/docs/src/guide/essentials/config-file.md @@ -75,7 +75,7 @@ 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. Only those comment lines change; an operator's hand-written comment on such an entry is replaced, and a start whose comments already match writes nothing. 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. +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. Like every framework write, the rewrite goes through the framework's YAML writer, which re-lays out hand-formatted YAML (quotes, inline lists, `yes`) without changing a value and does not keep a comment written beside a list item. 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` @@ -96,11 +96,11 @@ A decimal such as `0.5` is read as a `Double`, and narrowing it into a `float` o #### Collections, maps and wrongly shaped values -As of v6.3.0, a value is bound to the type its field declares. A `List` 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`, 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. Earlier versions bound every list element as its text, so a typed lookup such as `contains(30)` never matched. +As of v6.3.0, a value is bound to the type its field declares. A `List` 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`, 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` 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. -A value whose shape does not fit its field, such as a list or a plain value where a `Map` is declared, or text in a number 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 the value of a key whose name suggests a secret, such as `password` or `token`. +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`. -A map key that contains a dot, such as `my.rule`, is saved and read back as one key. Earlier versions saved it as the nested path `my: {rule: ...}` and read it back as `my`. Paths you read through `getConfig()` resolve as before. +A map key that contains a dot, such as `my.rule`, is saved and read back as one key. Earlier versions saved it as the nested path `my: {rule: ...}` and read it back as `my`. Paths you read through `getConfig()` into the map's other entries resolve as before. A key the loader could not read back, such as one ending in a dot, is refused when the configuration is saved, and the file is left as it was. 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)). diff --git a/docs/src/zh/guide/essentials/config-file.md b/docs/src/zh/guide/essentials/config-file.md index 028e476..5c0c214 100644 --- a/docs/src/zh/guide/essentials/config-file.md +++ b/docs/src/zh/guide/essentials/config-file.md @@ -64,7 +64,7 @@ TestConfig config = BasicFunctions.getInstance().getConfig("test/test1.yml", Tes `comment` 属性用于指定该配置项的注释; -自 v6.3.0 起,如果 `comment` 恰好是一个语言键,例如 `comment = "{config.limit}"`,框架会按服务器当前的 `language` 从模块的语言文件(`lang/en.json`、`lang/zh.json` 或对应的 `.yml`)中取出文字作为注释,这样同一个配置项就能以模块支持的每种语言提供注释。框架每次写这个配置文件时都会写入这段文字,包括服主文件里已有的键:首次启动写入默认值、保存、关服保存、面板写入,以及升级后或切换 `language` 后的第一次启动。只有这些注释行会变;服主在这类配置项上手写的注释会被替换,注释已经一致时启动不会写文件。语言文件里带换行的文字会写成多行注释。语言文件里没有这个键时,写入的就是这个键本身,并记一条警告,写明模块、文件、配置项和键名。其他注释(包括只是在文字中含有 `{player}` 这类占位符的注释)按原样写入,并且只在该键首次加入文件时写入。 +自 v6.3.0 起,如果 `comment` 恰好是一个语言键,例如 `comment = "{config.limit}"`,框架会按服务器当前的 `language` 从模块的语言文件(`lang/en.json`、`lang/zh.json` 或对应的 `.yml`)中取出文字作为注释,这样同一个配置项就能以模块支持的每种语言提供注释。框架每次写这个配置文件时都会写入这段文字,包括服主文件里已有的键:首次启动写入默认值、保存、关服保存、面板写入,以及升级后或切换 `language` 后的第一次启动。服主在这类配置项上手写的注释会被替换,设置值的含义不变,注释已经一致时启动不会写文件。与框架的每一次写文件一样,重写会经过框架的 YAML 输出:手写的格式(引号、行内列表、`yes`)会被统一但值不变,写在列表项旁边的注释不会保留。语言文件里带换行的文字会写成多行注释。语言文件里没有这个键时,写入的就是这个键本身,并记一条警告,写明模块、文件、配置项和键名。其他注释(包括只是在文字中含有 `{player}` 这类占位符的注释)按原样写入,并且只在该键首次加入文件时写入。 `parser` 属性用于指定该配置项的解析器。解析器用于将配置文件中的对象转换为配置项的类型。默认的解析器是 `DefaultConfigParser` , 它可以处理大多数情况,但并不是所有情况。如果你需要解析一个更复杂的对象,你可以创建一个继承 `ConfigParser` 类的类,并在 `parser` 属性中指定它。 @@ -82,11 +82,11 @@ YAML 会把 `1800` 这样的整数读成整数类型。自 v6.3.0 起,包装 #### 集合、映射与形状不对的值 -自 v6.3.0 起,配置值按字段声明的类型绑定。`List` 拿到的是 `Integer`,元素类型为 `Set`、`Long`、`Double`、`Boolean` 或枚举时同样会转换;映射的键和值会转换成映射声明的类型,值类型是你自己的类时仍然拿到原始的映射,与以前一致。旧版本写进文件的带引号数字(例如 `'30'`)会作为数字载入。无法转换的元素(例如 `List` 里的 `abc`)会被跳过,并记一条警告,写明文件、带元素位置的键、原值和声明类型,其余配置照常加载。在此之前,列表的每个元素都按文字绑定,因此 `contains(30)` 这类按类型查找永远匹配不上。 +自 v6.3.0 起,配置值按字段声明的类型绑定。`List` 拿到的是 `Integer`,元素类型为 `Set`、`Long`、`Double`、`Boolean` 或枚举时同样会转换;映射的键和值会转换成映射声明的类型,值类型是你自己的类时仍然拿到原始的映射,与以前一致。旧版本写进文件的带引号数字(例如 `'30'`)会作为数字载入。无法转换的元素(例如 `List` 里的 `abc`)会被跳过,并记一条警告,写明文件、带元素位置的键、原值和声明类型,其余配置照常加载;空的列表项也同样跳过。`List`、`Set`、`SortedSet`、`Queue`、`EnumSet`、`Map`、`SortedMap`、`ConcurrentMap` 和 `EnumMap` 字段都支持,自定义 `parser` 的读写与以前完全一致。在此之前,列表的每个元素都按文字绑定,因此 `contains(30)` 这类按类型查找永远匹配不上。 -值的形状与字段不符时(例如声明为 `Map` 的地方写成了列表或单个值,或数字字段里写了文字),字段保持声明的默认值,并记一条警告,写明文件、键、声明类型和文件里实际的内容。模块照常加载,服主的文件也不会被改写。在此之前,配置加载会抛出异常,模块无法启动。键名像密钥时(例如 `password`、`token`),警告不会打印原值。 +值的形状与字段不符时(例如声明为 `Map` 的地方写成了列表或单个值,数字字段里写了文字,或文字字段里写了 `2024-01-01` 这样的日期),字段保持声明的默认值,并记一条警告,写明文件、键、声明类型和文件里实际的内容。模块照常加载,服主的文件也不会被改写。在此之前,配置加载会抛出异常,模块无法启动。值本身的键名或其中任何一个键名像密钥时(例如 `password`、`token`),警告不会打印原值。 -含点的映射键(例如 `my.rule`)保存后会作为一个键原样读回。在此之前,它会被保存成嵌套路径 `my: {rule: ...}`,读回时变成 `my`。通过 `getConfig()` 读取的路径与以前一致。 +含点的映射键(例如 `my.rule`)保存后会作为一个键原样读回。在此之前,它会被保存成嵌套路径 `my: {rule: ...}`,读回时变成 `my`。通过 `getConfig()` 读取该映射其他条目的路径与以前一致。加载器无法读回的键(例如以点结尾的键)在保存时会被拒绝,文件保持原样。 自 v6.3.0 起,`int`、`long`、`Integer` 或 `Long` 类型的字段还可以用来控制任务间隔或命令冷却:`@Scheduled` 见[绑定到配置项的时间](/zh/guide/advanced/scheduled-tasks#绑定到配置项的时间),`@CmdCD` 见[配置项绑定的冷却时间](/zh/guide/essentials/cmd-executor#配置项绑定的冷却时间)。该配置类必须为模块恰好注册一次,因此指向目录的 `@ConfigEntity` 不能用于绑定。被绑定的字段不要再加 [`@Range`](/zh/guide/advanced/config-validation):绑定自带范围检查,而 `/ul reload` 期间违反 `@Range` 会中止该模块其余的重载步骤([#509](https://github.com/UltiKits/UltiTools-Reborn/issues/509))。 From c0d1d0382a97e6af13b099762b391073f936f487 Mon Sep 17 00:00:00 2001 From: Ling Bao Date: Wed, 30 Sep 2026 10:25:49 +1000 Subject: [PATCH 3/8] docs(config): float decimals, dotted map keys refused and named, and the whole-file re-render, as of v6.3.0 Follows the maintainer's answers of 2026-09-30 on UltiKits/UltiTools-Reborn#534, #553 and #542. Co-Authored-By: Claude Opus 5.5 (1M context) --- docs/src/guide/essentials/config-file.md | 6 +++--- docs/src/zh/guide/essentials/config-file.md | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/src/guide/essentials/config-file.md b/docs/src/guide/essentials/config-file.md index 96d55c3..f366f5a 100644 --- a/docs/src/guide/essentials/config-file.md +++ b/docs/src/guide/essentials/config-file.md @@ -75,7 +75,7 @@ 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. Like every framework write, the rewrite goes through the framework's YAML writer, which re-lays out hand-formatted YAML (quotes, inline lists, `yes`) without changing a value and does not keep a comment written beside a list item. 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. +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` @@ -92,7 +92,7 @@ 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 @@ -100,7 +100,7 @@ As of v6.3.0, a value is bound to the type its field declares. A `List` 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`. -A map key that contains a dot, such as `my.rule`, is saved and read back as one key. Earlier versions saved it as the nested path `my: {rule: ...}` and read it back as `my`. Paths you read through `getConfig()` into the map's other entries resolve as before. A key the loader could not read back, such as one ending in a dot, is refused when the configuration is saved, and the file is left as it was. +A map key cannot contain a dot. The configuration file uses `.` as its path separator, so a key such as `my.rule` would be read back as `my` → `rule`, and quoting it does not help. As of v6.3.0 the framework says so instead of renaming the key silently: when it writes a map (a save, a first-boot default, a panel write) it leaves such a key out and logs a warning naming the file, the entry and the key, and when it finds one in a file on start or reload it logs a warning asking the operator to rename it. Use `-` or `_` in map keys instead. Everything else about the configuration, including paths you read through `getConfig()`, is as before. 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)). diff --git a/docs/src/zh/guide/essentials/config-file.md b/docs/src/zh/guide/essentials/config-file.md index 5c0c214..1168858 100644 --- a/docs/src/zh/guide/essentials/config-file.md +++ b/docs/src/zh/guide/essentials/config-file.md @@ -64,7 +64,7 @@ TestConfig config = BasicFunctions.getInstance().getConfig("test/test1.yml", Tes `comment` 属性用于指定该配置项的注释; -自 v6.3.0 起,如果 `comment` 恰好是一个语言键,例如 `comment = "{config.limit}"`,框架会按服务器当前的 `language` 从模块的语言文件(`lang/en.json`、`lang/zh.json` 或对应的 `.yml`)中取出文字作为注释,这样同一个配置项就能以模块支持的每种语言提供注释。框架每次写这个配置文件时都会写入这段文字,包括服主文件里已有的键:首次启动写入默认值、保存、关服保存、面板写入,以及升级后或切换 `language` 后的第一次启动。服主在这类配置项上手写的注释会被替换,设置值的含义不变,注释已经一致时启动不会写文件。与框架的每一次写文件一样,重写会经过框架的 YAML 输出:手写的格式(引号、行内列表、`yes`)会被统一但值不变,写在列表项旁边的注释不会保留。语言文件里带换行的文字会写成多行注释。语言文件里没有这个键时,写入的就是这个键本身,并记一条警告,写明模块、文件、配置项和键名。其他注释(包括只是在文字中含有 `{player}` 这类占位符的注释)按原样写入,并且只在该键首次加入文件时写入。 +自 v6.3.0 起,如果 `comment` 恰好是一个语言键,例如 `comment = "{config.limit}"`,框架会按服务器当前的 `language` 从模块的语言文件(`lang/en.json`、`lang/zh.json` 或对应的 `.yml`)中取出文字作为注释,这样同一个配置项就能以模块支持的每种语言提供注释。框架每次写这个配置文件时都会写入这段文字,包括服主文件里已有的键:首次启动写入默认值、保存、关服保存、面板写入,以及升级后或切换 `language` 后的第一次启动。服主在这类配置项上手写的注释会被替换,设置值的含义不变,注释已经一致时启动不会写文件。重写改动的不只是注释行:与框架的每一次写文件一样,整个文件会重新输出,服主加的引号会被去掉,行内列表 `[a, b]` 变成多行,`yes` 变成 `true`,`1.50` 变成 `1.5`,写在列表项旁边的注释会丢失;值的含义不变。升级后第一次启动和切换 `language` 后各发生一次。语言文件里带换行的文字会写成多行注释。语言文件里没有这个键时,写入的就是这个键本身,并记一条警告,写明模块、文件、配置项和键名。其他注释(包括只是在文字中含有 `{player}` 这类占位符的注释)按原样写入,并且只在该键首次加入文件时写入。 `parser` 属性用于指定该配置项的解析器。解析器用于将配置文件中的对象转换为配置项的类型。默认的解析器是 `DefaultConfigParser` , 它可以处理大多数情况,但并不是所有情况。如果你需要解析一个更复杂的对象,你可以创建一个继承 `ConfigParser` 类的类,并在 `parser` 属性中指定它。 @@ -78,7 +78,7 @@ TestConfig config = BasicFunctions.getInstance().getConfig("test/test1.yml", Tes YAML 会把 `1800` 这样的整数读成整数类型。自 v6.3.0 起,包装类型 `Long`、`Float`、`Double` 的字段也能载入这样的值,与基本类型 `long`、`float`、`double` 的字段一致。在此之前,整数无法直接写入其他数值类型的包装类字段,因此这类字段只在首次启动(写入默认值时)能正常载入,之后每次启动和重载都会失败。框架只做拓宽转换,所以包装类型字段能接受的值与其对应的基本类型完全相同。 -`0.5` 这样的小数会被读成 `Double`,目前不支持把它收窄写入 `float` 或 `Float` 字段([#534](https://github.com/UltiKits/UltiTools-Reborn/issues/534))。可能包含小数的值请使用 `double` 或 `Double`。 +`0.5` 这样的小数会被读成 `Double`。自 v6.3.0 起,`float` 或 `Float` 字段(以及 `Float` 列表元素)在小数按 float 读回来一样时接受它,所以 `0.1`、`0.3`、`1.5` 都能载入,框架自己写出的 float 也总能读回([#534](https://github.com/UltiKits/UltiTools-Reborn/issues/534))。位数超过 float 能保存的值(例如 `0.123456789`)保留默认值并记一条警告;需要这种精度时请使用 `double` 或 `Double`。 #### 集合、映射与形状不对的值 @@ -86,7 +86,7 @@ YAML 会把 `1800` 这样的整数读成整数类型。自 v6.3.0 起,包装 值的形状与字段不符时(例如声明为 `Map` 的地方写成了列表或单个值,数字字段里写了文字,或文字字段里写了 `2024-01-01` 这样的日期),字段保持声明的默认值,并记一条警告,写明文件、键、声明类型和文件里实际的内容。模块照常加载,服主的文件也不会被改写。在此之前,配置加载会抛出异常,模块无法启动。值本身的键名或其中任何一个键名像密钥时(例如 `password`、`token`),警告不会打印原值。 -含点的映射键(例如 `my.rule`)保存后会作为一个键原样读回。在此之前,它会被保存成嵌套路径 `my: {rule: ...}`,读回时变成 `my`。通过 `getConfig()` 读取该映射其他条目的路径与以前一致。加载器无法读回的键(例如以点结尾的键)在保存时会被拒绝,文件保持原样。 +映射的键不能含点。配置文件用 `.` 作路径分隔符,`my.rule` 这样的键读回时会变成 `my` → `rule`,加引号也没用。自 v6.3.0 起,框架不再悄悄改名,而是明确告知:写映射时(保存、首次启动写入默认值、面板写入)会跳过这样的键,并记一条警告写明文件、配置项和键名;启动或重载时在文件里发现这样的键,也会警告服主改名。映射键请改用 `-` 或 `_`。配置的其他行为(包括通过 `getConfig()` 读取的路径)与以前一致。 自 v6.3.0 起,`int`、`long`、`Integer` 或 `Long` 类型的字段还可以用来控制任务间隔或命令冷却:`@Scheduled` 见[绑定到配置项的时间](/zh/guide/advanced/scheduled-tasks#绑定到配置项的时间),`@CmdCD` 见[配置项绑定的冷却时间](/zh/guide/essentials/cmd-executor#配置项绑定的冷却时间)。该配置类必须为模块恰好注册一次,因此指向目录的 `@ConfigEntity` 不能用于绑定。被绑定的字段不要再加 [`@Range`](/zh/guide/advanced/config-validation):绑定自带范围检查,而 `/ul reload` 期间违反 `@Range` 会中止该模块其余的重载步骤([#509](https://github.com/UltiKits/UltiTools-Reborn/issues/509))。 From 94f0ca4d4dcd80eaaedd18b32e7375376b7c98ef Mon Sep 17 00:00:00 2001 From: Ling Bao Date: Wed, 30 Sep 2026 11:09:53 +1000 Subject: [PATCH 4/8] docs(config): dotted keys refused wherever the map is; UUID elements, as of v6.3.0 Co-Authored-By: Claude Opus 5.5 (1M context) --- docs/src/guide/essentials/config-file.md | 4 ++-- docs/src/zh/guide/essentials/config-file.md | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/src/guide/essentials/config-file.md b/docs/src/guide/essentials/config-file.md index f366f5a..518db2b 100644 --- a/docs/src/guide/essentials/config-file.md +++ b/docs/src/guide/essentials/config-file.md @@ -96,11 +96,11 @@ A decimal such as `0.5` is read as a `Double`. As of v6.3.0, a `float` or `Float #### Collections, maps and wrongly shaped values -As of v6.3.0, a value is bound to the type its field declares. A `List` 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`, 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` 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. +As of v6.3.0, a value is bound to the type its field declares. A `List` receives `Integer`s, and a `Set`, `Long`, `Double`, `Boolean`, `UUID` 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`, 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` 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. 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`. -A map key cannot contain a dot. The configuration file uses `.` as its path separator, so a key such as `my.rule` would be read back as `my` → `rule`, and quoting it does not help. As of v6.3.0 the framework says so instead of renaming the key silently: when it writes a map (a save, a first-boot default, a panel write) it leaves such a key out and logs a warning naming the file, the entry and the key, and when it finds one in a file on start or reload it logs a warning asking the operator to rename it. Use `-` or `_` in map keys instead. Everything else about the configuration, including paths you read through `getConfig()`, is as before. +A map key cannot contain a dot. The configuration file uses `.` as its path separator, so a key such as `my.rule` would be read back as `my` → `rule`, and quoting it does not help. As of v6.3.0 the framework says so instead of renaming the key silently: when it writes a map (a save, a first-boot default, a panel write), wherever the map is - inside another map, an object or a list - it leaves such a key out and logs a warning naming the file, the entry and the key, and when it finds one in a file on start or reload it logs a warning asking the operator to rename it. Use `-` or `_` in map keys instead. Everything else about the configuration, including paths you read through `getConfig()`, is as before. 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)). diff --git a/docs/src/zh/guide/essentials/config-file.md b/docs/src/zh/guide/essentials/config-file.md index 1168858..ff62feb 100644 --- a/docs/src/zh/guide/essentials/config-file.md +++ b/docs/src/zh/guide/essentials/config-file.md @@ -82,11 +82,11 @@ YAML 会把 `1800` 这样的整数读成整数类型。自 v6.3.0 起,包装 #### 集合、映射与形状不对的值 -自 v6.3.0 起,配置值按字段声明的类型绑定。`List` 拿到的是 `Integer`,元素类型为 `Set`、`Long`、`Double`、`Boolean` 或枚举时同样会转换;映射的键和值会转换成映射声明的类型,值类型是你自己的类时仍然拿到原始的映射,与以前一致。旧版本写进文件的带引号数字(例如 `'30'`)会作为数字载入。无法转换的元素(例如 `List` 里的 `abc`)会被跳过,并记一条警告,写明文件、带元素位置的键、原值和声明类型,其余配置照常加载;空的列表项也同样跳过。`List`、`Set`、`SortedSet`、`Queue`、`EnumSet`、`Map`、`SortedMap`、`ConcurrentMap` 和 `EnumMap` 字段都支持,自定义 `parser` 的读写与以前完全一致。在此之前,列表的每个元素都按文字绑定,因此 `contains(30)` 这类按类型查找永远匹配不上。 +自 v6.3.0 起,配置值按字段声明的类型绑定。`List` 拿到的是 `Integer`,元素类型为 `Set`、`Long`、`Double`、`Boolean`、`UUID` 或枚举时同样会转换;映射的键和值会转换成映射声明的类型,值类型是你自己的类时仍然拿到原始的映射,与以前一致。旧版本写进文件的带引号数字(例如 `'30'`)会作为数字载入。无法转换的元素(例如 `List` 里的 `abc`)会被跳过,并记一条警告,写明文件、带元素位置的键、原值和声明类型,其余配置照常加载;空的列表项也同样跳过。`List`、`Set`、`SortedSet`、`Queue`、`EnumSet`、`Map`、`SortedMap`、`ConcurrentMap` 和 `EnumMap` 字段都支持,自定义 `parser` 的读写与以前完全一致。在此之前,列表的每个元素都按文字绑定,因此 `contains(30)` 这类按类型查找永远匹配不上。 值的形状与字段不符时(例如声明为 `Map` 的地方写成了列表或单个值,数字字段里写了文字,或文字字段里写了 `2024-01-01` 这样的日期),字段保持声明的默认值,并记一条警告,写明文件、键、声明类型和文件里实际的内容。模块照常加载,服主的文件也不会被改写。在此之前,配置加载会抛出异常,模块无法启动。值本身的键名或其中任何一个键名像密钥时(例如 `password`、`token`),警告不会打印原值。 -映射的键不能含点。配置文件用 `.` 作路径分隔符,`my.rule` 这样的键读回时会变成 `my` → `rule`,加引号也没用。自 v6.3.0 起,框架不再悄悄改名,而是明确告知:写映射时(保存、首次启动写入默认值、面板写入)会跳过这样的键,并记一条警告写明文件、配置项和键名;启动或重载时在文件里发现这样的键,也会警告服主改名。映射键请改用 `-` 或 `_`。配置的其他行为(包括通过 `getConfig()` 读取的路径)与以前一致。 +映射的键不能含点。配置文件用 `.` 作路径分隔符,`my.rule` 这样的键读回时会变成 `my` → `rule`,加引号也没用。自 v6.3.0 起,框架不再悄悄改名,而是明确告知:写映射时(保存、首次启动写入默认值、面板写入),无论映射在哪里(另一个映射、对象或列表里),都会跳过这样的键,并记一条警告写明文件、配置项和键名;启动或重载时在文件里发现这样的键,也会警告服主改名。映射键请改用 `-` 或 `_`。配置的其他行为(包括通过 `getConfig()` 读取的路径)与以前一致。 自 v6.3.0 起,`int`、`long`、`Integer` 或 `Long` 类型的字段还可以用来控制任务间隔或命令冷却:`@Scheduled` 见[绑定到配置项的时间](/zh/guide/advanced/scheduled-tasks#绑定到配置项的时间),`@CmdCD` 见[配置项绑定的冷却时间](/zh/guide/essentials/cmd-executor#配置项绑定的冷却时间)。该配置类必须为模块恰好注册一次,因此指向目录的 `@ConfigEntity` 不能用于绑定。被绑定的字段不要再加 [`@Range`](/zh/guide/advanced/config-validation):绑定自带范围检查,而 `/ul reload` 期间违反 `@Range` 会中止该模块其余的重载步骤([#509](https://github.com/UltiKits/UltiTools-Reborn/issues/509))。 From 786d30546d0f63827b57ab3213ea81b443c5ae26 Mon Sep 17 00:00:00 2001 From: Ling Bao Date: Wed, 30 Sep 2026 11:35:41 +1000 Subject: [PATCH 5/8] docs(config): dotted keys are refused only in maps stored as sections; list-element maps keep them, as of v6.3.0 Co-Authored-By: Claude Opus 5.5 (1M context) --- docs/src/guide/essentials/config-file.md | 2 +- docs/src/zh/guide/essentials/config-file.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/src/guide/essentials/config-file.md b/docs/src/guide/essentials/config-file.md index 518db2b..2b40d26 100644 --- a/docs/src/guide/essentials/config-file.md +++ b/docs/src/guide/essentials/config-file.md @@ -100,7 +100,7 @@ As of v6.3.0, a value is bound to the type its field declares. A `List` 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`. -A map key cannot contain a dot. The configuration file uses `.` as its path separator, so a key such as `my.rule` would be read back as `my` → `rule`, and quoting it does not help. As of v6.3.0 the framework says so instead of renaming the key silently: when it writes a map (a save, a first-boot default, a panel write), wherever the map is - inside another map, an object or a list - it leaves such a key out and logs a warning naming the file, the entry and the key, and when it finds one in a file on start or reload it logs a warning asking the operator to rename it. Use `-` or `_` in map keys instead. Everything else about the configuration, including paths you read through `getConfig()`, is as before. +A map that the file stores as a section - a map field, a map nested in one, or a map inside an object - cannot have a key with a dot. The configuration file uses `.` as its path separator, so a key such as `my.rule` would be read back as `my` → `rule`, and quoting it does not help. A map that is an element of a list is plain data, which the file keeps whole, so a dotted key there is fine. As of v6.3.0 the framework says so instead of renaming the key silently: when it writes such a map (a save, a first-boot default, a panel write) it leaves such a key out and logs a warning naming the file, the entry and the key, and when it finds one in a file on start or reload it logs a warning asking the operator to rename it. Use `-` or `_` in map keys instead. Everything else about the configuration, including paths you read through `getConfig()`, is as before. 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)). diff --git a/docs/src/zh/guide/essentials/config-file.md b/docs/src/zh/guide/essentials/config-file.md index ff62feb..786726c 100644 --- a/docs/src/zh/guide/essentials/config-file.md +++ b/docs/src/zh/guide/essentials/config-file.md @@ -86,7 +86,7 @@ YAML 会把 `1800` 这样的整数读成整数类型。自 v6.3.0 起,包装 值的形状与字段不符时(例如声明为 `Map` 的地方写成了列表或单个值,数字字段里写了文字,或文字字段里写了 `2024-01-01` 这样的日期),字段保持声明的默认值,并记一条警告,写明文件、键、声明类型和文件里实际的内容。模块照常加载,服主的文件也不会被改写。在此之前,配置加载会抛出异常,模块无法启动。值本身的键名或其中任何一个键名像密钥时(例如 `password`、`token`),警告不会打印原值。 -映射的键不能含点。配置文件用 `.` 作路径分隔符,`my.rule` 这样的键读回时会变成 `my` → `rule`,加引号也没用。自 v6.3.0 起,框架不再悄悄改名,而是明确告知:写映射时(保存、首次启动写入默认值、面板写入),无论映射在哪里(另一个映射、对象或列表里),都会跳过这样的键,并记一条警告写明文件、配置项和键名;启动或重载时在文件里发现这样的键,也会警告服主改名。映射键请改用 `-` 或 `_`。配置的其他行为(包括通过 `getConfig()` 读取的路径)与以前一致。 +文件以配置节保存的映射(映射字段、嵌套在其中的映射、对象里的映射)的键不能含点。配置文件用 `.` 作路径分隔符,`my.rule` 这样的键读回时会变成 `my` → `rule`,加引号也没用。列表元素里的映射是普通数据,文件能原样保存,键里含点没有问题。自 v6.3.0 起,框架不再悄悄改名,而是明确告知:写这类映射时(保存、首次启动写入默认值、面板写入)会跳过这样的键,并记一条警告写明文件、配置项和键名;启动或重载时在文件里发现这样的键,也会警告服主改名。映射键请改用 `-` 或 `_`。配置的其他行为(包括通过 `getConfig()` 读取的路径)与以前一致。 自 v6.3.0 起,`int`、`long`、`Integer` 或 `Long` 类型的字段还可以用来控制任务间隔或命令冷却:`@Scheduled` 见[绑定到配置项的时间](/zh/guide/advanced/scheduled-tasks#绑定到配置项的时间),`@CmdCD` 见[配置项绑定的冷却时间](/zh/guide/essentials/cmd-executor#配置项绑定的冷却时间)。该配置类必须为模块恰好注册一次,因此指向目录的 `@ConfigEntity` 不能用于绑定。被绑定的字段不要再加 [`@Range`](/zh/guide/advanced/config-validation):绑定自带范围检查,而 `/ul reload` 期间违反 `@Range` 会中止该模块其余的重载步骤([#509](https://github.com/UltiKits/UltiTools-Reborn/issues/509))。 From c8129a8bcdaa5af5ebccc361451b988c79b41db7 Mon Sep 17 00:00:00 2001 From: Ling Bao Date: Wed, 30 Sep 2026 12:10:00 +1000 Subject: [PATCH 6/8] docs(config): dotted map keys are warned about and still split as in 6.2, as of v6.3.0 Follows the maintainer's answer of 2026-09-30 on UltiKits/UltiTools-Reborn#553 (warn only, the write path stays as in 6.2); no refusal claim remains. Co-Authored-By: Claude Opus 5.5 (1M context) --- docs/src/guide/essentials/config-file.md | 4 ++-- docs/src/zh/guide/essentials/config-file.md | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/src/guide/essentials/config-file.md b/docs/src/guide/essentials/config-file.md index 2b40d26..d98ad58 100644 --- a/docs/src/guide/essentials/config-file.md +++ b/docs/src/guide/essentials/config-file.md @@ -96,11 +96,11 @@ A decimal such as `0.5` is read as a `Double`. As of v6.3.0, a `float` or `Float #### Collections, maps and wrongly shaped values -As of v6.3.0, a value is bound to the type its field declares. A `List` receives `Integer`s, and a `Set`, `Long`, `Double`, `Boolean`, `UUID` 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`, 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` 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. +As of v6.3.0, a value is bound to the type its field declares. A `List` 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`, 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` 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. 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`. -A map that the file stores as a section - a map field, a map nested in one, or a map inside an object - cannot have a key with a dot. The configuration file uses `.` as its path separator, so a key such as `my.rule` would be read back as `my` → `rule`, and quoting it does not help. A map that is an element of a list is plain data, which the file keeps whole, so a dotted key there is fine. As of v6.3.0 the framework says so instead of renaming the key silently: when it writes such a map (a save, a first-boot default, a panel write) it leaves such a key out and logs a warning naming the file, the entry and the key, and when it finds one in a file on start or reload it logs a warning asking the operator to rename it. Use `-` or `_` in map keys instead. Everything else about the configuration, including paths you read through `getConfig()`, is as before. +A map key with a dot is written and read as it always was: 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 - a key such as `my.rule` is stored split into nested levels (`my` → `rule`), and quoting it does not help. As of v6.3.0 this is no longer silent: when a start or reload finds such a key in the file, and before the framework writes one (a save, a first-boot default, a panel write), it logs a warning naming the file, the entry, the map's nested path and the module and asking the operator to rename the key. The check only reads - it never changes a value or the file. A map that is an element of a list is plain data the file keeps whole, so a dotted key there is fine and is not warned about. Use `-` or `_` in map keys. 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)). diff --git a/docs/src/zh/guide/essentials/config-file.md b/docs/src/zh/guide/essentials/config-file.md index 786726c..ca5d3b4 100644 --- a/docs/src/zh/guide/essentials/config-file.md +++ b/docs/src/zh/guide/essentials/config-file.md @@ -82,11 +82,11 @@ YAML 会把 `1800` 这样的整数读成整数类型。自 v6.3.0 起,包装 #### 集合、映射与形状不对的值 -自 v6.3.0 起,配置值按字段声明的类型绑定。`List` 拿到的是 `Integer`,元素类型为 `Set`、`Long`、`Double`、`Boolean`、`UUID` 或枚举时同样会转换;映射的键和值会转换成映射声明的类型,值类型是你自己的类时仍然拿到原始的映射,与以前一致。旧版本写进文件的带引号数字(例如 `'30'`)会作为数字载入。无法转换的元素(例如 `List` 里的 `abc`)会被跳过,并记一条警告,写明文件、带元素位置的键、原值和声明类型,其余配置照常加载;空的列表项也同样跳过。`List`、`Set`、`SortedSet`、`Queue`、`EnumSet`、`Map`、`SortedMap`、`ConcurrentMap` 和 `EnumMap` 字段都支持,自定义 `parser` 的读写与以前完全一致。在此之前,列表的每个元素都按文字绑定,因此 `contains(30)` 这类按类型查找永远匹配不上。 +自 v6.3.0 起,配置值按字段声明的类型绑定。`List` 拿到的是 `Integer`,元素类型为 `Set`、`Long`、`Double`、`Boolean` 或枚举时同样会转换;映射的键和值会转换成映射声明的类型,值类型是你自己的类时仍然拿到原始的映射,与以前一致。旧版本写进文件的带引号数字(例如 `'30'`)会作为数字载入。无法转换的元素(例如 `List` 里的 `abc`)会被跳过,并记一条警告,写明文件、带元素位置的键、原值和声明类型,其余配置照常加载;空的列表项也同样跳过。`List`、`Set`、`SortedSet`、`Queue`、`EnumSet`、`Map`、`SortedMap`、`ConcurrentMap` 和 `EnumMap` 字段都支持,自定义 `parser` 的读写与以前完全一致。在此之前,列表的每个元素都按文字绑定,因此 `contains(30)` 这类按类型查找永远匹配不上。 值的形状与字段不符时(例如声明为 `Map` 的地方写成了列表或单个值,数字字段里写了文字,或文字字段里写了 `2024-01-01` 这样的日期),字段保持声明的默认值,并记一条警告,写明文件、键、声明类型和文件里实际的内容。模块照常加载,服主的文件也不会被改写。在此之前,配置加载会抛出异常,模块无法启动。值本身的键名或其中任何一个键名像密钥时(例如 `password`、`token`),警告不会打印原值。 -文件以配置节保存的映射(映射字段、嵌套在其中的映射、对象里的映射)的键不能含点。配置文件用 `.` 作路径分隔符,`my.rule` 这样的键读回时会变成 `my` → `rule`,加引号也没用。列表元素里的映射是普通数据,文件能原样保存,键里含点没有问题。自 v6.3.0 起,框架不再悄悄改名,而是明确告知:写这类映射时(保存、首次启动写入默认值、面板写入)会跳过这样的键,并记一条警告写明文件、配置项和键名;启动或重载时在文件里发现这样的键,也会警告服主改名。映射键请改用 `-` 或 `_`。配置的其他行为(包括通过 `getConfig()` 读取的路径)与以前一致。 +含点的映射键仍按原来的方式写入和读取:配置文件用 `.` 作路径分隔符,在文件以配置节保存的映射里(映射字段、嵌套在其中的映射、对象里的映射),`my.rule` 这样的键会被拆成嵌套的几层(`my` → `rule`),加引号也没用。自 v6.3.0 起这不再是无声的:启动或重载时在文件里发现这样的键,以及框架写入这样的键之前(保存、首次启动写入默认值、面板写入),都会记一条警告,写明文件、配置项、嵌套路径和模块,请服主改名。检查只读,不改任何值和文件。列表元素里的映射是普通数据,文件能原样保存,键里含点没有问题,也不警告。映射键请改用 `-` 或 `_`。 自 v6.3.0 起,`int`、`long`、`Integer` 或 `Long` 类型的字段还可以用来控制任务间隔或命令冷却:`@Scheduled` 见[绑定到配置项的时间](/zh/guide/advanced/scheduled-tasks#绑定到配置项的时间),`@CmdCD` 见[配置项绑定的冷却时间](/zh/guide/essentials/cmd-executor#配置项绑定的冷却时间)。该配置类必须为模块恰好注册一次,因此指向目录的 `@ConfigEntity` 不能用于绑定。被绑定的字段不要再加 [`@Range`](/zh/guide/advanced/config-validation):绑定自带范围检查,而 `/ul reload` 期间违反 `@Range` 会中止该模块其余的重载步骤([#509](https://github.com/UltiKits/UltiTools-Reborn/issues/509))。 From a3f2b1e0c9c5cefcc724f188442195355e12013f Mon Sep 17 00:00:00 2001 From: Ling Bao Date: Wed, 30 Sep 2026 12:38:06 +1000 Subject: [PATCH 7/8] docs(config): the dotted-key warning's scope and wording, as of v6.3.0 Co-Authored-By: Claude Opus 5.5 (1M context) --- docs/src/guide/essentials/config-file.md | 2 +- docs/src/zh/guide/essentials/config-file.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/src/guide/essentials/config-file.md b/docs/src/guide/essentials/config-file.md index d98ad58..ccf76df 100644 --- a/docs/src/guide/essentials/config-file.md +++ b/docs/src/guide/essentials/config-file.md @@ -100,7 +100,7 @@ As of v6.3.0, a value is bound to the type its field declares. A `List` 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`. -A map key with a dot is written and read as it always was: 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 - a key such as `my.rule` is stored split into nested levels (`my` → `rule`), and quoting it does not help. As of v6.3.0 this is no longer silent: when a start or reload finds such a key in the file, and before the framework writes one (a save, a first-boot default, a panel write), it logs a warning naming the file, the entry, the map's nested path and the module and asking the operator to rename the key. The check only reads - it never changes a value or the file. A map that is an element of a list is plain data the file keeps whole, so a dotted key there is fine and is not warned about. Use `-` or `_` in map keys. +A map key with a dot is written and read as it always was: 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 - a key such as `my.rule` is stored split into nested levels (`my` → `rule`), and quoting it does not help. As of v6.3.0 this is no longer silent: when a start or reload finds such a key in the file, and before the framework writes one (a save, a first-boot default, a panel write), it logs a warning naming the file, the entry, the map's nested path and the module, saying the key will be split into nested levels the next time the file is loaded and asking the operator to rename it. Only entries declared as a `Map` are checked, and maps nested in them as far as the declared type says `Map`. The check only reads - it never changes a value or the file. A map that is an element of a list is plain data the file keeps whole, so a dotted key there is fine and is not warned about. Use `-` or `_` in map keys. 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)). diff --git a/docs/src/zh/guide/essentials/config-file.md b/docs/src/zh/guide/essentials/config-file.md index ca5d3b4..b9db444 100644 --- a/docs/src/zh/guide/essentials/config-file.md +++ b/docs/src/zh/guide/essentials/config-file.md @@ -86,7 +86,7 @@ YAML 会把 `1800` 这样的整数读成整数类型。自 v6.3.0 起,包装 值的形状与字段不符时(例如声明为 `Map` 的地方写成了列表或单个值,数字字段里写了文字,或文字字段里写了 `2024-01-01` 这样的日期),字段保持声明的默认值,并记一条警告,写明文件、键、声明类型和文件里实际的内容。模块照常加载,服主的文件也不会被改写。在此之前,配置加载会抛出异常,模块无法启动。值本身的键名或其中任何一个键名像密钥时(例如 `password`、`token`),警告不会打印原值。 -含点的映射键仍按原来的方式写入和读取:配置文件用 `.` 作路径分隔符,在文件以配置节保存的映射里(映射字段、嵌套在其中的映射、对象里的映射),`my.rule` 这样的键会被拆成嵌套的几层(`my` → `rule`),加引号也没用。自 v6.3.0 起这不再是无声的:启动或重载时在文件里发现这样的键,以及框架写入这样的键之前(保存、首次启动写入默认值、面板写入),都会记一条警告,写明文件、配置项、嵌套路径和模块,请服主改名。检查只读,不改任何值和文件。列表元素里的映射是普通数据,文件能原样保存,键里含点没有问题,也不警告。映射键请改用 `-` 或 `_`。 +含点的映射键仍按原来的方式写入和读取:配置文件用 `.` 作路径分隔符,在文件以配置节保存的映射里(映射字段、嵌套在其中的映射、对象里的映射),`my.rule` 这样的键会被拆成嵌套的几层(`my` → `rule`),加引号也没用。自 v6.3.0 起这不再是无声的:启动或重载时在文件里发现这样的键,以及框架写入这样的键之前(保存、首次启动写入默认值、面板写入),都会记一条警告,写明文件、配置项、嵌套路径和模块,说明这个键下次读取时会被拆成嵌套的几层,请服主改名。只检查声明为 `Map` 的配置项及其中声明为映射的嵌套映射。检查只读,不改任何值和文件。列表元素里的映射是普通数据,文件能原样保存,键里含点没有问题,也不警告。映射键请改用 `-` 或 `_`。 自 v6.3.0 起,`int`、`long`、`Integer` 或 `Long` 类型的字段还可以用来控制任务间隔或命令冷却:`@Scheduled` 见[绑定到配置项的时间](/zh/guide/advanced/scheduled-tasks#绑定到配置项的时间),`@CmdCD` 见[配置项绑定的冷却时间](/zh/guide/essentials/cmd-executor#配置项绑定的冷却时间)。该配置类必须为模块恰好注册一次,因此指向目录的 `@ConfigEntity` 不能用于绑定。被绑定的字段不要再加 [`@Range`](/zh/guide/advanced/config-validation):绑定自带范围检查,而 `/ul reload` 期间违反 `@Range` 会中止该模块其余的重载步骤([#509](https://github.com/UltiKits/UltiTools-Reborn/issues/509))。 From 610b67bad1e47db5f0087c4000aec520dc84e407 Mon Sep 17 00:00:00 2001 From: Ling Bao Date: Wed, 30 Sep 2026 13:07:33 +1000 Subject: [PATCH 8/8] docs(553): do not put a dot in a map key - documented, not checked; null values; module parsers as before UltiKits/UltiTools-Reborn#553, maintainer answer of 2026-09-30 ("no check, documentation only"): the framework neither refuses nor warns about a dotted map key, so the page now says plainly that such a key is split into nested levels on write and on load, that quoting it does not help, and that module authors and operators must use '-' or '_'. No claim of a warning remains. Also: a custom parser, including one extending DefaultConfigParser, reads and writes exactly as before; save() leaves a null map/object value out with a warning where earlier versions threw a NullPointerException and left the file unchanged. English and Chinese pages changed together. Co-Authored-By: Claude Opus 5.5 (1M context) --- docs/src/guide/essentials/config-file.md | 4 ++-- docs/src/zh/guide/essentials/config-file.md | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/src/guide/essentials/config-file.md b/docs/src/guide/essentials/config-file.md index ccf76df..df9f462 100644 --- a/docs/src/guide/essentials/config-file.md +++ b/docs/src/guide/essentials/config-file.md @@ -96,11 +96,11 @@ A decimal such as `0.5` is read as a `Double`. As of v6.3.0, a `float` or `Float #### Collections, maps and wrongly shaped values -As of v6.3.0, a value is bound to the type its field declares. A `List` 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`, 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` 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. +As of v6.3.0, a value is bound to the type its field declares. A `List` 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`, 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`. -A map key with a dot is written and read as it always was: 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 - a key such as `my.rule` is stored split into nested levels (`my` → `rule`), and quoting it does not help. As of v6.3.0 this is no longer silent: when a start or reload finds such a key in the file, and before the framework writes one (a save, a first-boot default, a panel write), it logs a warning naming the file, the entry, the map's nested path and the module, saying the key will be split into nested levels the next time the file is loaded and asking the operator to rename it. Only entries declared as a `Map` are checked, and maps nested in them as far as the declared type says `Map`. The check only reads - it never changes a value or the file. A map that is an element of a list is plain data the file keeps whole, so a dotted key there is fine and is not warned about. Use `-` or `_` in map keys. +**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)). diff --git a/docs/src/zh/guide/essentials/config-file.md b/docs/src/zh/guide/essentials/config-file.md index b9db444..0688f57 100644 --- a/docs/src/zh/guide/essentials/config-file.md +++ b/docs/src/zh/guide/essentials/config-file.md @@ -82,11 +82,11 @@ YAML 会把 `1800` 这样的整数读成整数类型。自 v6.3.0 起,包装 #### 集合、映射与形状不对的值 -自 v6.3.0 起,配置值按字段声明的类型绑定。`List` 拿到的是 `Integer`,元素类型为 `Set`、`Long`、`Double`、`Boolean` 或枚举时同样会转换;映射的键和值会转换成映射声明的类型,值类型是你自己的类时仍然拿到原始的映射,与以前一致。旧版本写进文件的带引号数字(例如 `'30'`)会作为数字载入。无法转换的元素(例如 `List` 里的 `abc`)会被跳过,并记一条警告,写明文件、带元素位置的键、原值和声明类型,其余配置照常加载;空的列表项也同样跳过。`List`、`Set`、`SortedSet`、`Queue`、`EnumSet`、`Map`、`SortedMap`、`ConcurrentMap` 和 `EnumMap` 字段都支持,自定义 `parser` 的读写与以前完全一致。在此之前,列表的每个元素都按文字绑定,因此 `contains(30)` 这类按类型查找永远匹配不上。 +自 v6.3.0 起,配置值按字段声明的类型绑定。`List` 拿到的是 `Integer`,元素类型为 `Set`、`Long`、`Double`、`Boolean` 或枚举时同样会转换;映射的键和值会转换成映射声明的类型,值类型是你自己的类时仍然拿到原始的映射,与以前一致。旧版本写进文件的带引号数字(例如 `'30'`)会作为数字载入。无法转换的元素(例如 `List` 里的 `abc`)会被跳过,并记一条警告,写明文件、带元素位置的键、原值和声明类型,其余配置照常加载;空的列表项也同样跳过。`List`、`Set`、`SortedSet`、`Queue`、`EnumSet`、`Map`、`SortedMap`、`ConcurrentMap` 和 `EnumMap` 字段都支持,自定义 `parser`(包括继承 `DefaultConfigParser` 的)的读写与以前完全一致。在此之前,列表的每个元素都按文字绑定,因此 `contains(30)` 这类按类型查找永远匹配不上。映射或对象里有 `null` 值时,`save()` 现在不写这个值,并记一条警告,写明文件、配置项和它的嵌套路径;旧版本遇到它会抛 `NullPointerException` 中止保存,文件保持不变。 值的形状与字段不符时(例如声明为 `Map` 的地方写成了列表或单个值,数字字段里写了文字,或文字字段里写了 `2024-01-01` 这样的日期),字段保持声明的默认值,并记一条警告,写明文件、键、声明类型和文件里实际的内容。模块照常加载,服主的文件也不会被改写。在此之前,配置加载会抛出异常,模块无法启动。值本身的键名或其中任何一个键名像密钥时(例如 `password`、`token`),警告不会打印原值。 -含点的映射键仍按原来的方式写入和读取:配置文件用 `.` 作路径分隔符,在文件以配置节保存的映射里(映射字段、嵌套在其中的映射、对象里的映射),`my.rule` 这样的键会被拆成嵌套的几层(`my` → `rule`),加引号也没用。自 v6.3.0 起这不再是无声的:启动或重载时在文件里发现这样的键,以及框架写入这样的键之前(保存、首次启动写入默认值、面板写入),都会记一条警告,写明文件、配置项、嵌套路径和模块,说明这个键下次读取时会被拆成嵌套的几层,请服主改名。只检查声明为 `Map` 的配置项及其中声明为映射的嵌套映射。检查只读,不改任何值和文件。列表元素里的映射是普通数据,文件能原样保存,键里含点没有问题,也不警告。映射键请改用 `-` 或 `_`。 +**映射的键里不要用点**。配置文件用 `.` 作路径分隔符,在文件以配置节保存的映射里(映射字段、嵌套在其中的映射、存在其中的对象里的映射),`my.rule` 这样的键在写入和读取时都会被拆成嵌套的几层(`my` → `rule`),在 YAML 里给键加引号(`"my.rule":`)也没用。这与旧版本相同,框架既不拒绝也不警告,所以这类键请改用 `-` 或 `_`,声明的默认值和模块运行时生成的键都一样,也请这样告诉服主。列表元素里的映射是普通数据,文件能原样保存,键里的点会保留。 自 v6.3.0 起,`int`、`long`、`Integer` 或 `Long` 类型的字段还可以用来控制任务间隔或命令冷却:`@Scheduled` 见[绑定到配置项的时间](/zh/guide/advanced/scheduled-tasks#绑定到配置项的时间),`@CmdCD` 见[配置项绑定的冷却时间](/zh/guide/essentials/cmd-executor#配置项绑定的冷却时间)。该配置类必须为模块恰好注册一次,因此指向目录的 `@ConfigEntity` 不能用于绑定。被绑定的字段不要再加 [`@Range`](/zh/guide/advanced/config-validation):绑定自带范围检查,而 `/ul reload` 期间违反 `@Range` 会中止该模块其余的重载步骤([#509](https://github.com/UltiKits/UltiTools-Reborn/issues/509))。