SDK d’événements de jeu · Protocole v1

Envoyez des événements de jeu à Hovermate

Un jeu ou un mod peut signaler des éliminations, des boss vaincus, des tours, des buts ou ses propres moments. Hovermate crée des marqueurs chronologiques et des clips de moments forts automatiques, comme pour ses fiches de jeux intégrées.

Clients C#, C++, Java, Python, JavaScript et Lua · Un fichier chacun, sans dépendances · Licence MIT

Fonctionnement

  1. Votre mod signale un moment. Un seul appel suffit, par exemple event("bossKill"). Le client l’envoie en arrière-plan sans bloquer le jeu.
  2. Hovermate le marque. Chaque événement devient un marqueur de l’enregistrement et de la session, avec votre libellé.
  3. Le joueur choisit les moments forts. Votre jeu reçoit sa propre fiche dans Clips. Chaque type activé par le joueur enregistre aussi un clip.

Quand son SDK est activé, Hovermate publie son adresse, un jeton et son dossier de réception dans 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"
}

Lisez url dans ce fichier et utilisez-le exactement tel qu’il est écrit ; ne codez jamais en dur et ne modifiez jamais son hôte, son port ou son chemin.

Les clients lisent %LOCALAPPDATA%\Hovermate\Sdk\connection.json, ou le chemin de HOVERMATE_SDK_CONNECTION pour les tests et configurations isolées. Ils le relisent après une réponse 401, car le jeton change à chaque désactivation puis réactivation du SDK. L’absence du fichier signifie que Hovermate n’est pas installé ou que le SDK est désactivé ; les clients restent silencieux.

Trois façons d’envoyer des événements

Utilisez la première autorisée par l’environnement de votre mod.

HTTP

Votre mod peut ouvrir une connexion réseau locale.

Envoyez l’événement par POST à l’URL avec Authorization: Bearer <token>. Les requêtes contenant un en-tête Origin sont refusées : les pages web ne peuvent donc pas envoyer d’événements.

C#, C++, Java, Python, JavaScript ; GDScript peut utiliser HTTPRequest.

Fichiers

Votre mod peut écrire des fichiers, mais n’a pas accès au réseau.

Ajoutez un événement JSON par ligne dans {inbox}\<game.id>.jsonl ; aucun jeton n’est nécessaire. Une sandbox limitée au dossier du mod, comme Cyber Engine Tweaks, écrit plutôt dans hovermate-events.jsonl à cet endroit ; le joueur ajoute ce fichier à la fiche du jeu.

Tous les clients se replient sur le dossier de réception ; le client Lua écrit le fichier du dossier du mod.

Lignes de journal

Vos scripts peuvent seulement écrire dans le journal du jeu.

Écrivez [HOVERMATE] , puis un objet événement JSON ; tout ce qui précède le marqueur est ignoré. scriptslog.txt de The Witcher 3 et Papyrus.0.log de Skyrim et Fallout 4 sont lus automatiquement pendant que le jeu tourne.

Scripts Witcher 3, Papyrus ou tout jeu doté d’un journal de scripts.

Une requête 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"}

Réponse : 202 {"accepted":1}.

Une ligne de journal

Une ligne du journal du jeu
[12:00:01] [Script] [HOVERMATE] {"v":1,"game":{"id":"com.example.mygame","name":"My Game"},"type":"bossKill","context":"Act 2"}

Pour un autre journal, le joueur l’ajoute dans Clips › fiche du jeu › Lire les événements depuis un fichier, ou votre hello le propose.

Démarrage rapide

Chaque exemple envoie hello, kill, bossKill et un événement personnalisé parry. Ce sont les fichiers exécutables du dossier examples du SDK.

C#

Environnement
netstandard2.0, .NET Framework 4.7.2+ et .NET, avec C# 7.3 : Unity, BepInEx, MelonLoader.
Configuration
Ajoutez HovermateEvents.cs au projet de votre 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();

Lancez dans examples/csharp (l’exemple cible .NET 8 ou ultérieur) :

dotnet run

C++

Environnement
C++17 sous Windows. Fichier d’en-tête uniquement ; utilise WinHTTP et lie winhttp.lib.
Configuration
Incluez hovermate_events.h. Appelez hovermate::shutdown() avant le déchargement de votre DLL, jamais depuis 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;
}

Lancez dans une invite de commandes développeur, depuis examples/cpp :

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

Java

