ConfigService 将 Java 配置对象映射为插件数据目录中的 YAML 或 JSON 文件。Java 类声明结构与默认值,磁盘文件保存管理员可以修改的实际配置。
配置类使用 @ConfigFile,并遵循文件服务概览 ⇱中的对象约束:
@ConfigFile(name = "config", format = FileType.YAML)
@NamingStyle(NamingStyle.Style.KEBAB)
@Comment("插件主配置文件")
public class MainConfig {
@Comment("当前全局语言")
public String language = "zh_CN";
@Key("debug")
public boolean debugMode = false;
public Database database = new Database();
public static class Database {
public String host = "127.0.0.1";
public int port = 3306;
}
}
默认 KEBAB 命名会把 debugMode 转换为 debug-mode;@Key("debug") 会覆盖自动命名。@Comment 只写入 YAML,JSON 不保存注释。
@ConfigFile 的主要参数为:
| 参数 | 作用 | ||
|---|---|---|---|
name | 不含扩展名的文件名,默认为 config | ||
path | 相对于插件数据目录的子目录 | ||
format | FileType.YAML 或 FileType.JSON | ||
emit | 是否允许创建、修复和写回文件,默认为 true |
例如:
@ConfigFile(name = "database", path = "settings", format = FileType.JSON)
public class DatabaseConfig {
}
对应文件为 plugins/<YourPlugin>/settings/database.json。
ConfigService configs = lin.linFile().config();
MainConfig config = configs.bind(MainConfig.class);
绑定会读取或创建文件、执行适用的版本迁移、合并缺失默认值并校验字段。全部检查通过后,服务才更新活动对象。错误定位和失败保护见配置校验 ⇱。
已有文件缺少代码声明的键时,活动对象会先使用代码默认值,插件可以继续读取配置;原文件不会因此自动改变。允许输出时,服务会生成 config-diff.yml 之类的差异文件,列出缺失路径和待写入的默认值。差异文件仅供管理员查看,不参与配置读取。
管理员确认差异内容后,可以执行:
/linlang files repair <插件名称>
这条命令会重新读取目标插件已经绑定的配置和语言文件,再把当时仍然缺失的键写入原文件。插件名称支持 Tab 补全,但不能指定 Linlang 运行时插件本身。它不会直接套用先前缓存的差异,因此管理员在检测后所做的修改不会被旧快照覆盖。修复期间不会再次生成或更新差异文件,也不会重复输出缺失键告警;已有差异文件保留为本次修复前的记录。
需要一次修复全部已注册插件时,可以执行:
/linlang files repair-all
批量修复会隔离每个目标的失败;某个插件修复失败时,其余插件仍会继续处理。命令先输出检查、修复和失败总数,再逐项列出实际发生修复的插件及其配置键、语言键数量;没有变化的插件不会重复列出。
开发者也可以在明确的管理操作中调用:
int repaired = configs.repairMissingKeys();
返回值是本次实际补入的键数。普通 bind(...) 和 reload() 不代表修复授权。
需要让服务器持续自动修复时,可以在 plugins/LinlangRuntimeBukkit/config.yml 中明确开启:
file-service:
auto-repair-missing-keys: true
该策略在运行时启动时读取并应用。修改后执行 /linlang reload-all,运行时会在重载各插件前重新应用策略,因此本次重载发现的缺失键即可按新设置处理。该设置作用于运行时自身的文件服务以及随后创建的插件 Facade,默认值为 false。
YAML 中每个补入的键前都会写入 # [Linlang] 缺失键修复自动补入。JSON 语法不允许注释,Linlang 不会插入额外元数据破坏配置结构,而是保留对应的 -diff.json 作为修复记录。
业务代码修改活动对象后,可以保存所有已绑定配置:
config.debugMode = true;
configs.saveAll();
saveAll() 会用内存状态覆盖磁盘内容。管理员可能已经编辑文件时,应先决定是保存内存修改还是执行重载,不要无条件保存。
configs.reload();
重载会尽量原地更新根对象,但未保存的内存修改不会与磁盘内容合并。失败文件保留旧活动值,其他文件仍继续重载;调用方应检查汇总异常,不能在部分失败后发送“全部成功”。
Map、List、数组和 ConfigText 字段可能在重载中被替换,应从根配置对象重新取得。Facade 的参数应用不会读取配置文件;完整的 Facade 重建则需要重新取得服务并绑定。
只读配置可以使用 @ConfigFile(emit = false)、@NoEmit 或 bind(..., false)。三者都会禁止创建文件、生成差异、修复缺失键和显式保存,详细规则见文件服务概览 ⇱。
| 注解 | 作用 | ||
|---|---|---|---|
@ConfigFile | 声明文件名、子目录、格式和写回策略 | ||
@NamingStyle | 设置字段的自动命名方式 | ||
@Key | 覆盖单个字段的文件键名 | ||
@Comment | 为 YAML 类或字段写入注释 | ||
@NoEmit | 禁止生成和写回文件 | ||
@ConfigVersion | 声明结构版本和版本键 |
@ConfigVersion 在文件中记录结构版本,默认版本键为 _linlang-version:
@ConfigFile(name = "config")
@ConfigVersion(2)
public class MainConfig {
public String newName = "default";
}
迁移器必须在绑定配置类之前注册:
configs.registerMigrator(new Migrator() {
public int from() {
return 1;
}
public int to() {
return 2;
}
public boolean supports(Class<?> type) {
return type == MainConfig.class;
}
public void migrate(MutableDocument doc) {
doc.set("new-name", doc.get("old-name"));
doc.remove("old-name");
}
});
MainConfig config = configs.bind(MainConfig.class);
服务从文件版本开始连续执行迁移,再按新结构填充对象。迁移链缺失、同一起始版本存在多个适用迁移器,或文件版本高于代码支持版本时,绑定会失败。
每一步的 to() 必须向目标版本推进。可以注册 1 -> 2、2 -> 3,也可以由一个迁移器完成 1 -> 3;批量注册使用 registerMigrators(...)。
讨论