SDK de eventos de juego · Protocolo v1

Envía eventos de juego a Hovermate

Cualquier juego o mod puede notificar bajas, jefes derrotados, vueltas, goles o momentos propios. Hovermate crea marcadores de tiempo y clips automáticos, igual que con sus fichas de juegos integradas.

Clientes para C#, C++, Java, Python, JavaScript y Lua · Un archivo cada uno, sin dependencias · Licencia MIT

Cómo funciona

  1. Tu mod notifica un momento. Basta una llamada, como event("bossKill"). El cliente lo envía en segundo plano sin bloquear el juego.
  2. Hovermate lo marca. Cada evento se convierte en un marcador de la grabación y la sesión con tu etiqueta.
  3. El jugador elige los momentos destacados. Tu juego obtiene su propia ficha en Clips. Para cada tipo activado por el jugador también se guarda un clip.

Mientras el SDK está activo, Hovermate publica su dirección, un token y su carpeta de entrada en 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"
}

Lee url de este archivo y úsalo exactamente como está escrito; nunca fijes en el código ni cambies su host, puerto o ruta.

Los clientes leen %LOCALAPPDATA%\Hovermate\Sdk\connection.json, o la ruta de HOVERMATE_SDK_CONNECTION para pruebas y configuraciones aisladas. Vuelven a leerlo tras una respuesta 401, porque el token cambia al desactivar y activar el SDK. Si el archivo no existe, Hovermate no está instalado o el SDK está desactivado; los clientes permanecen en silencio.

Tres formas de enviar eventos

Usa la primera que permita el entorno de tu mod.

HTTP

Tu mod puede abrir una conexión de red local.

Envía el evento por POST a la URL con Authorization: Bearer <token>. Las solicitudes con cabecera Origin se rechazan, por lo que las páginas web no pueden enviar eventos.

C#, C++, Java, Python, JavaScript; GDScript puede usar HTTPRequest.

Archivos

Tu mod puede escribir archivos, pero no acceder a la red.

Añade un evento JSON por línea a {inbox}\<game.id>.jsonl; no hace falta token. Un entorno limitado a la carpeta del mod, como Cyber Engine Tweaks, escribe allí en hovermate-events.jsonl; el jugador añade ese archivo a la ficha del juego.

Todos los clientes recurren a la carpeta de entrada; el cliente Lua escribe en la carpeta del mod.

Líneas de registro

Tus scripts solo pueden escribir en el registro del propio juego.

Escribe [HOVERMATE] seguido de un objeto de evento JSON; se ignora todo lo anterior al marcador. scriptslog.txt de The Witcher 3 y Papyrus.0.log de Skyrim y Fallout 4 se leen automáticamente mientras el juego está abierto.

Scripts de Witcher 3, Papyrus o cualquier juego con registro de scripts.

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

Respuesta: 202 {"accepted":1}.

Una línea de registro

Una línea en el registro del juego
[12:00:01] [Script] [HOVERMATE] {"v":1,"game":{"id":"com.example.mygame","name":"My Game"},"type":"bossKill","context":"Act 2"}

Para otro registro, el jugador lo añade en Clips › ficha del juego › Leer eventos de un archivo; también puede proponerlo tu hello.

Inicio rápido

Cada ejemplo envía hello, kill, bossKill y un evento personalizado parry. Son los archivos ejecutables de la carpeta examples del SDK.

C#

Entorno
netstandard2.0, .NET Framework 4.7.2+ y .NET, con C# 7.3: Unity, BepInEx, MelonLoader.
Configuración
Añade HovermateEvents.cs al proyecto de tu 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();

Ejecuta en examples/csharp (el ejemplo requiere .NET 8 o posterior):

dotnet run

C++

Entorno
C++17 en Windows. Solo cabecera; usa WinHTTP y enlaza winhttp.lib.
Configuración
Incluye hovermate_events.h. Llama a hovermate::shutdown() antes de descargar tu DLL, nunca dentro de 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;
}

Ejecuta en un símbolo del sistema para desarrolladores, desde examples/cpp:

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

Java

Entorno
Java 8 o posterior: ModTheSpire, Forge, Fabric.
Configuración
Copia HovermateEvents.java a tu mod y cambia su paquete, o reubícalo durante el shading, para evitar conflictos con copias de otros 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);
    }
}

