Deadworks/Tournament

Контракт 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.statePartial<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.playersPlayer[]Нетворс, 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/commandsEnvelopeТа же команда 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. Получить код

Скачать ZIP

Или через 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. Подключение к плагину

  1. Запустите сервер Deadlock с плагином Deadworks, который поднимает WebSocket (по умолчанию ws://localhost:27080/deadworks).
  2. Откройте «Лобби», вставьте URL и токен, нажмите «Подключить».
  3. Плагин шлёт события evt.* по факту и snapshot.state раз в секунду (вкладка Hybrid Telemetry Engine), плюс события stats.* из таблицы выше.
  4. Без сервера сайт работает в режиме эмуляции.

Имена API плагина в примере C# — иллюстративные; сверяйте с актуальной версией Deadworks.