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 إلى العنوان مع Authorization: Bearer <token>. ترفض الطلبات التي تحتوي على ترويسة Origin، لذلك لا تستطيع صفحات الويب إرسال الأحداث.

C# وC++ وJava وPython وJavaScript؛ ويمكن لـGDScript استخدام HTTPRequest.

الملفات

يستطيع التعديل كتابة الملفات، لكنه لا يستطيع استخدام الشبكة.

أضف حدث JSON واحدًا لكل سطر إلى {inbox}\<game.id>.jsonl؛ لا يلزم رمز مصادقة. إذا كانت البيئة لا تسمح بالكتابة إلا داخل مجلد التعديل، مثل Cyber Engine Tweaks، فاكتب إلى hovermate-events.jsonl فيه، ثم يضيف اللاعب الملف إلى بطاقة اللعبة.

يعود كل العملاء إلى مجلد الاستقبال عند الحاجة؛ ويكتب عميل 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 ms ويعيد محاولة الطلب الفاشل مرة واحدة.
  • يسع الطابور 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 حدث، تقبل أو ترفض معًا. يجب تسمية اللعبة الجديدة في أول حدث يذكرها داخل الدفعة
جسم HTTP64 KB
أسطر ملفات الاستقبال4 KB؛ تتجاهل الأسطر الأطول
الأنواع المخصصة200 لكل لعبة
مهلة العميل250 ms أو أقل، ومحاولة إضافية واحدة كحد أقصى، وطابور محدود يحذف الحدث الأقدم
الحالةالمعنى
202مقبول: {"accepted": n}
400غير صالح: {"error", "detail", "index"}. يحدد index أول حدث غير صالح في الدفعة
401الرمز مفقود أو خاطئ: أعد قراءة connection.json
403يحتوي الطلب على ترويسة Origin
413جسم الطلب أكبر من 64 KB
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، يستمع مستقبل الاختبار على منفذ رقمي لعنوان الاسترجاع ويكتب هذا العنوان في 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 نفسه.