Ejecuta desde examples/java:

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

Python

Entorno
Python 3.7 o posterior, solo biblioteca estándar.
Configuración
Coloca hovermate_events.py junto a tu 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()

Ejecuta desde examples/python:

python example.py

JavaScript

Entorno
Node.js 18 o posterior, como módulo ES.
Configuración
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();

Ejecuta desde examples/js:

node example.mjs

Lua

Entorno
Lua 5.1+ y LuaJIT: Cyber Engine Tweaks y otros entornos sin red.
Configuración
Coloca hovermate_events.lua junto a init.lua. Los eventos se guardan en hovermate-events.jsonl dentro de la carpeta del mod: indica a los jugadores que añadan ese archivo a 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()

Ejecuta en la carpeta que representa la de tu mod:

lua example.lua

Comportamiento de los clientes

LlamadaFunción
init(gameId, gameName, exe?)Inicia el cliente. gameId coincide con [a-z0-9._-]{3,64} y permanece estable entre versiones del mod; exe es el nombre del ejecutable del juego.
hello(offers, log?, files?)Anuncia los tipos de eventos disponibles y, opcionalmente, un registro o archivos del mod que leer.
event(type, {label, tags, context})Envía un tipo de evento estándar.
custom(customType, customLabel, {…})Envía un tipo propio; customLabel da nombre a su interruptor de momentos destacados.
flush() / shutdown()Espera los eventos en cola y detiene el proceso en segundo plano al descargar el mod.
  • Las llamadas regresan al instante y nunca propagan excepciones al juego.
  • Un proceso en segundo plano envía eventos con un tiempo de espera de 250 ms y reintenta una vez las solicitudes fallidas.
  • La cola admite 256 eventos y descarta el más antiguo.
  • Si HTTP no está disponible, los eventos se envían a la carpeta de entrada durante unos segundos.
  • Sin connection.json, el cliente permanece en silencio y comprueba de nuevo cada 5 segundos.
  • Cada evento incluye game.name. El texto se recorta a los límites indicados abajo y las etiquetas no válidas se descartan sin provocar un fallo.

El cliente Lua (hovermate_events.lua) escribe cada evento al instante. Prefiere hovermate-events.jsonl en la carpeta del mod, después la carpeta de entrada si se permiten rutas absolutas y, por último, líneas [HOVERMATE] mediante una función writer que proporcionas, como print. logLine(type, options) devuelve una de esas líneas para que la imprimas.

El objeto de 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" }
CampoObligatorioRegla
vsí1
game.idsí[a-z0-9._-]{3,64}: nombre DNS inverso o identificador corto, estable entre versiones del mod
game.nameprimer eventoEn el primer evento o hello de la sesión; máximo 64 caracteres
game.exenoSolo un nombre de archivo. Vincula los eventos a la carpeta del juego detectado y permite a ese ejecutable iniciar el búfer de repetición
typesíUn tipo estándar de abajo o custom
customType, customLabelpara custom[a-z0-9_.-]{1,32} y un nombre de interruptor de hasta 40 caracteres
labelnoTexto de este evento para la ayuda del marcador y el título del clip; máximo 80 caracteres
tagsnoHasta 8 etiquetas, cada una conforme a [a-z0-9_-]{1,24}
contextnoMapa, nivel o zona para títulos de momentos destacados; máximo 64 caracteres
timenoISO-8601 UTC. Hovermate registra la llegada; un time a menos de 5 s precisa esa marca

Las longitudes cuentan unidades UTF-16: un emoji ocupa 2. Los campos de texto rechazan caracteres de control. Los campos desconocidos se ignoran y las nuevas versiones solo añaden campos. No envíes datos personales: las etiquetas y el contexto pueden aparecer en títulos de clips y nombres de archivos.

Tipos estándar

29 tipos compartidos con las fichas integradas de Hovermate. La lista solo crece; los tipos estándar desconocidos se rechazan.

  • 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

Tipos personalizados

Para momentos fuera de la lista estándar. Cada tipo tiene su propio interruptor, cuyo nombre indica customLabel; hasta 200 por juego.

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

hello

