آلية العمل
- يبلغ التعديل عن لحظة. يكفي استدعاء واحد مثل
event("bossKill"). يرسلها العميل في الخلفية ولا يعطل اللعبة. - يضيف Hovermate علامة. يصبح كل حدث علامة على الخط الزمني للتسجيل والجلسة، مع التسمية التي تختارها.
- يختار اللاعب اللقطات البارزة. تحصل لعبتك على بطاقة مستقلة في «المقاطع». ولكل نوع يفعله اللاعب يحفظ الحدث مقطعًا بارزًا أيضًا.
عندما يكون SDK مفعّلًا، يكتب Hovermate عنوانه ورمز المصادقة ومجلد الاستقبال في 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 إلى مشروع التعديل.
// 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 runC++
- العميل
- hovermate_events.h
- بيئة التشغيل
- C++17 على Windows. ملف ترويسة فقط؛ يستخدم WinHTTP ويربط winhttp.lib.
- الإعداد
- ضمّن hovermate_events.h. استدعِ hovermate::shutdown() قبل إلغاء تحميل DLL، ولا تستدعه داخل 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;
}شغّل من موجه أوامر المطورين داخل examples/cpp:
cl /std:c++17 /EHsc /utf-8 /I..\..\cpp example.cppJava
- العميل
- HovermateEvents.java
- بيئة التشغيل
- Java 8 أو أحدث: ModTheSpire وForge وFabric.
- الإعداد
- انسخ HovermateEvents.java إلى التعديل وغيّر الحزمة أو أعد تحديد موقعها أثناء shading لتجنب تعارضها مع نسخ التعديلات الأخرى.
// 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 ExamplePython
- العميل
- hovermate_events.py
- بيئة التشغيل
- Python 3.7 أو أحدث، باستخدام المكتبة القياسية فقط.
- الإعداد
- ضع hovermate_events.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.pyJavaScript
- العميل
- hovermate-events.mjs
- بيئة التشغيل
- Node.js 18 أو أحدث، كوحدة ES.
- الإعداد
- استورد 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();شغّل من examples/js:
node example.mjsLua
- العميل
- hovermate_events.lua
- بيئة التشغيل
- Lua 5.1+ وLuaJIT: Cyber Engine Tweaks وبيئات معزولة أخرى دون شبكة.
- الإعداد
- ضع hovermate_events.lua بجانب init.lua. تكتب الأحداث في hovermate-events.jsonl داخل مجلد التعديل؛ أخبر اللاعبين بإضافة هذا الملف في 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()شغّل من المجلد الذي يمثل مجلد التعديل:
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. القائمة تتوسع فقط؛ ويرفض النوع القياسي المجهول.
killdeathassistmultikillroundWonroundLostmatchWonmatchLostmvpgoalsaveobjectivemissionlapbestLappositioncrashincidentpenaltypitfinishraceWonlandingtakeofffightWonlevelUpbossKillzoneachievement
الأنواع المخصصة
للحظات غير الموجودة في القائمة القياسية. لكل نوع مفتاح مستقل باسم يحدده 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 حدث، تقبل أو ترفض معًا. يجب تسمية اللعبة الجديدة في أول حدث يذكرها داخل الدفعة |
| جسم HTTP | 64 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.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 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 نفسه.