目标:让游戏或模组向 Hovermate 发送事件,像内置游戏卡片一样生成时间轴标记并自动保存精彩时刻。三类传输方式覆盖可联网、仅可写文件和仅可写日志的模组环境。
1. 连接发现
SDK 开启时,Engine 写入 %LOCALAPPDATA%\Hovermate\Sdk\connection.json(设置 › 片段 › 游戏事件 SDK,默认开启):
{
"version": 1,
"url": "http://local.hovermate.gg/hovermate/v1/events",
"token": "<random, base64url, 32 bytes>",
"inbox": "C:\\Users\\<user>\\AppData\\Local\\Hovermate\\Sdk\\Inbox"
}url是事件的发送地址。请从此文件读取并按原样使用;切勿在代码中写死或更改主机、端口或路径。Hovermate 目前写入http://local.hovermate.gg/hovermate/v1/events;SDK 测试服务器会写入自己的测试地址。- 客户端在启动时及每次收到
401后读取该文件。令牌可能轮换。 HOVERMATE_SDK_CONNECTION=<path>可为测试、工具和隔离数据目录覆盖文件位置。客户端必须读取文件中的inbox字段获取收件目录,不能自行推导。- 文件缺失表示未安装 Hovermate 或 SDK 已关闭。客户端保持静默,不向游戏抛出异常。
2. 传输方式
2.1 HTTP(可联网模组:C#、C++、Java、Python、JS)
POST {url};请求头为Authorization: Bearer <token>、Content-Type: application/json。- 携带
Origin请求头的请求会以403拒绝,防止浏览器发送事件。 - 请求体为单个事件对象,或最多包含 100 个事件的
{ "events": [ ... ] }。最大请求体为 64 KB。 - 批次采用全有或全无原则。Hovermate 先验证全部事件;若有一个无效,则全部不接收,并在
400响应体中给出位置:{ "error": "<code>", "detail": "...", "index": 3 }。本会话中的新游戏,必须在批次首个提及它的事件中提供game.name。 -
响应:
202已接收;400无效,响应体为{ "error": "<code>", "detail": "...", "index": <n, batches only> };401令牌错误或缺失;403请求包含Origin请求头;413请求体过大。会先读取并丢弃最多 1 MB,以便客户端收到状态;更大的请求体可能在读取前即被拒绝,并关闭连接;429每个游戏每秒超过 20 个事件;405、411、415表示方法错误、缺少长度或内容类型错误。
- 客户端从后台发送,无需等待,不阻塞游戏线程。超时不超过 250 ms,队列有容量限制并丢弃最早事件,最多重试一次。
2.2 JSONL 收件目录(可写文件但不能联网的模组)
- 向
{inbox}\<game.id>.jsonl追加 UTF-8 行,每行一个事件对象,不带 BOM。 - 无需令牌:文件夹属于用户。
- Engine 启动时从收件目录内每个
*.jsonl文件的末尾开始监视追加内容。模组可在会话开始时清空自己的文件。
2.3 日志行约定(只能写游戏日志的脚本模组)
- 一行中包含标记
[HOVERMATE],后面紧跟一个 JSON 事件对象。时间戳、频道名等标记前内容均被忽略。 - SDK 卡片中的“从文件读取事件”列表供用户添加日志路径。通过其他方式收到的
hello事件(§4)也可提议路径。Engine 使用现有的日志追踪适配器读取文件。 - 对于脚本日志位置已知的游戏,Hovermate 会在游戏运行时自动监视。列表位于游戏目录数据(
sdkLogs)中,例如Documents\The Witcher 3\scriptslog.txt以及 Skyrim/Fallout 的 Papyrus 日志。这些模组只需开启游戏脚本日志,无需其他用户设置。
2.4 模组目录事件文件(仅能写入自身目录的沙箱)
- 一些模组环境只允许写入模组目录,例如 Cyber Engine Tweaks Lua 模组。此类模组向目录中的
hovermate-events.jsonl追加 JSONL 行(§2.2)。 - 用户将文件加入 SDK 卡片的“从文件读取事件”列表。模组 README 应说明文件位置。Hovermate 像监视收件文件一样读取其新增内容。
- 模组也可将自身路径以
[HOVERMATE]日志行输出一次,或在有其他传输方式时,通过路径数组hello.files发送。Hovermate 随后提议添加。
3. 事件对象
{
"v": 1,
"game": { "id": "com.example.mygame", "name": "My Game", "exe": "MyGame.exe" },
"type": "kill",
"label": "Double kill",
"tags": ["headshot"],
"context": "Act 2 · The City",
"time": "2026-11-09T21:15:03.120Z"
}v— 必填,值为1。game.id— 必填,符合[a-z0-9._-]{3,64}。反向 DNS 名称或简短标识符,须在模组版本间保持稳定。game.name— 每个会话的首个事件或hello中必填;最多 64 个字符。game.exe— 可选,仅文件名。关联已检测的游戏目录,并允许该可执行文件启动回放缓冲。type— 必填。使用Docs/CLIPS.md中共享列表的标准类型:kill, death, assist, multikill, roundWon, roundLost, matchWon, matchLost, mvp, objective, lap, bestLap, position, crash, incident, levelUp, bossKill, zone, achievement, goal, save, mission, fightWon, penalty, pit, finish, raceWon, landing, takeoff;或"custom"。Docs/CLIPS.md中的列表为权威定义,只会扩充。未知标准类型以400拒绝。customType/customLabel— 当type为"custom"时必填。customType符合[a-z0-9_.-]{1,32};customLabel最多 40 个字符,是卡片中的开关显示名。label— 可选,事件显示文本,用于标记提示或片段标题,最多 80 个字符。tags— 可选,最多 8 个标签,每个符合[a-z0-9_-]{1,24}。context— 可选,用于精彩片段标题的地图、关卡或区域,最多 64 个字符。time— 可选,ISO-8601 UTC。Engine 记录接收时刻;与其相差 ±5 秒内的time可修正该时刻,其他值忽略。- 未知字段会被忽略,因此未来版本仅新增字段。
- 字符限制按 UTF-16 代码单元计算,与 JavaScript 的
length和 .NET 的string.Length一致。模式须匹配整个值,尾随换行不合法。空字符串或仅空白的字符串视为缺失。 - 不要发送个人数据。标签和上下文可能出现在片段标题和文件名中。
4. hello(可选,推荐)
{ "v": 1, "game": { "id": "com.example.mygame", "name": "My Game", "exe": "MyGame.exe" },
"type": "hello",
"offers": ["kill", "death", "bossKill", { "customType": "parry", "customLabel": "Parry" }],
"log": "%LOCALAPPDATA%\\MyGame\\Saved\\Logs\\MyGame.log" }- 允许 Hovermate 在首个真实事件到达前创建游戏卡片与精彩片段开关。
offers列出标准类型和/或自定义类型对象。- 可选的
log提议日志行方式的日志文件;可选的files提议模组事件文件(§2.4)。Hovermate 读取任何提议路径之前都会询问用户。 hello可通过所有方式接收,包括[HOVERMATE]日志行。
5. Hovermate 行为
- 新
game.id的首个有效事件或hello创建一张“SDK”游戏卡片。它使用名称、exe;若 exe 匹配已安装游戏,则显示目录和封面;每种声明或收到的类型有独立开关,并提供事件前后时长设置。用户开启类型之前不保存精彩片段,但始终写入标记。 - 事件经过与内置卡片相同的处理流程:事件记录、片段和会话标记、精彩时刻窗口、通知。
- 当
game.exe匹配检测到的前台游戏时,事件归属于该游戏;否则仅在录制或缓冲运行时接收,并标记为 SDK 游戏。 -
限制:
- 每个游戏每秒 20 个事件;
- 每个游戏 200 个自定义类型;
- 超过 4 KB 的收件行将被丢弃;
- 错误行会计数,并显示在卡片状态中(“N 个无效事件”),绝不原样记录其内容。