SDK ігрових подій · Протокол v1

Надсилайте ігрові події в Hovermate

Гра або мод можуть повідомляти про вбивства, перемоги над босами, кола, голи чи власні події. Hovermate створює маркери шкали часу й автоматично зберігає яскраві моменти, як для вбудованих карток ігор.

Клієнти для C#, C++, Java, Python, JavaScript і Lua · По одному файлу без залежностей · Ліцензія MIT

Як це працює

  1. Мод повідомляє про подію. Достатньо одного виклику, наприклад event("bossKill"). Клієнт надсилає її у фоні й не блокує гру.
  2. Hovermate ставить маркер. Кожна подія з вашою назвою з’являється на шкалі запису та сесії.
  3. Гравець обирає яскраві моменти. Гра отримує власну картку в розділі «Кліпи». Для ввімкнених гравцем типів подій також зберігаються кліпи.

Поки SDK увімкнено, Hovermate записує адресу, токен і папку вхідних подій у connection.json:

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 для тестів та ізольованих налаштувань. Після відповіді 401 файл читається знову: токен змінюється після вимкнення й увімкнення SDK. Якщо файла немає, Hovermate не встановлено або SDK вимкнено; клієнти нічого не надсилають.

Три способи передавання подій

Оберіть перший спосіб, доступний у середовищі вашого мода.

HTTP

Мод може відкривати локальне мережеве з’єднання.

Надішліть подію методом POST на вказаний URL із Authorization: Bearer <token>. Запити із заголовком Origin відхиляються, тому вебсторінки не можуть надсилати події.

C#, C++, Java, Python, JavaScript; у GDScript можна використати HTTPRequest.

Файли

Мод може записувати файли, але не має доступу до мережі.

Додавайте по одній події JSON на рядок у {inbox}\<game.id>.jsonl; токен не потрібен. У середовищі з записом лише до папки мода, як Cyber Engine Tweaks, записуйте там hovermate-events.jsonl. Гравець додає цей файл у картці гри.

Усі клієнти за недоступності HTTP переходять на вхідну папку; клієнт Lua пише файл у папці мода.

Рядки журналу

Скрипти можуть писати лише у власний журнал гри.

Виведіть [HOVERMATE] , а потім один об’єкт події JSON; усе перед маркером ігнорується. Журнали scriptslog.txt The Witcher 3 і Papyrus.0.log Skyrim та Fallout 4 читаються автоматично, доки гра працює.

Скрипти 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. Це готові до запуску файли з папки examples SDK.

C#

Клієнт
HovermateEvents.cs
Середовище виконання
netstandard2.0, .NET Framework 4.7.2+ і .NET із C# 7.3: Unity, BepInEx, MelonLoader.
Налаштування
Додайте HovermateEvents.cs до проєкту мода.
csharp/Program.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 run

C++

Клієнт
hovermate_events.h
Середовище виконання
C++17 у Windows. Лише заголовочний файл; використовується WinHTTP і підключається winhttp.lib.
Налаштування
Підключіть hovermate_events.h. Викличте hovermate::shutdown() перед вивантаженням DLL, за межами DllMain.
cpp/example.cpp
// 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.cpp

Java

Середовище виконання
Java 8 і новіші версії: ModTheSpire, Forge, Fabric.
Налаштування
Скопіюйте HovermateEvents.java до мода й змініть пакет або перемістіть його під час shading, щоб копії в різних модах не конфліктували.
java/Example.java
// 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 Example

Python

Середовище виконання
Python 3.7 і новіші версії, лише стандартна бібліотека.
Налаштування
Розмістіть hovermate_events.py поруч зі скриптом.
python/example.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.py

JavaScript

Середовище виконання
Node.js 18 і новіші версії, модуль ES.
Налаштування
Імпортуйте hovermate-events.mjs.
js/example.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.mjs

Lua

Середовище виконання
Lua 5.1+ і LuaJIT: Cyber Engine Tweaks та інші середовища без мережі.
Налаштування
Розмістіть hovermate_events.lua поруч з init.lua. Події записуються в hovermate-events.jsonl у папці мода; попросіть гравців додати цей файл у Hovermate.
lua/example.lua
-- 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()

Запустіть із папки, що використовується замість папки мода:

lua example.lua

Поведінка клієнтів

