仕組み
- MOD が出来事を通知します。
event("bossKill")などの呼び出し 1 回で送信できます。クライアントはバックグラウンドで動作し、ゲームをブロックしません。 - Hovermate がマーカーを付けます。指定したラベル付きで、録画とセッションのタイムラインにイベントが表示されます。
- プレイヤーがハイライトを選びます。「クリップ」にゲーム専用カードが作られます。有効にした種類のイベントはクリップとしても保存されます。
SDK が有効な間、Hovermate はアドレス、トークン、受信フォルダーを connection.json に書き込みます。
{
"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 はこのファイルから読み取り、書かれているとおりに使ってください。ホスト、ポート、パスをコードに埋め込んだり変更したりしないでください。
クライアントは %LOCALAPPDATA%\Hovermate\Sdk\connection.json を読みます。テストや分離した環境では HOVERMATE_SDK_CONNECTION のパスを使用できます。SDK のオフ・オンでトークンが変わるため、401 の後は再読込します。ファイルがなければ Hovermate が未インストールか SDK が無効なので、何も送信しません。
イベントを送る 3 つの方法
MOD の環境で利用できる最初の方法を選んでください。
HTTP
MOD がローカルのネットワーク接続を開ける場合。
Authorization: Bearer <token> を付けて URL にイベントを POST します。Origin ヘッダーのあるリクエストは拒否されるため、ウェブページからは送信できません。
C#、C++、Java、Python、JavaScript に対応。GDScript では HTTPRequest を使用できます。
ファイル
MOD がファイルを書き込めるものの、ネットワークを利用できない場合。
{inbox}\<game.id>.jsonl に 1 行 1 件の JSON イベントを追記します。トークンは不要です。Cyber Engine Tweaks のように MOD フォルダーのみ書込み可能な環境では、その中の hovermate-events.jsonl に記録し、プレイヤーがゲームカードにファイルを追加します。
各クライアントは受信フォルダーへフォールバックします。Lua クライアントは MOD フォルダーに書き込みます。
ログ行
スクリプトがゲームのログにしか出力できない場合。
[HOVERMATE] に続けて JSON イベントオブジェクトを出力します。マーカーより前は無視されます。The Witcher 3 の scriptslog.txt、Skyrim と Fallout 4 の Papyrus.0.log はゲーム実行中に自動で読み込まれます。
Witcher 3 のスクリプト、Papyrus、またはスクリプトログのあるゲーム。
HTTP リクエスト
POST /hovermate/v1/events HTTP/1.1
Host: local.hovermate.gg
Authorization: Bearer <token from connection.json>
Content-Type: application/json
Content-Length: 95
{"v":1,"game":{"id":"com.example.mygame","name":"My Game"},"type":"kill","label":"Double kill"}応答:202 {"accepted":1}。
ログ行の例
[12:00:01] [Script] [HOVERMATE] {"v":1,"game":{"id":"com.example.mygame","name":"My Game"},"type":"bossKill","context":"Act 2"}他のログは「クリップ」› ゲームカード › ファイルからイベントを読むで追加します。hello から提案することもできます。
クイックスタート
各サンプルは hello、kill、bossKill と独自の parry イベントを送ります。SDK の examples フォルダーにある実行可能なファイルを掲載しています。
C#
- クライアント
- HovermateEvents.cs
- 実行環境
- netstandard2.0、.NET Framework 4.7.2+、.NET、C# 7.3:Unity、BepInEx、MelonLoader。
- セットアップ
- MOD プロジェクトに HovermateEvents.cs を追加します。
// Sends hello, kill, bossKill and a custom "parry" event to Hovermate (or to tools/mock-server.py).
// Run: dotnet run
using System;
using Hovermate.Sdk;
HovermateEvents.Log = Console.WriteLine;
HovermateEvents.Init("com.example.sdkdemo", "SDK Demo", "SdkDemo.exe");
HovermateEvents.Hello(new HovermateOffer[] { "kill", "bossKill", HovermateOffer.Custom("parry", "Parry") });
HovermateEvents.Event("kill", label: "Double kill", tags: new[] { "headshot" }, context: "Act 2");
HovermateEvents.Event("bossKill", label: "The Warden", context: "Act 2");
HovermateEvents.Custom("parry", "Parry", label: "Perfect parry");
Console.WriteLine(HovermateEvents.Flush(2000) ? "sent" : "timed out");
HovermateEvents.Shutdown();examples/csharp で実行します(サンプルは .NET 8 以降が対象)。
dotnet runC++
- クライアント
- hovermate_events.h
- 実行環境
- Windows 上の C++17。ヘッダーのみで、WinHTTP を使い winhttp.lib をリンクします。
- セットアップ
- hovermate_events.h をインクルードします。DLL のアンロード前に hovermate::shutdown() を呼び出してください。DllMain 内では呼び出さないでください。
// Sends hello, kill, bossKill and a custom "parry" event to Hovermate (or to tools/mock-server.py).
// Build (Developer Command Prompt): cl /std:c++17 /EHsc /utf-8 /I..\..\cpp example.cpp
#include <cstdio>
#include "hovermate_events.h"
int main() {
hovermate::set_log([](const std::string& message) { std::printf("%s\n", message.c_str()); });
hovermate::init("com.example.sdkdemo", "SDK Demo", "SdkDemo.exe");
hovermate::hello({ "kill", "bossKill", hovermate::Offer::custom("parry", "Parry") });
hovermate::event("kill", { "Double kill", { "headshot" }, "Act 2" });
hovermate::event("bossKill", { "The Warden", {}, "Act 2" });
hovermate::custom("parry", "Parry", { "Perfect parry", {}, {} });
std::printf("%s\n", hovermate::flush(2000) ? "sent" : "timed out");
hovermate::shutdown();
return 0;
}開発者コマンドプロンプトで examples/cpp から実行します。
cl /std:c++17 /EHsc /utf-8 /I..\..\cpp example.cppJava
- クライアント
- HovermateEvents.java
- 実行環境
- Java 8 以降:ModTheSpire、Forge、Fabric。
- セットアップ
- HovermateEvents.java を MOD にコピーし、パッケージを変更するか shading 時に再配置して、他の MOD 内のコピーとの衝突を防ぎます。
// Sends hello, kill, bossKill and a custom "parry" event to Hovermate (or to tools/mock-server.py).
// Run (Java 8+): javac -d out ../../java/HovermateEvents.java Example.java && java -cp out Example
import gg.hovermate.sdk.HovermateEvents;
import gg.hovermate.sdk.HovermateEvents.Offer;
import java.util.Arrays;
public class Example {
public static void main(String[] args) {
HovermateEvents.log = System.out::println;
HovermateEvents.init("com.example.sdkdemo", "SDK Demo", "SdkDemo.exe");
HovermateEvents.hello(Offer.of("kill"), Offer.of("bossKill"), Offer.custom("parry", "Parry"));
HovermateEvents.event("kill", "Double kill", Arrays.asList("headshot"), "Act 2");
HovermateEvents.event("bossKill", "The Warden", null, "Act 2");
HovermateEvents.custom("parry", "Parry", "Perfect parry", null, null);
System.out.println(HovermateEvents.flush(2000) ? "sent" : "timed out");
HovermateEvents.shutdown(1000);
}
}examples/java で実行します。
javac -d out ../../java/HovermateEvents.java Example.java && java -cp out ExamplePython
- クライアント
- hovermate_events.py
- 実行環境
- Python 3.7 以降。標準ライブラリのみを使用します。
- セットアップ
- スクリプトと同じ場所に hovermate_events.py を置きます。
"""Sends hello, kill, bossKill and a custom "parry" event to Hovermate (or to tools/mock-server.py).
Run from any folder: python example.py
"""
import os
import sys
sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)), '..', '..', 'python'))
import hovermate_events as hovermate # noqa: E402
hovermate.set_log(print)
hovermate.init('com.example.sdkdemo', 'SDK Demo', 'SdkDemo.exe')
hovermate.hello(['kill', 'bossKill', hovermate.custom_offer('parry', 'Parry')])
hovermate.event('kill', label='Double kill', tags=['headshot'], context='Act 2')
hovermate.event('bossKill', label='The Warden', context='Act 2')
hovermate.custom('parry', 'Parry', label='Perfect parry')
print('sent' if hovermate.flush(2.0) else 'timed out')
hovermate.shutdown()examples/python で実行します。
python example.pyJavaScript
- クライアント
- hovermate-events.mjs
- 実行環境
- Node.js 18 以降。ES モジュールとして使用します。
- セットアップ
- hovermate-events.mjs をインポートします。
// Sends hello, kill, bossKill and a custom "parry" event to Hovermate (or to tools/mock-server.py).
// Run with Node.js 18+: node example.mjs
import * as hovermate from '../../js/hovermate-events.mjs';
hovermate.setLog(console.log);
hovermate.init('com.example.sdkdemo', 'SDK Demo', 'SdkDemo.exe');
hovermate.hello(['kill', 'bossKill', hovermate.customOffer('parry', 'Parry')]);
hovermate.event('kill', { label: 'Double kill', tags: ['headshot'], context: 'Act 2' });
hovermate.event('bossKill', { label: 'The Warden', context: 'Act 2' });
hovermate.custom('parry', 'Parry', { label: 'Perfect parry' });
console.log((await hovermate.flush(2000)) ? 'sent' : 'timed out');
await hovermate.shutdown();examples/js で実行します。
node example.mjsLua
- クライアント
- hovermate_events.lua
- 実行環境
- Lua 5.1+、LuaJIT:Cyber Engine Tweaks などネットワークのないサンドボックス。
- セットアップ
- init.lua の隣に hovermate_events.lua を置きます。イベントは MOD フォルダーの hovermate-events.jsonl に出力されます。このファイルを Hovermate に追加するようプレイヤーに案内してください。
-- Sends hello, kill, bossKill and a custom "parry" event to Hovermate (or to tools/mock-server.py --watch).
-- Run from the folder that stands in for your mod folder: lua path/to/example.lua
-- It writes hovermate-events.jsonl into the current folder.
--
-- In a Cyber Engine Tweaks mod, copy hovermate_events.lua next to init.lua and call it from your events:
-- local hovermate = require("hovermate_events")
-- registerForEvent("onInit", function()
-- hovermate.init("com.example.mymod", "My Mod", "Cyberpunk2077.exe", { log = print })
-- hovermate.hello({ "kill", "bossKill", hovermate.customOffer("parry", "Parry") })
-- end)
-- Then tell your users to add <game>\bin\x64\plugins\cyber_engine_tweaks\mods\<your mod>\hovermate-events.jsonl
-- in Hovermate: Clips > the game's card > Read events from a file.
local here = (arg and arg[0] or ''):match('^(.*[\\/])') or './'
package.path = here .. '../../lua/?.lua;' .. package.path
local hovermate = require('hovermate_events')
hovermate.init('com.example.sdkdemo', 'SDK Demo', 'SdkDemo.exe', { log = print, writer = print })
print('transport', hovermate.transport())
hovermate.hello({ 'kill', 'bossKill', hovermate.customOffer('parry', 'Parry') })
hovermate.event('kill', { label = 'Double kill', tags = { 'headshot' }, context = 'Act 2' })
hovermate.event('bossKill', { label = 'The Warden', context = 'Act 2' })
hovermate.custom('parry', 'Parry', { label = 'Perfect parry' })
print('log line for script-only mods:', hovermate.logLine('kill', { label = 'Double kill' }))
hovermate.shutdown()MOD フォルダーに相当するフォルダーで実行します。
lua example.luaクライアントの動作
| 呼び出し | 動作 |
|---|---|
init(gameId, gameName, exe?) | クライアントを開始します。gameId は [a-z0-9._-]{3,64} に一致し、MOD のバージョン間で変更しません。exe はゲームの実行ファイル名です。 |
hello(offers, log?, files?) | 送信可能なイベントの種類と、任意で読み取るログや MOD フォルダーのファイルを通知します。 |
event(type, {label, tags, context}) | 標準タイプのイベントを送信します。 |
custom(customType, customLabel, {…}) | 独自タイプを送信します。customLabel がハイライト切り替えの表示名になります。 |
flush() / shutdown() | キュー内のイベントを待ち、MOD のアンロード時にバックグラウンドワーカーを停止します。 |
- 呼び出しはすぐに戻り、ゲームへ例外を送出しません。
- バックグラウンドワーカーがタイムアウト 250 ms で送信し、失敗したリクエストは 1 回だけ再試行します。
- キューは 256 イベントを保持し、超過時は最も古いものを破棄します。
- HTTP に接続できない場合、数秒間は受信フォルダーにイベントを送信します。
connection.jsonがない場合は何もせず、5 秒ごとに再確認します。- すべてのイベントに
game.nameが含まれます。文字列は以下の上限で切り詰められ、不正なタグはエラーにせず削除されます。
Lua クライアント(hovermate_events.lua)は各イベントを即座に書き込みます。MOD フォルダーの hovermate-events.jsonl、絶対パスが許可される場合は受信フォルダー、最後に渡された writer 関数(例:print)による [HOVERMATE] 行の順で試します。logLine(type, options) は手動で出力できる行を返します。
イベントオブジェクト
{ "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 名または短い識別子。MOD のバージョン間で固定 |
game.name | 最初のイベント | セッション最初のイベントまたは hello で必須。最大 64 文字 |
game.exe | いいえ | ファイル名のみ。検出したゲームフォルダーにイベントを関連付け、その実行ファイルによるリプレイバッファの開始を可能にします |
type | はい | 以下の標準タイプ、または custom |
customType, customLabel | custom の場合 | [a-z0-9_.-]{1,32} と最大 40 文字の切り替え名 |
label | いいえ | マーカーのツールチップやクリップ名に使うイベントの表示文。最大 80 文字 |
tags | いいえ | 最大 8 個。それぞれ [a-z0-9_-]{1,24} に一致するタグ |
context | いいえ | ハイライトのタイトルに使うマップ、レベル、エリア。最大 64 文字 |
time | いいえ | ISO-8601 UTC。Hovermate が受信時刻を記録し、その前後 5 秒以内の time で補正します |
長さは UTF-16 コード単位で数えるため、絵文字は 2 です。テキストの制御文字は拒否されます。未知のフィールドは無視し、将来の版はフィールドを追加する方式です。個人情報は送らないでください。ラベルやコンテキストはクリップ名やファイル名に表示される場合があります。
標準タイプ
Hovermate 内蔵のゲームカードと共通の 29 タイプ。リストは追加のみで、未知の標準タイプは拒否されます。
killdeathassistmultikillroundWonroundLostmatchWonmatchLostmvpgoalsaveobjectivemissionlapbestLappositioncrashincidentpenaltypitfinishraceWonlandingtakeofffightWonlevelUpbossKillzoneachievement
独自タイプ
標準リストにない出来事に使用します。各タイプに customLabel の名前でハイライト切り替えを作成します。1 ゲームあたり最大 200 タイプです。
{ "v": 1, "game": { "id": "com.example.mygame" },
"type": "custom", "customType": "parry", "customLabel": "Parry", "label": "Perfect parry" }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",
"files": ["C:\\Games\\MyGame\\mods\\my_mod\\hovermate-events.jsonl"] }offersは標準タイプと{ "customType", "customLabel" }オブジェクトを列挙します。logは[HOVERMATE]行を含むゲームログ、filesは MOD フォルダーのイベントファイルを提案します。Hovermate は提案されたパスを読む前にプレイヤーへ確認します。helloはすべての送信方法で使用でき、[HOVERMATE]ログ行にも対応します。
制限と応答
| 制限 | 値 |
|---|---|
| ゲームごとのイベント数 | 毎秒 20 件。超過すると 429 となり、そのリクエスト全体を拒否します。hello は数えません |
| バッチ | { "events": [ … ] } に 1~100 イベント。全件受理または全件拒否です。新しいゲームの名前は、バッチ内で最初にそのゲームを参照するイベントで指定します |
| HTTP 本文 | 64 KB |
| 受信・ファイルの行 | 4 KB。長い行は破棄します |
| 独自タイプ | 1 ゲームにつき 200 |
| クライアントのタイムアウト | 最大 250 ms、再試行は最大 1 回。上限付きキューから最古のイベントを削除します |
| ステータス | 意味 |
|---|---|
202 | 受理:{"accepted": n} |
400 | 不正:{"error", "detail", "index"}。index はバッチ内の最初の不正イベントを示します |
401 | トークンがない、または不正:connection.json を再読込してください |
403 | リクエストに Origin ヘッダーがあります |
413 | 本文が 64 KB を超えています |
429 | 1 ゲームで毎秒 20 イベントを超えています |
405, 411, 415 | POST 以外、Content-Length の欠落、または JSON 以外のコンテンツタイプ |
エラーコード:invalid_json、version、game、type、custom、label、tags、context、offers、log、files、batch。ファイル内の不正な行はゲームカードの不正イベント数に加算します。Hovermate は内容をそのままログに記録しません。
Hovermate なしでテスト
テスト受信機 mock-server.py(Python 3.8+、標準ライブラリ)は Hovermate と同じ応答を返します。一時フォルダーに専用の connection.json を作成し、同じルールとエラーコードを適用します。受信フォルダーと --watch のファイルを監視し、各イベントとエラーを表示します。
Hovermate と異なり、テスト受信機は数値のループバックポートで待ち受け、そのアドレスを自分の connection.json に書き込みます。テスト用のツールにすぎません。クライアントはファイルにある url をそのまま使います。
python tools/mock-server.py
# In another terminal, with the path it printed:
$env:HOVERMATE_SDK_CONNECTION = "C:\Users\you\AppData\Local\Temp\hovermate-sdk-mock-…\connection.json"
python examples/python/example.pyHovermate SDK mock listening on http://127.0.0.1:52777/hovermate/v1/events
[21:15:03.120] http com.example.sdkdemo hello offers=kill,bossKill,custom:parry
[21:15:03.131] http com.example.sdkdemo kill "Double kill" tags=headshot context="Act 2"
[21:15:03.140] http com.example.sdkdemo bossKill "The Warden" context="Act 2"
[21:15:03.148] http com.example.sdkdemo custom:parry "Parry" "Perfect parry"--watch FILEは MOD フォルダーのhovermate-events.jsonl、または[HOVERMATE]行のあるゲームログの追記を読み取ります。--follow-proposalsはプレイヤーが許可したものとして、helloが提案するパスを読みます。--record FILEは受理・拒否した各イベントを JSON の 1 行として追記し、自動検証に利用できます。
HOVERMATE_SDK_CONNECTION は Hovermate 本体でも同じように使えるため、テストで本番の接続ファイルを変更しません。
適合性テスト
SDK の conformance/cases.json には、正常なイベントから各エラーコードまで、リクエストと Hovermate の期待応答が含まれます。conformance/run.py でテスト受信機や他の受信機へ送れます。Hovermate 自身のテストでも同じファイルを使用します。
python conformance/run.py
# or against a running receiver:
python conformance/run.py --connection path\to\connection.jsonプレイヤーからの見え方
- 新しい
game.idの最初の正常なイベントまたはhelloで「クリップ」に SDK カードを作成します。ゲーム名、exeがインストール済みゲームに一致する場合のフォルダーとカバー、通知・受信した各タイプの切り替えが表示されます。 - ハイライトの切り替えは最初はオフです。どのタイプでクリップを保存するかはプレイヤーが選びます。マーカーは常に記録されます。
- イベントは内蔵カードと同じ経路で処理されます。履歴、クリップとセッションのマーカー、ハイライトウィンドウ、通知が対象です。
game.exeが前面のゲームに一致する場合、そのゲームのイベントとして扱います。それ以外は録画中またはリプレイバッファの動作中のみ受理します。- 不正なイベント数がカードの状態に表示されるため、プレイヤーが問題を報告できます。
プロトコルとファイル
プロトコル v1 は、接続検出、4 つの配信方法、イベントオブジェクト、hello、Hovermate の動作を規定します。
SDK(クライアント、サンプル、ツール、適合性テスト、プロトコル)は MIT ライセンスのため、MOD にクライアントを同梱できます。Hovermate 本体にはこのライセンスは適用されません。