Opcional y recomendado. Crea la ficha del juego y sus interruptores antes del primer evento real para que el jugador pueda configurarla de antemano.

{ "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 enumera tipos estándar y objetos { "customType", "customLabel" }.
  • log propone un registro con líneas [HOVERMATE]; files propone archivos de eventos del mod. Hovermate pide permiso al jugador antes de leer cualquier ruta propuesta.
  • hello funciona con cualquier transporte, incluida una línea de registro [HOVERMATE].

Límites y respuestas

LímiteValor
Eventos por juego20 por segundo. Superarlo devuelve 429 y no se acepta nada de la solicitud; hello no cuenta
Lote{ "events": [ … ] } con 1–100 eventos, todo o nada. Un juego nuevo debe tener nombre en el primer evento del lote que lo mencione
Cuerpo HTTP64 KB
Líneas de archivos de entrada4 KB; las líneas más largas se descartan
Tipos personalizados200 por juego
Tiempos de espera del clienteHasta 250 ms, como máximo un reintento y una cola limitada que descarta el evento más antiguo
EstadoSignificado
202Aceptado: {"accepted": n}
400No válido: {"error", "detail", "index"}. index indica el primer evento incorrecto del lote
401Token ausente o incorrecto: vuelve a leer connection.json
403La solicitud contiene una cabecera Origin
413Cuerpo superior a 64 KB
429Más de 20 eventos por segundo para un juego
405, 411, 415Método distinto de POST, falta Content-Length o el tipo de contenido no es JSON

Códigos de error: invalid_json, version, game, type, custom, label, tags, context, offers, log, files, batch. Las líneas incorrectas de los archivos se cuentan y aparecen como eventos no válidos en la ficha del juego; Hovermate nunca registra su contenido original.

Prueba sin Hovermate

El receptor de prueba mock-server.py (Python 3.8+, biblioteca estándar) responde exactamente como Hovermate. Crea su propio connection.json en una carpeta temporal, aplica las mismas reglas y códigos de error, sigue la carpeta de entrada y los archivos de --watch, e imprime cada evento y error.

A diferencia de Hovermate, el receptor de prueba escucha en un puerto numérico de loopback y escribe esa dirección en su connection.json. Es solo una herramienta de prueba: los clientes usan la url que contenga el archivo.

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
Salida del receptor de prueba
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 sigue un archivo del mod hovermate-events.jsonl o un registro del juego con líneas [HOVERMATE].
  • --follow-proposals lee las rutas propuestas por hello, como si el jugador las hubiera autorizado.
  • --record FILE añade cada evento aceptado o rechazado como línea JSON para comprobaciones automáticas.

HOVERMATE_SDK_CONNECTION funciona igual con Hovermate, por lo que las pruebas nunca tocan el archivo de conexión real.

Casos de conformidad

El SDK incluye conformance/cases.json: solicitudes con las respuestas esperadas de Hovermate, desde eventos válidos hasta cada código de error. conformance/run.py las envía al receptor de prueba o a cualquier otro. Hovermate usa ese mismo archivo en sus propias pruebas.

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

Qué ve el jugador

  • El primer evento válido o hello de un nuevo game.id crea una ficha SDK en Clips con el nombre, la carpeta y la portada si exe coincide con un juego instalado, y un interruptor por cada tipo anunciado o recibido.
  • Los interruptores de momentos destacados empiezan desactivados: el jugador elige qué tipos guardan clips. Los marcadores siempre se escriben.
  • Los eventos siguen el mismo recorrido que las fichas integradas: diario, marcadores de clips y sesiones, ventana de momentos destacados y notificaciones.
  • Si game.exe coincide con el juego en primer plano, los eventos se atribuyen a ese juego. Si no, solo se aceptan durante una grabación o con el búfer de repetición activo.
  • Los eventos no válidos se cuentan en el estado de la ficha para que los jugadores puedan informar de problemas.

Protocolo y archivos

El protocolo v1 es el contrato completo: detección de conexión, cuatro formas de entrega, objeto de evento, hello y comportamiento de Hovermate.

El SDK —clientes, ejemplos, herramientas, casos de conformidad y protocolo— tiene licencia MIT, así que puedes incluir un cliente en tu mod. Hovermate en sí no está cubierto por esa licencia.