Environnement
Java 8 ou ultérieur : ModTheSpire, Forge, Fabric.
Configuration
Copiez HovermateEvents.java dans votre mod et changez son package, ou relocalisez-le lors du shading, pour éviter les conflits avec les copies d’autres mods.
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);
    }
}

Lancez depuis examples/java :

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

Python

Environnement
Python 3.7 ou ultérieur, bibliothèque standard uniquement.
Configuration
Placez hovermate_events.py à côté de votre 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()

Lancez depuis examples/python :

python example.py

JavaScript

Environnement
Node.js 18 ou ultérieur, en module ES.
Configuration
Importez 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();

Lancez depuis examples/js :

node example.mjs

Lua

Environnement
Lua 5.1+ et LuaJIT : Cyber Engine Tweaks et autres sandboxes sans réseau.
Configuration
Placez hovermate_events.lua à côté de init.lua. Les événements sont écrits dans hovermate-events.jsonl dans le dossier du mod : demandez aux joueurs d’ajouter ce fichier dans 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()

Lancez dans le dossier représentant celui de votre mod :

lua example.lua

Comportement des clients

AppelFonction
init(gameId, gameName, exe?)Démarre le client. gameId correspond à [a-z0-9._-]{3,64} et reste identique entre les versions du mod ; exe est le nom de l’exécutable du jeu.
hello(offers, log?, files?)Annonce les types que votre jeu peut envoyer et, éventuellement, un journal ou des fichiers du mod à lire.
event(type, {label, tags, context})Envoie un type d’événement standard.
custom(customType, customLabel, {…})Envoie votre propre type ; customLabel nomme son option de moments forts.
flush() / shutdown()Attend les événements en file et arrête le traitement en arrière-plan au déchargement du mod.
  • Les appels reviennent immédiatement et ne transmettent jamais d’exception au jeu.
  • Un traitement en arrière-plan envoie les événements avec un délai maximal de 250 ms et réessaie une fois les requêtes en échec.
  • La file contient 256 événements et supprime le plus ancien en cas de dépassement.
  • Si HTTP est inaccessible, les événements sont envoyés au dossier de réception pendant quelques secondes.
  • Sans connection.json, le client reste silencieux et vérifie à nouveau toutes les 5 secondes.
  • Chaque événement contient game.name. Le texte est tronqué aux limites ci-dessous et les tags invalides sont ignorés au lieu de provoquer un échec.

Le client Lua (hovermate_events.lua) écrit immédiatement chaque événement. Il privilégie hovermate-events.jsonl dans le dossier du mod, puis le dossier de réception si les chemins absolus sont autorisés, puis des lignes [HOVERMATE] via la fonction writer fournie, par exemple print. logLine(type, options) renvoie une telle ligne pour que vous l’affichiez.

L’objet événement

{ "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" }
ChampObligatoireRègle
voui1
game.idoui[a-z0-9._-]{3,64} : nom DNS inversé ou identifiant court, stable entre les versions du mod
game.namepremier événementDans le premier événement ou hello d’une session ; 64 caractères maximum
game.exenonUn nom de fichier uniquement. Associe les événements au dossier du jeu détecté et permet à cet exécutable de démarrer le tampon de replay
typeouiUn type standard ci-dessous ou custom
customType, customLabelpour custom[a-z0-9_.-]{1,32}, et un nom d’option de 40 caractères maximum
labelnonTexte de cet événement pour l’infobulle du marqueur et le titre du clip ; 80 caractères maximum
tagsnonJusqu’à 8 tags, chacun correspondant à [a-z0-9_-]{1,24}
contextnonCarte, niveau ou zone pour les titres des moments forts ; 64 caractères maximum
timenonISO-8601 UTC. Hovermate date la réception ; une valeur time à moins de 5 s affine cette date

Les longueurs comptent les unités UTF-16 : un emoji compte pour 2. Les champs texte refusent les caractères de contrôle. Les champs inconnus sont ignorés ; les versions suivantes ajoutent seulement des champs. N’envoyez pas de données personnelles : libellés et contextes peuvent apparaître dans les titres de clips et noms de fichiers.

Types standard

29 types partagés avec les fiches de jeux intégrées de Hovermate. La liste s’allonge seulement ; un type standard inconnu est refusé.

  • 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

Types personnalisés

Pour les moments absents de la liste standard. Chaque type a sa propre option de moments forts, nommée par customLabel ; un jeu peut en avoir jusqu’à 200.

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

