Контракт v1
API и WebSocket события
Плагин Deadworks поднимает WebSocket на игровом сервере. Все сообщения в обе стороны — JSON-конверт. Авторизация — параметр ?token=.
Конверт
{ "type": string, "matchId": string, "ts": number /* unix ms */, "payload": object }Команды: сайт → плагин
| client.hello | { client, version } | Рукопожатие после открытия сокета |
| cmd.coin.flip | {} | Подбросить монетку в лобби |
| cmd.coin.choose | { by: "winner"|"loser", value: "first"|"second"|"amber"|"sapphire" } | Выбор пика/стороны |
| cmd.match.start | {} | Начать игру (только после монетки) |
| cmd.match.pause / cmd.match.resume | {} | Пауза драфта или матча |
| cmd.draft.select | { hero } | Пик или бан текущей команды |
| cmd.chat.send | { id, author, text, t } | Сообщение с сайта в игровой чат |
События: плагин → сайт
| match.state | Partial<MatchState> | Полный/частичный снапшот (при подключении) |
| lobby.updated | { A: string[], B: string[] } | Состав лобби |
| coin.updated | { flipping, winner, firstPick, sideA } | Состояние монетки |
| draft.updated | { step, picks, bans, turnLeft, reserve } | Драфт и таймеры (30 с + резерв 120 с) |
| match.phase | { phase: "lobby"|"draft"|"live"|"ended", gameTime } | Смена фазы |
| match.paused / match.resumed | { by } | Пауза снята/поставлена |
| chat.message | { id, from: "game"|"web", author, text, t } | Сообщение из игры |
| evt.kill / evt.cast / evt.purchase / evt.parry / evt.clutch_save / evt.urn_pickup | { hero, ... } | Event Stream: мгновенные дискретные события (push) |
| snapshot.state | { tick, players: [{ hero, hp, speed, pos:[x,y,z], nw, cc, soulsLost }] } | Snapshot Stream: полный срез 1 Hz |
| stats.players | Player[] | Нетворс, secures/denies, предметы по слотам, кулдауны, парирования, скорость, air time, dismounts |
| stats.fight_recap | { at, focus, winner, shields: {A,B}, damage: [{hero,team,dmg}] } | Плашка тимфайта |
| stats.advanced | [{ hero, deaths, soulsLost, shots, headshots, crits, ccTime, soloKills, clutch: { used, saved, last } }] | Глубокая аналитика: Death Tax, точность HS/крит, CC-время (с), соло-фраги без ассистов, Clutch Actives (Metal Skin / Rescue Beam / Ethereal Shift) |
| stats.clutch | [{ t, text }] | Лента clutch-парирований |
| stats.loot | { A|B: { boxes, runes, buffs, souls } } | Лут-гоблин |
| stats.urn | { carrier, carrierTeam, progress, deliveries } | Soul Urn Run |
| stats.rejuvenator | { taken, active, left } | Rejuvenator Impact |
Пример: stats.advanced
{
"type": "stats.advanced", "matchId": "DW-7KQ2A", "ts": 1793350000000,
"payload": [{
"hero": "Haze", "deaths": 3, "soulsLost": 2140,
"shots": 1820, "headshots": 412, "crits": 96,
"ccTime": 18.5, "soloKills": 2,
"clutch": { "used": 4, "saved": 3, "last": "Metal Skin" }
}]
}Точность HS = headshots / shots, крит = crits / shots. soloKills — убийства без ассистов союзников. clutch.saved — сейв-предмет прожат при HP ниже 20% и игрок выжил.
REST (опционально)
| GET /api/matches/:id | — | Снапшот MatchState |
| POST /api/matches/:id/commands | Envelope | Та же команда cmd.*, если WS недоступен |
| GET /api/heroes | — | Пул героев для драфта |
C# плагин
// Deadworks plugin (C#) — WebSocket bridge
public class TournamentPlugin : DeadworksPlugin
{
private readonly WsHub _hub = new("http://0.0.0.0:27080/deadworks/");
public override void OnLoad()
{
_hub.OnCommand("cmd.match.pause", _ => { Server.Pause(); Broadcast("match.paused", new { by = "web" }); });
_hub.OnCommand("cmd.draft.select", p => Draft.Select(p.GetString("hero")));
_hub.OnCommand("cmd.chat.send", p => Server.PrintToChatAll($"[WEB] {p.GetString("text")}"));
Events.OnChatMessage += (player, text) =>
Broadcast("chat.message", new { id = Guid.NewGuid(), from = "game", author = player.Name, text, t = Now() });
Events.OnParry += (attacker, victim) => Stats.RecordParry(attacker, victim);
Timers.Every(1.0f, () => Broadcast("stats.players", Stats.SnapshotPlayers()));
Timers.Every(2.0f, () => Broadcast("stats.advanced", Stats.SnapshotAdvanced()));
}
void Broadcast(string type, object payload) =>
_hub.SendAll(new { type, matchId = Match.Id, ts = Now(), payload });
}Веб-клиент
const ws = new WebSocket("ws://server:27080/deadworks?token=SECRET");
ws.onmessage = (e) => {
const { type, payload } = JSON.parse(e.data);
if (type.startsWith("stats.")) render(type, payload);
};
ws.send(JSON.stringify({ type: "cmd.coin.flip", matchId: "DW-7KQ2A", ts: Date.now(), payload: {} }));Локальный запуск и интеграция
1. Получить код
Или через GitHub: в редакторе Lovable — кнопка GitHub → Connect → Create repository, затем:
git clone https://github.com/<you>/deadworks-tournament-kit.git
2. Запуск
Нужен Node.js 20+ (или Bun).
cd deadworks-tournament-kit npm install npm run dev # http://localhost:8080 npm run build # продакшн-сборка
3. Подключение к плагину
- Запустите сервер Deadlock с плагином Deadworks, который поднимает WebSocket (по умолчанию
ws://localhost:27080/deadworks). - Откройте «Лобби», вставьте URL и токен, нажмите «Подключить».
- Плагин шлёт события
evt.*по факту иsnapshot.stateраз в секунду (вкладка Hybrid Telemetry Engine), плюс событияstats.*из таблицы выше. - Без сервера сайт работает в режиме эмуляции.
Имена API плагина в примере C# — иллюстративные; сверяйте с актуальной версией Deadworks.