Theseus 模组文档
开发指南

配置与数据存储


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)

关键语义:

8.2 ctx.getDataDir():模组自有数据的落地位置

ctx.getDataDir() 返回 data/mods/<模组id>/,首次访问自动创建目录(含父目录)。约定与建议:

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));
    }
}