hello

Facultatif et recommandé. Crée la fiche de votre jeu et ses options avant le premier événement réel, afin que le joueur puisse la configurer à l’avance.

{ "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 énumère les types standard et les objets { "customType", "customLabel" }.
  • log propose un journal avec des lignes [HOVERMATE] ; files propose des fichiers d’événements du mod. Hovermate demande l’accord du joueur avant de lire un chemin proposé.
  • hello fonctionne avec tous les transports, y compris une ligne de journal [HOVERMATE].

Limites et réponses

LimiteValeur
Événements par jeu20 par seconde. Au-delà, réponse 429 et aucun événement de la requête n’est accepté ; hello n’est pas compté
Lot{ "events": [ … ] } de 1 à 100 événements, entièrement accepté ou refusé. Le premier événement mentionnant un nouveau jeu doit le nommer
Corps HTTP64 Ko
Lignes des fichiers d’entrée4 Ko ; les lignes plus longues sont ignorées
Types personnalisés200 par jeu
Délais des clients250 ms maximum, une seule nouvelle tentative, une file bornée supprimant l’événement le plus ancien
StatutSignification
202Accepté : {"accepted": n}
400Invalide : {"error", "detail", "index"}. index indique le premier événement invalide du lot
401Jeton manquant ou incorrect : relisez connection.json
403La requête contient un en-tête Origin
413Corps dépassant 64 Ko
429Plus de 20 événements par seconde pour un jeu
405, 411, 415Méthode autre que POST, absence de Content-Length ou contenu non JSON

Codes d’erreur : invalid_json, version, game, type, custom, label, tags, context, offers, log, files, batch. Les lignes invalides des fichiers sont comptées et signalées dans la fiche du jeu ; Hovermate ne journalise jamais leur contenu brut.

Tester sans Hovermate

Le récepteur de test mock-server.py (Python 3.8+, bibliothèque standard) répond exactement comme Hovermate. Il crée son propre connection.json dans un dossier temporaire, applique les mêmes règles et codes d’erreur, suit le dossier d’entrée et les fichiers de --watch, puis affiche chaque événement et chaque erreur.

Contrairement à Hovermate, le récepteur de test écoute sur un port numérique de loopback et écrit cette adresse dans son connection.json. Ce n’est qu’un outil de test : les clients utilisent l’url que contient le fichier.

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
Sortie du récepteur de test
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 suit un fichier de mod hovermate-events.jsonl ou un journal contenant des lignes [HOVERMATE].
  • --follow-proposals lit les chemins proposés par hello, comme si le joueur les avait autorisés.
  • --record FILE ajoute chaque événement accepté ou refusé sous forme de ligne JSON pour les vérifications automatiques.

HOVERMATE_SDK_CONNECTION fonctionne aussi avec Hovermate lui-même : les tests ne touchent jamais le vrai fichier de connexion.

Cas de conformité

Le SDK inclut conformance/cases.json : des requêtes et les réponses attendues de Hovermate, des événements valides à chaque code d’erreur. conformance/run.py les envoie au récepteur de test ou à tout autre récepteur. Hovermate utilise le même fichier dans ses propres tests.

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

Ce que voit le joueur

  • Le premier événement valide ou hello d’un nouveau game.id crée une fiche SDK dans Clips : nom du jeu, dossier et jaquette si exe correspond à un jeu installé, et une option pour chaque type annoncé ou reçu.
  • Les options de moments forts sont initialement désactivées : le joueur choisit les types qui enregistrent des clips. Les marqueurs sont toujours écrits.
  • Les événements suivent le même circuit que les fiches intégrées : journal, marqueurs des clips et sessions, fenêtre de moments forts et notifications.
  • Quand game.exe correspond au jeu au premier plan, les événements lui sont attribués. Sinon, ils sont acceptés uniquement pendant un enregistrement ou l’exécution du tampon de replay.
  • Les événements invalides sont comptés dans l’état de la fiche pour que les joueurs puissent vous signaler un problème.

Protocole et fichiers

Le protocole v1 est le contrat complet : découverte de la connexion, quatre moyens de livraison, objet événement, hello et comportement de Hovermate.

Le SDK — clients, exemples, outils, cas de conformité et protocole — est sous licence MIT : vous pouvez intégrer un client à votre mod. Cette licence ne couvre pas Hovermate lui-même.