Skip to content

Latest commit

 

History

History
210 lines (166 loc) · 10.7 KB

File metadata and controls

210 lines (166 loc) · 10.7 KB

Phira-mp+ 插件开发文档

Phira-mp+ 支持 WASM 插件 — 编译为 WebAssembly 后通过 wasmtime 动态加载。

内置插件系统(NativePlugin trait)已合并入服务器核心代码,不再支持独立原生插件注册。 外部扩展请使用 WASM 插件。

WASM 插件

WASM 插件通过 wasmtime 运行时动态加载,部署在 plugins/ 目录下(可通过 -d 参数自定义)。

JSON ABI

WASM 插件通过 JSON 字符串与宿主通信:

导出函数:

函数 签名 说明
phira_init () -> i32 初始化插件,返回 0 表示成功
phira_get_info () -> () 填写插件元数据到内存偏移 0
phira_cleanup () -> () 插件卸载时清理
phira_on_event (ptr: i32, len: i32) -> i32 事件处理,返回 0=已处理 1=未处理
phira_alloc (size: i32) -> i32 分配线性内存,返回指针
phira_dealloc (ptr: i32, size: i32) 释放内存

导入函数(宿主提供):

函数 说明
phira:host/log(level_ptr, level_len, msg_ptr, msg_len) 记录日志
phira:host/uuid(out_ptr, out_len) 生成 UUID v4
phira:host/time() -> i64 获取 Unix 时间戳(毫秒)
phira:host/api(method_ptr, method_len, args_ptr, args_len, out_ptr, out_len) -> i32 通用 API 桥接

通用 API (phira:host/api)

所有方法通过 JSON 参数调用,返回 JSON 结果。返回码:0=成功,非0=错误。

方法 参数 返回 说明
state.query { method, params } 查询结果 统一状态查询入口
player.touches { user_id } 最近触控数据 查询用户最近触控帧
player.judges { user_id } 最近判定数据 查询用户最近判定事件
round.data { round_uuid, player_id } 完整 Touches/Judges 轮次数据查询
round.list {} 轮次列表 所有已记录轮次
send.to_user { user_id, message } "ok" 发送消息给指定用户
send.to_room { room_id, message } "ok" 向房间广播消息
send.to_all { message } "ok" 向所有用户广播
ext.get_user { user_id, key } 字段值 获取用户扩展数据
ext.set_user { user_id, key, value } "ok" 设置用户扩展数据
ext.get_room { room_id, key } 字段值 获取房间扩展数据
ext.set_room { room_id, key, value } "ok" 设置房间扩展数据
room.create_empty { room_id, endpoint? } {"ok": true, "room_id": string} 创建无人持久空房间,endpoint 为空时使用全局 Phira API
room.set_persistent_empty { room_id, persistent } {"ok": true, "persistent_empty": bool} 设置房间无人时是否保留
room.kick { room_id, target_id } {"ok": true} 从房间踢出用户
room.set_host / room.clear_host { room_id, target_id? } {"ok": true} 设置房主;target_id:null? 表示系统 ? 房主
room.set_lock { room_id, locked } {"ok": true} 锁定/解锁房间
room.force_move { room_id, target_id, monitor } {"ok": true} 强制迁移用户到房间
room.set_hidden { room_id, hidden } {"ok": true, "hidden": bool} 设置隐藏状态
room.is_hidden { room_id } {"hidden": bool} 查询隐藏状态
room.set_phira_api_endpoint { room_id, endpoint } {"ok": true, "phira_api_endpoint": string} 设置房间独立 Phira API,传 null/default 清除
room.get_phira_api_endpoint { room_id } {"phira_api_endpoint": string, "using_room_override": bool} 查询当前生效 Phira API
room.clear_phira_api_endpoint { room_id } {"ok": true} 清除房间覆盖,恢复全局配置
room.close { room_id } {"ok": true} 解散房间
admin.kick_user { user_id, reason } {"ok": true} 从服务器踢出用户
admin.ban_user { user_id, reason } {"ok": true, "reason": string} 封禁用户,返回规范化后的原因
admin.unban_user { user_id } {"ok": true} 解封用户
admin.is_banned { user_id } {"banned": bool, "reason": string?} 检查封禁状态及原因
admin.ban_list {} 封禁列表 获取所有封禁
admin.list_users {} 用户列表 列出所有在线用户
persist.events { since_sequence?, limit?, kind?, room_id?, user_id? } 事件数组 读取统一 PG 持久化事件
persist.rooms { since_sequence?, limit? } 房间快照数组 读取统一 PG 房间快照
persist.playtime { user_id } 游玩时间 读取统一 PG 游玩时间
persist.top_playtime { limit? } 排行数组 读取游玩时间排行
persist.touches { since_sequence?, limit?, round_uuid?, player_id? } 批次数组 从 PostgreSQL 读取 Touches 追加批次
persist.judges { since_sequence?, limit?, round_uuid?, player_id? } 批次数组 从 PostgreSQL 读取 Judges 追加批次
admin.ids / admin.is_admin / admin.add_id / admin.remove_id / admin.set_ids docs/api.md 管理员 ID 读取或维护游戏内管理员
plugin.api_call { plugin, method, args } 调用结果 调用其他插件注册的 API
plugin.api_register { method } 注册确认 注册本插件 API 供其他插件调用
config.get { key } 配置值 获取插件配置
config.set { key, value } "ok" 设置插件配置
http.get { url } 响应正文 HTTP GET 请求
http.post { url, body, content_type } 响应正文 HTTP POST 请求
file.read { path } 文件内容 读取插件数据文件
file.write { path, content } "ok" 写入插件数据文件
uuid.v4 {} UUID 字符串 生成 UUID
time.now {} Unix 时间戳 获取当前时间

