Come funziona
- La mod segnala un momento. Basta una chiamata, ad esempio
event("bossKill"). Il client invia in background senza bloccare il gioco. - Hovermate lo contrassegna. Ogni evento diventa un indicatore sulla sequenza temporale della registrazione e della sessione, con la tua etichetta.
- 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:
{
"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
[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#
- Client
- HovermateEvents.cs
- 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.
// 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 runC++
- Client
- hovermate_events.h
- 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.
// 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.cppJava
- Client
- HovermateEvents.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.
// 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 ExamplePython
- Client
- hovermate_events.py
- Ambiente
- Python 3.7 e versioni successive, solo libreria standard.
- Configurazione
- Metti hovermate_events.py accanto allo script.
"""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.pyJavaScript
- Client
- hovermate-events.mjs
- Ambiente
- Node.js 18 e versioni successive, come modulo ES.
- Configurazione
- Importa 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();Esegui in examples/js:
node example.mjsLua
- Client
- hovermate_events.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.
-- 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.luaComportamento dei client
| Chiamata | Funzione |
|---|---|
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.jsonil 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" }| Campo | Obbligatorio | Regola |
|---|---|---|
v | sì | 1 |
game.id | sì | [a-z0-9._-]{3,64}: nome DNS inverso o identificatore breve, stabile tra le versioni della mod |
game.name | primo evento | Nel primo evento o hello della sessione; massimo 64 caratteri |
game.exe | no | Solo un nome di file. Collega gli eventi alla cartella del gioco rilevato e permette all’eseguibile di avviare il buffer di replay |
type | sì | Un tipo standard sotto oppure custom |
customType, customLabel | per custom | [a-z0-9_.-]{1,32} e un nome per l’interruttore di massimo 40 caratteri |
label | no | Testo del singolo evento per il suggerimento del marcatore e il titolo della clip; massimo 80 caratteri |
tags | no | Massimo 8 tag, ognuno conforme a [a-z0-9_-]{1,24} |
context | no | Mappa, livello o area per i titoli dei momenti salienti; massimo 64 caratteri |
time | no | ISO-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.
killdeathassistmultikillroundWonroundLostmatchWonmatchLostmvpgoalsaveobjectivemissionlapbestLappositioncrashincidentpenaltypitfinishraceWonlandingtakeofffightWonlevelUpbossKillzoneachievement
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"] }offerselenca tipi standard e oggetti{ "customType", "customLabel" }.logpropone un log con righe[HOVERMATE];filespropone file di eventi nella cartella della mod. Hovermate chiede il consenso del giocatore prima di leggere un percorso proposto.hellofunziona con ogni trasporto, comprese le righe di log[HOVERMATE].
Limiti e risposte
| Limite | Valore |
|---|---|
| Eventi per gioco | 20 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 HTTP | 64 KB |
| Righe dei file di ingresso | 4 KB; le righe più lunghe vengono scartate |
| Tipi personalizzati | 200 per gioco |
| Timeout dei client | Massimo 250 ms, un solo nuovo tentativo, coda limitata che scarta l’evento più vecchio |
| Stato | Significato |
|---|---|
202 | Accettato: {"accepted": n} |
400 | Non valido: {"error", "detail", "index"}. index indica il primo evento non valido del lotto |
401 | Token mancante o errato: rileggi connection.json |
403 | La richiesta contiene un’intestazione Origin |
413 | Corpo oltre 64 KB |
429 | Più di 20 eventi al secondo per un gioco |
405, 411, 415 | Metodo 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.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 FILEsegue un file della modhovermate-events.jsonlo un log del gioco con righe[HOVERMATE].--follow-proposalslegge i percorsi proposti dahello, come se il giocatore li avesse autorizzati.--record FILEaggiunge 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.jsonCosa vede il giocatore
- Il primo evento valido o
hellodi un nuovogame.idcrea una scheda SDK in Clip, con nome del gioco, cartella e copertina seexecorrisponde 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.execorrisponde 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.