开发指南
配置与数据存储
8.1 ctx.config():读写 data/mods/<模组id>/config.json
ModConfig 是扁平的 key-value JSON 读写接口,封装 data/mods/<模组id>/config.json。
| 方法 | 说明 |
|---|---|
String get(String key, String defaultValue) | 读字符串,不存在返回默认值 |
int getInt(String key, int defaultValue) | 读整数,不存在或类型不符返回默认值 |
boolean getBool(String key, boolean defaultValue) | 读布尔,不存在或类型不符返回默认值 |
double getDouble(String key, double defaultValue) | 读浮点,不存在或类型不符返回默认值 |
void set(String key, Object value) | 写入配置(自动持久化到磁盘) |
Map<String, Object> getAll() | 读取全部配置(只读快照,之后的 set 不反映到已返回的 Map) |
关键语义:
- 值类型限定
String/Integer/Double/Boolean/List<Object>;复杂结构需模组自行 JSON 序列化成 String 存进单个 key。 - 懒加载:首次调用时才加载文件;文件不存在或解析失败则返回默认值,且不创建空文件(不要指望「读一次就自动生成 config.json」)。
set时同步写盘:不需要也不应该在退出时额外 flush。- 实例方法 synchronized 保护,可在事件处理器与滤镜中并发访问(游戏线程写、渲染线程读是安全的)。
8.2 ctx.getDataDir():模组自有数据的落地位置
ctx.getDataDir() 返回 data/mods/<模组id>/,首次访问自动创建目录(含父目录)。约定与建议:
config.json只归ctx.config()使用,不要手写这个文件;- 其它自有文件(缓存、自建索引、导出的数据)放在该目录下自行管理,路径用
dataDir.resolve(...)拼,不要硬编码data/mods/...; - 目录创建失败时宿主不抛异常,自行读写时再处理错误。
java
Path dataDir = ctx.getDataDir();
Path cacheFile = dataDir.resolve("cache.json");
if (Files.exists(cacheFile)) {
// 读回上次的自有数据
}8.3 模组自带配置界面:Mod.createConfigScene()
模组实现 Mod.createConfigScene() 并返回一个 Scene,玩家即可在模组列表里按 C 进入该场景进行配置;返回 null(默认)表示模组没有配置界面。
典型做法(可参考真实模组 mods-hinthud):onPreInit / onInit 期间建好配置访问层(持有 ctx.config()),createConfigScene() 在玩家按下 C 时才被调用,因此它只能返回已经准备好依赖的场景实例 —— 若配置层或场景宿主还没就绪,就直接返回 null,避免抛出空指针。
java
public final class MyMod implements GameplayMod {
private MyConfig cfg;
private SceneHost sceneHost;
@Override
public ModInfo info() {
return ModInfo.of("mymod", "My Mod", "0.1.0", List.of("You"), "带配置界面的示例");
}
@Override
public void onPreInit(ModContext ctx) {
cfg = new MyConfig(ctx.config());
}
@Override
public void onInit(ModContext ctx) {
sceneHost = ctx.capabilities().get(SceneHost.class);
}
@Override
public Scene createConfigScene() {
return cfg != null ? new MyConfigScene(cfg, sceneHost) : null;
}
}配置场景本身就是一个普通的 Scene:render(RenderCanvas) 自绘列表项,handleInput(InputEvent) 处理方向键 / 回车,改动即时写回配置。
8.4 最小示例:读配置 → 玩家改 → 存回
把「键名 + 默认值 + 取值收敛」集中在一个小的配置访问层里,渲染与配置界面共享同一实例,改值即时生效并持久化。
java
/** 配置访问层:键名集中定义,读写都收敛到默认值与合法区间。 */
final class MyConfig {
private static final String KEY_HINT_ENABLED = "hintEnabled";
private static final String KEY_ALPHA = "alpha";
private final ModConfig cfg;
MyConfig(ModConfig cfg) {
this.cfg = cfg;
}
/** 读:文件不存在 / 键缺失时返回默认值,且不创建空文件。 */
boolean hintEnabled() {
return cfg.getBool(KEY_HINT_ENABLED, true);
}
double alpha() {
double a = cfg.getDouble(KEY_ALPHA, 0.85);
return a < 0.0 ? 0.0 : (a > 1.0 ? 1.0 : a);
}
/** 写:set 调用即同步写盘。 */
void setHintEnabled(boolean v) {
cfg.set(KEY_HINT_ENABLED, v);
}
void setAlpha(double v) {
cfg.set(KEY_ALPHA, Math.max(0.0, Math.min(1.0, v)));
}
}java
// 配置界面里:玩家按键 → 改值 → 存回
@Override
public boolean handleInput(InputEvent event) {
if (event.type() == InputType.KEY_PRESSED
&& event.keyCode() == java.awt.event.KeyEvent.VK_ENTER) {
cfg.setHintEnabled(!cfg.hintEnabled()); // 读到内存 → 翻转 → 同步落盘
return true;
}
return false;
}java
// 渲染时实时读回(同一实例,玩家改完立刻生效)
private void renderHud(RenderCanvas canvas) {
if (cfg.hintEnabled()) {
canvas.setAlpha(cfg.alpha());
canvas.setColor(Color.WHITE);
canvas.drawText("HINT", 20, 40, TextStyle.of(14, Color.WHITE));
}
}