SDK degli eventi di gioco · Protocollo v1

Invia eventi di gioco a Hovermate

Un gioco o una mod può segnalare uccisioni, boss sconfitti, giri, gol o momenti personalizzati. Hovermate crea indicatori temporali e clip automatiche, esattamente come con le schede di gioco integrate.

Client C#, C++, Java, Python, JavaScript e Lua · Un file ciascuno, senza dipendenze · Licenza MIT

Come funziona

  1. La mod segnala un momento. Basta una chiamata, ad esempio event("bossKill"). Il client invia in background senza bloccare il gioco.
  2. Hovermate lo contrassegna. Ogni evento diventa un indicatore sulla sequenza temporale della registrazione e della sessione, con la tua etichetta.
  3. Il giocatore sceglie i momenti salienti. Il gioco ottiene una scheda in Clip. Per ogni tipo attivato dal giocatore viene salvata anche una clip.

Quando l’SDK è attivo, Hovermate pubblica indirizzo, token e cartella di ricezione in 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"
}

Leggi url da questo file e usalo esattamente come è scritto; non scrivere mai nel codice né modificare host, porta o percorso.

I client leggono %LOCALAPPDATA%\Hovermate\Sdk\connection.json, oppure il percorso in HOVERMATE_SDK_CONNECTION per test e configurazioni isolate. Lo rileggono dopo una risposta 401: il token cambia quando l’SDK viene disattivato e riattivato. Se il file manca, Hovermate non è installato o l’SDK è disattivato; i client rimangono silenziosi.

Tre modi per inviare eventi

Usa il primo consentito dall’ambiente della tua mod.

HTTP

La mod può aprire una connessione di rete locale.

Invia l’evento con POST all’URL usando Authorization: Bearer <token>. Le richieste con intestazione Origin vengono rifiutate, quindi le pagine web non possono inviare eventi.

C#, C++, Java, Python, JavaScript; GDScript può usare HTTPRequest.

File

La mod può scrivere file, ma non accedere alla rete.

Aggiungi un evento JSON per riga a {inbox}\<game.id>.jsonl; non serve un token. Una sandbox limitata alla cartella della mod, come Cyber Engine Tweaks, scrive invece in hovermate-events.jsonl al suo interno; il giocatore aggiunge il file nella scheda del gioco.

Tutti i client ripiegano sulla cartella di ricezione; il client Lua scrive nella cartella della mod.

Righe di log

Gli script possono scrivere soltanto nel log del gioco.

Scrivi [HOVERMATE] seguito da un oggetto evento JSON; tutto ciò che precede il marcatore viene ignorato. scriptslog.txt di The Witcher 3 e Papyrus.0.log di Skyrim e Fallout 4 vengono letti automaticamente durante il gioco.

Script di Witcher 3, Papyrus o qualsiasi gioco con un log degli script.

Una richiesta 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"}

Risposta: 202 {"accepted":1}.

Una riga di log

Una riga nel log del gioco
[12:00:01] [Script] [HOVERMATE] {"v":1,"game":{"id":"com.example.mygame","name":"My Game"},"type":"bossKill","context":"Act 2"}

Per un altro log, il giocatore lo aggiunge in Clip › scheda del gioco › Leggi eventi da un file; anche hello può proporlo.

Guida rapida

Ogni esempio invia hello, kill, bossKill e un evento personalizzato parry. Sono i file eseguibili della cartella examples dell’SDK.

C#

Ambiente
netstandard2.0, .NET Framework 4.7.2+ e .NET con C# 7.3: Unity, BepInEx, MelonLoader.
Configurazione
Aggiungi HovermateEvents.cs al progetto della mod.
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();

Esegui in examples/csharp (l’esempio richiede .NET 8 o versioni successive):

dotnet run

C++

Ambiente
C++17 su Windows. Solo header; usa WinHTTP e collega winhttp.lib.
Configurazione
Includi hovermate_events.h. Chiama hovermate::shutdown() prima di scaricare la DLL, mai dentro 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;
}

Esegui da un prompt dei comandi per sviluppatori, in examples/cpp:

cl /std:c++17 /EHsc /utf-8 /I..\..\cpp example.cpp

Java

Ambiente
Java 8 e versioni successive: ModTheSpire, Forge, Fabric.
Configurazione
Copia HovermateEvents.java nella mod e modifica il package, oppure rilocalizzalo durante lo shading, per evitare conflitti con copie in altre mod.
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);
    }
}