canonical ABI 定义见 wit/phira-plugin.wit(当前使用 JSON bridge 传输)。旧版指针见 wit/phira/mpplus.wit。其中 persistenceadmin-config import interface 对应统一 PG 持久化读取和管理员 ID 管理。

插件事件

WASM 插件通过 phira_on_event 接收事件,事件以 JSON 格式传入。type 字段标识事件类型:

type 数据字段 触发时机
user_connect user_id, user_name, user_ip 用户认证通过后
user_disconnect user_id, user_name 用户断开连接
room_create user_id, room_id 创建房间
room_join user_id, room_id, is_monitor 加入房间
room_leave user_id, room_id 离开房间
room_modify user_id, room_id, data 修改房间设置
game_start user_id, room_id 游戏开始
game_end user_id, user_name, room_id, score, accuracy, perfect, good, bad, miss, max_combo, full_combo 玩家提交成绩
player_touches user_id, room_id, data 玩家触控事件
player_judges user_id, room_id, data 玩家判定事件
round_complete room_id, chart_id, chart_name 一轮游戏完成

示例 — 监听用户连接事件:

// 在 phira_on_event 中:
function phira_on_event(event_ptr, event_len) {
  let json = memory_to_string(event_ptr, event_len);
  let event = JSON.parse(json);
  if (event.type === 'user_connect') {
    console.log(`User ${event.user_name}(${event.user_id}) connected from ${event.user_ip}`);
  }
  return 0; // 0 = handled
}

插件元数据

WASM 插件可通过两种方式声明元数据:

  1. 导出 phira_get_info — 插件自行将 JSON 元数据写入线性内存
  2. 内存偏移 0 — 在 WASM 模块的偏移 0 处放置长度前缀的 JSON(备用模式)
{
  "name": "my-plugin",
  "version": "0.1.0",
  "author": "me",
  "description": "My WASM plugin"
}

插件间 API 调用

WASM 插件可以通过 plugin.api_call 调用其他插件注册的 API:

// 调用 playtime-tracker 插件的 count 方法
let result = api.call('plugin.api_call', {
  plugin: 'player-tracker',
  method: 'count',
  args: []
});
// result = {"count": 42}

通过 plugin.api_register 注册自己的 API:

api.call('plugin.api_register', {
  method: 'my_custom_api'
});

注意:完整双向 WASM 回调目前为 stub,注册后可被调用但返回固定响应。 原生 Rust 注册的 API(通过 PluginManager::register_plugin_api)可被 WASM 插件正常调用。

实时数据流

Touches/Judges 数据通过 player_touchesplayer_judges 事件推送到 WASM 插件的 phira_on_event 处理器,无需主动轮询。配置 PostgreSQL 后,服务端同时写入 mp_round_touch_batches / mp_round_judge_batches 追加批次表;插件可用 persist.touches / persist.judges 按全局 sequence 增量读取,保留期由 touch_judge_retention_days 控制,未设置时遵循 persistence_retention_days

构建 WASM 插件

WASM 插件需编译为目标 wasm32-unknown-unknown

rustup target add wasm32-unknown-unknown
cargo build --target wasm32-unknown-unknown --release

生成的 .wasm 文件放在服务器的 plugins/ 目录下,下次启动或执行 plugin reload 时加载。

服务端配置

WASM 插件可通过 config.get / config.set API 读写自己的配置(内存中,重启消失)。 持久化配置建议使用 file.read / file.write 操作 data/plugins/<plugin_name>/ 目录。

WIT 接口定义

旧版 WIT 指针见 wit/phira/mpplus.wit;canonical ABI 见 wit/phira-plugin.wit,包含以下接口:

  • user-events — 用户事件监听
  • user-info — 用户信息查询
  • room-info — 房间信息查询
  • messaging — 消息发送
  • room-management — 房间管理
  • user-management — 用户管理
  • utilities — 工具函数
  • database — 数据库接口(预留)
  • plugin-config — 插件配置
  • plugin — 插件主入口
  • cli — CLI 命令接口

Runtime v2 插件 ABI 方向

当前 WASM 插件仍使用 abi-json-v1:宿主把事件和 API 参数编码成 JSON 字符串,写入 guest memory,再由插件侧解析。这不是 Runtime v2 的最终 ABI,因为它有几个问题:

  • host 与 guest 的字段 schema 不一致时,编译期发现不了;
  • Touch/Judge 这类大 payload 需要重复 JSON 编解码;
  • ABI 版本、字段变更、必填字段缺失都缺少统一 contract test;
  • 项目已经有 WIT/WASM Host API 方向,但 JSON 字符串仍是实际传输层。

Step 34 起,所有 JSON ABI 编解码应集中在 plugin_abi.rs。后续应先补齐 contract tests,再把 guest-facing ABI 迁移到 typed WIT/component-model 接口。