ВикликДія
init(gameId, gameName, exe?)Запускає клієнт. gameId відповідає [a-z0-9._-]{3,64} і не змінюється між версіями мода; exe — назва виконуваного файла гри.
hello(offers, log?, files?)Оголошує типи подій гри та, за потреби, журнал або файли в папці мода для читання.
event(type, {label, tags, context})Надсилає подію стандартного типу.
custom(customType, customLabel, {…})Надсилає власний тип події; customLabel визначає назву його перемикача яскравих моментів.
flush() / shutdown()Очікує на надсилання черги й зупиняє фоновий обробник під час вивантаження мода.
  • Виклики повертаються одразу й не передають винятки у гру.
  • Фоновий обробник надсилає події з тайм-аутом 250 мс і один раз повторює невдалий запит.
  • Черга вміщує 256 подій; у разі переповнення видаляється найстаріша.
  • Якщо HTTP недоступний, події кілька секунд надсилаються до вхідної папки.
  • Без connection.json клієнт мовчить і повторює перевірку кожні 5 секунд.
  • Кожна подія містить game.name. Текст обрізається до вказаних нижче меж, а некоректні теги відкидаються без помилки.

Клієнт Lua (hovermate_events.lua) записує події одразу. Спочатку використовується hovermate-events.jsonl у папці мода, потім вхідна папка, якщо дозволені абсолютні шляхи, а потім рядки [HOVERMATE] через передану функцію writer, наприклад print. Виклик 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-ім’я або короткий ідентифікатор, незмінний між версіями мода
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 позначає час отримання; time у межах 5 с уточнює цю позначку

Довжина рахується в кодових одиницях UTF-16: емодзі займає 2. Керівні символи в тексті заборонені. Невідомі поля ігноруються, тому нові версії лише додають поля. Не надсилайте персональні дані: підписи й контекст можуть потрапити в назви кліпів і файлів.

Стандартні типи

29 типів, спільних із вбудованими картками ігор Hovermate. Список лише розширюється; невідомий стандартний тип відхиляється.

  • kill
  • death
  • assist
  • multikill
  • roundWon
  • roundLost
  • matchWon
  • matchLost
  • mvp
  • goal
  • save
  • objective
  • mission
  • lap
  • bestLap
  • position
  • crash
  • incident
  • penalty
  • pit
  • finish
  • raceWon
  • landing
  • takeoff
  • fightWon
  • levelUp
  • bossKill
  • zone
  • achievement

Власні типи

Для подій поза стандартним списком. Кожен тип має окремий перемикач із назвою customLabel; на гру допускається до 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 — файли подій у папці мода. Перед читанням запропонованого шляху Hovermate запитує дозвіл гравця.
  • hello працює через будь-який канал передавання, зокрема рядок журналу [HOVERMATE].

Обмеження та відповіді

ОбмеженняЗначення
Події на гру20 за секунду. У разі перевищення повертається 429, і весь запит відхиляється; hello не враховується
Пакет{ "events": [ … ] } із 1–100 подіями, цілком або ніяк. Нову гру треба назвати в першій події пакета, яка її згадує
Тіло HTTP-запиту64 КБ
Рядки вхідних файлів4 КБ; довші рядки відкидаються
Власні типи200 на гру
Тайм-аути клієнтаДо 250 мс, щонайбільше один повтор; обмежена черга видаляє найстарішу подію
СтатусЗначення
202Прийнято: {"accepted": n}
400Некоректні дані: {"error", "detail", "index"}. index вказує першу помилкову подію пакета
401Токен відсутній або неправильний: перечитайте connection.json
403Запит містить заголовок Origin
413Тіло запиту понад 64 КБ
429Понад 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, тестовий приймач слухає числовий порт на loopback-адресі й записує цю адресу у свій 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.py
Вивід тестового приймача
Hovermate 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 читає додані рядки файла мода hovermate-events.jsonl або журналу гри з рядками [HOVERMATE].
  • --follow-proposals читає шляхи, запропоновані в hello, наче гравець дозволив доступ.
  • --record FILE записує кожну прийняту й відхилену подію рядком JSON для автоматичних перевірок.

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

Що бачить гравець

  • Перша правильна подія або hello нового game.id створює картку SDK у «Кліпах»: назва гри, папка й обкладинка, якщо exe відповідає встановленій грі, та перемикач кожного оголошеного чи отриманого типу.
  • Спочатку яскраві моменти вимкнено: гравець обирає типи для збереження кліпів. Маркери записуються завжди.
  • Події проходять той самий шлях, що й вбудовані картки: журнал, маркери в кліпах і сесіях, вікно яскравих моментів, сповіщення.
  • Якщо game.exe відповідає активній грі, події належать їй. Інакше вони приймаються лише під час запису чи роботи буфера повтору.
  • Помилкові події рахуються в статусі картки, щоб гравець міг повідомити про проблему.

Протокол і файли

Протокол v1 визначає повний контракт: пошук підключення, чотири способи доставки, об’єкт події, hello і поведінку Hovermate.

SDK — клієнти, приклади, інструменти, перевірки відповідності та протокол — доступний за MIT, тож клієнт можна додати до мода. На сам Hovermate ця ліцензія не поширюється.