Esegui in examples/java:

javac -d out ../../java/HovermateEvents.java Example.java && java -cp out Example

Python

Ambiente
Python 3.7 e versioni successive, solo libreria standard.
Configurazione
Metti hovermate_events.py accanto allo script.
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()

Esegui in examples/python:

python example.py

JavaScript

Ambiente
Node.js 18 e versioni successive, come modulo ES.
Configurazione
Importa 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();

Esegui in examples/js:

node example.mjs

Lua

Ambiente
Lua 5.1+ e LuaJIT: Cyber Engine Tweaks e altre sandbox senza rete.
Configurazione
Metti hovermate_events.lua accanto a init.lua. Gli eventi finiscono in hovermate-events.jsonl nella cartella della mod: chiedi ai giocatori di aggiungere il file in 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()

Esegui nella cartella che rappresenta quella della mod:

lua example.lua

Comportamento dei client

ChiamataFunzione
init(gameId, gameName, exe?)Avvia il client. gameId corrisponde a [a-z0-9._-]{3,64} e resta invariato tra le versioni della mod; exe è il nome dell’eseguibile del gioco.
hello(offers, log?, files?)Annuncia i tipi di eventi disponibili ed eventualmente un log o file della mod da leggere.
event(type, {label, tags, context})Invia un tipo di evento standard.
custom(customType, customLabel, {…})Invia un tipo personalizzato; customLabel ne nomina l’interruttore dei momenti salienti.
flush() / shutdown()Attende gli eventi in coda e arresta il worker in background quando la mod viene scaricata.
  • Le chiamate terminano subito e non propagano mai eccezioni al gioco.
  • Un worker in background invia gli eventi con timeout di 250 ms e riprova una sola volta le richieste fallite.
  • La coda contiene 256 eventi e scarta il più vecchio.
  • Quando HTTP non è raggiungibile, gli eventi vanno alla cartella di ricezione per alcuni secondi.
  • Senza connection.json il client resta silenzioso e ricontrolla ogni 5 secondi.
  • Ogni evento contiene game.name. Il testo viene tagliato ai limiti indicati sotto e i tag non validi vengono scartati senza generare errori.

Il client Lua (hovermate_events.lua) scrive subito ogni evento. Preferisce hovermate-events.jsonl nella cartella della mod, poi la cartella di ricezione se sono permessi percorsi assoluti, infine righe [HOVERMATE] tramite una funzione writer fornita, come print. logLine(type, options) restituisce una riga da stampare autonomamente.

L’oggetto evento

{ "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" }
CampoObbligatorioRegola
vsì1
game.idsì[a-z0-9._-]{3,64}: nome DNS inverso o identificatore breve, stabile tra le versioni della mod
game.nameprimo eventoNel primo evento o hello della sessione; massimo 64 caratteri
game.exenoSolo un nome di file. Collega gli eventi alla cartella del gioco rilevato e permette all’eseguibile di avviare il buffer di replay
typesìUn tipo standard sotto oppure custom
customType, customLabelper custom[a-z0-9_.-]{1,32} e un nome per l’interruttore di massimo 40 caratteri
labelnoTesto del singolo evento per il suggerimento del marcatore e il titolo della clip; massimo 80 caratteri
tagsnoMassimo 8 tag, ognuno conforme a [a-z0-9_-]{1,24}
contextnoMappa, livello o area per i titoli dei momenti salienti; massimo 64 caratteri
timenoISO-8601 UTC. Hovermate registra l’ora di ricezione; un time entro 5 s la precisa

Le lunghezze contano unità UTF-16: un’emoji vale 2. I campi di testo rifiutano i caratteri di controllo. I campi sconosciuti vengono ignorati, quindi le versioni future aggiungono soltanto campi. Non inviare dati personali: etichette e contesti possono comparire nei titoli delle clip e nei nomi dei file.

Tipi standard

29 tipi condivisi con le schede integrate di Hovermate. L’elenco cresce soltanto; i tipi standard sconosciuti vengono rifiutati.

  • 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

Tipi personalizzati

Per i momenti non previsti dall’elenco standard. Ogni tipo ottiene un interruttore chiamato customLabel; massimo 200 per gioco.

{ "v": 1, "game": { "id": "com.example.mygame" },
  "type": "custom", "customType": "parry", "customLabel": "Parry", "label": "Perfect parry" }

