v1.9.8 新增
项目数据库 API 让扩展通过稳定契约使用项目数据集合。扩展只认识自己在 manifest 中声明的逻辑别名,不接触项目文件路径或真实集合 id;Studio 负责创建、选择、绑定和版本兼容检查。

声明数据依赖

extension.jsondataDependencies 中为每项依赖声明别名、契约和版本范围:
{
  "id": "example.inventory",
  "name": "背包系统",
  "version": "1.0.0",
  "sdkVersion": "^1.9.8",
  "entry": "dist/index.js",
  "dataDependencies": {
    "items": {
      "name": "道具目录",
      "description": "背包和商店共同读取的道具定义",
      "contract": "community.avg.item-catalog",
      "version": "^1.0.0",
      "access": "read",
      "autoCreate": {
        "name": "道具目录",
        "contractVersion": "1.0.0",
        "runtimePolicy": "readonly",
        "schema": {
          "type": "object",
          "required": ["name", "price"],
          "columns": [
            { "key": "name", "label": "名称", "type": "text" },
            { "key": "price", "label": "价格", "type": "number" },
            { "key": "icon", "label": "图标", "type": "image" }
          ]
        }
      }
    }
  }
}
字段说明
contract社区或团队约定的稳定契约 id
version扩展接受的 semver 范围
accessreadruntime-write
optional可选依赖;缺失时不阻止扩展继续使用其它能力
autoCreate缺少兼容集合时的创建模板;设为 false 时必须由创作者手工绑定
同一契约可以由多个扩展共享。契约升级时用新的 contractVersion 表示实际创建版本,并让消费方逐步扩大或调整接受范围。

获取集合

在剧本方法或其它已注入上下文的位置,通过依赖别名获取集合:
static listAffordableItems = method({
  title: "查询可购买道具",
  async run(ctx) {
    const items = ctx.database.collection("items");
    return items.find(
      { price: { $lte: 100 } },
      { sort: { price: 1 }, limit: 20 },
    );
  },
});
collection() 接收的是 manifest 中的 items 别名,不是 Studio 数据集合页面里的表名或内部 id。依赖完成绑定后,集合始终可用,不需要连接或关闭数据库。

查询

const items = ctx.database.collection("items");

const one = await items.getById("item-id");
const result = await items.find(
  {
    $and: [
      { category: "medicine" },
      { price: { $gte: 10, $lte: 100 } },
    ],
  },
  { sort: { price: 1 }, skip: 0, limit: 50 },
);
字段过滤支持 $eq$ne$gt$gte$lt$lte$in$contains$exists,也可以用 $and / $or 组合。所有方法均为异步 API,不应依赖当前 JSON 文件实现或同步读取项目目录。

新增、更新和删除

需要写入的依赖应声明 access: "runtime-write",数据表运行时策略也必须允许写入:
const inventory = ctx.database.collection("inventory");

const created = await inventory.insertOne({ itemId: "key", count: 1 });

await inventory.updateOne(
  { id: created.id },
  { $inc: { count: 1 }, $set: { equipped: true } },
);

await inventory.deleteOne({ id: created.id });
记录 id 由宿主生成且不可修改。更新操作支持 $set$unset$inc$push$pull;返回值包含命中与实际修改的记录数。

监听变化

const stopWatching = ctx.database.collection("inventory").watch(() => {
  // 重新查询并刷新界面
});

// 界面卸载或扩展停止时
stopWatching();
监听器只通知集合有效数据发生变化,不直接携带完整记录。收到通知后重新调用 find(),可以避免事件载荷和当前数据不同步。

运行时策略与权限

策略扩展写入结果
readonly拒绝运行时写入,只允许查询
session只保留到本次游戏关闭
archive保存到当前玩家存档
profile保存到跨存档共享档案
扩展身份彼此隔离,只能通过自己声明并完成绑定的别名访问集合。不要绕过 API 读取 databases/ 文件,也不要假设另一扩展的别名、真实集合 id 或底层存储格式。
删除字段或收窄契约前,先发布兼容迁移并确认所有消费扩展已经升级。Studio 会检查契约版本,但不会替业务数据自动推断复杂迁移规则。
创作者如何建立和维护数据表,见项目数据集合