hello

Facoltativo e consigliato. Crea la scheda del gioco con gli interruttori prima del primo evento reale, così il giocatore può configurarla in anticipo.

{ "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 elenca tipi standard e oggetti { "customType", "customLabel" }.
  • log propone un log con righe [HOVERMATE]; files propone file di eventi nella cartella della mod. Hovermate chiede il consenso del giocatore prima di leggere un percorso proposto.
  • hello funziona con ogni trasporto, comprese le righe di log [HOVERMATE].

Limiti e risposte

LimiteValore
Eventi per gioco20 al secondo. Oltre il limite si riceve 429 e nessun evento della richiesta viene accettato; hello non viene contato
Lotto{ "events": [ … ] } con 1–100 eventi, accettati o rifiutati tutti insieme. Il primo evento che menziona un nuovo gioco nel lotto deve nominarlo
Corpo HTTP64 KB
Righe dei file di ingresso4 KB; le righe più lunghe vengono scartate
Tipi personalizzati200 per gioco
Timeout dei clientMassimo 250 ms, un solo nuovo tentativo, coda limitata che scarta l’evento più vecchio
StatoSignificato
202Accettato: {"accepted": n}
400Non valido: {"error", "detail", "index"}. index indica il primo evento non valido del lotto
401Token mancante o errato: rileggi connection.json
403La richiesta contiene un’intestazione Origin
413Corpo oltre 64 KB
429Più di 20 eventi al secondo per un gioco
405, 411, 415Metodo diverso da POST, Content-Length mancante o tipo di contenuto non JSON

Codici di errore: invalid_json, version, game, type, custom, label, tags, context, offers, log, files, batch. Le righe errate nei file vengono contate e mostrate nella scheda del gioco come eventi non validi; Hovermate non ne registra mai il testo grezzo.

Test senza Hovermate

Il ricevitore di prova mock-server.py (Python 3.8+, libreria standard) risponde come Hovermate. Crea un proprio connection.json in una cartella temporanea, applica le stesse regole e gli stessi codici di errore, segue la cartella di ingresso e i file passati con --watch e stampa ogni evento ed errore.

A differenza di Hovermate, il ricevitore di prova ascolta su una porta numerica di loopback e scrive quell’indirizzo nel proprio connection.json. È solo uno strumento di test: i client usano l’url presente nel file.

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
Output del ricevitore di prova
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 segue un file della mod hovermate-events.jsonl o un log del gioco con righe [HOVERMATE].
  • --follow-proposals legge i percorsi proposti da hello, come se il giocatore li avesse autorizzati.
  • --record FILE aggiunge ogni evento accettato e rifiutato come riga JSON, per verifiche automatiche.

HOVERMATE_SDK_CONNECTION funziona allo stesso modo con Hovermate: i test non toccano mai il vero file di connessione.

Casi di conformità

L’SDK include conformance/cases.json: richieste con le risposte attese di Hovermate, dagli eventi validi a ciascun codice di errore. conformance/run.py le invia al ricevitore di prova o ad altri ricevitori. Hovermate usa lo stesso file nei propri test.

python conformance/run.py
# or against a running receiver:
python conformance/run.py --connection path\to\connection.json

Cosa vede il giocatore

  • Il primo evento valido o hello di un nuovo game.id crea una scheda SDK in Clip, con nome del gioco, cartella e copertina se exe corrisponde a un gioco installato, e un interruttore per ciascun tipo annunciato o ricevuto.
  • Gli interruttori dei momenti salienti partono disattivati: il giocatore sceglie quali tipi salvano clip. I marcatori vengono sempre scritti.
  • Gli eventi seguono lo stesso percorso delle schede integrate: registro, marcatori di clip e sessioni, finestra dei momenti salienti e notifiche.
  • Se game.exe corrisponde al gioco in primo piano, gli eventi sono attribuiti a quel gioco. Altrimenti vengono accettati solo durante una registrazione o con il buffer di replay attivo.
  • Gli eventi non validi vengono contati nello stato della scheda, così i giocatori possono segnalare problemi.

Protocollo e file

Il protocollo v1 è il contratto completo: connessione, quattro modalità di consegna, oggetto evento, hello e comportamento di Hovermate.

L’SDK, inclusi client, esempi, strumenti, casi di conformità e protocollo, ha licenza MIT: puoi distribuire un client nella tua mod. Hovermate stesso non è coperto da questa licenza.