- Что такое реплей Dota 2 и какие данные в нем лежат
- Как скачать реплей Dota 2
- Как использовать парсер Clarity для обработки реплеев
- Python-обработка событий: от потока строк к таблицам
- Как выделить события урона между героями
- Визуализация таймлайна урона
- Как применить DBSCAN для кластеризации урона
- Как определить начало и конец временных промежутков для хайлайтов
- Идеи для усовершенствования поиска хайлайтов
- Как реализовать автоматическую запись видео моментов
- Короткое резюме процесса
Реплей Dota 2 — это как “черный ящик” матча: в нем записаны события игры по времени. В этом посте разберем, как скачать реплей, прогнать его парсером Clarity, найти урон между героями, визуализировать таймлайн, а затем автоматически собрать хайлайты и подготовить запись видео.
Что такое реплей Dota 2 и какие данные в нем лежат
Файл записи матча в Dota 2 обычно имеет вид:
<match_id>.dem
Внутри лежит набор событий, которые относятся к разным действиям в игре: урон, лечение, покупки, сообщения в чат, переходы состояний и так далее. Именно поэтому задача “проанализировать матч” превращается в задачу “прочитать список событий и сгруппировать их по смыслу”.
Типы событий в реплеях
После парсинга реплей превращается в последовательность JSON-событий. У каждого события есть как минимум:
type— тип событияtime— время в секундах с начала матча
Важно: time может быть отрицательным — так игра может отмечать события, которые происходят “до” основной игровой части (например, вокруг появления крипов).
Ниже типичные типы, которые встречаются при анализе:
| Тип события | Зачем смотреть |
|---|---|
DOTA_COMBATLOG_DAMAGE |
урон, самое важное для хайлайтов |
DOTA_COMBATLOG_HEAL / хил (встречается как соответствующий тип) |
восстановление здоровья |
DOTA_COMBATLOG_MODIFIER_ADD / ...REMOVE |
эффекты и баффы |
DOTA_COMBATLOG_GOLD |
получение золота |
DOTA_COMBATLOG_DEATH |
убийства |
DOTA_COMBATLOG_ITEM |
поднятие предметов |
DOTA_COMBATLOG_PURCHASE |
покупки |
CHAT_MESSAGE_ITEM_PURCHASE, chatwheel, pings, chat |
контекст из коммуникации |
draft_start, draft_timings |
драфт |
interval, actions |
технические/служебные интервалы |
Как скачать реплей Dota 2
Поисковый запрос “сайт чтобы скачать реплей дота 2” чаще всего ведет к двум практичным вариантам.
1) Через клиент Dota 2
В Steam/Dota 2 можно скачать матч в интерфейсе Watch. Вы выбираете MatchID, после чего игра сохраняет файл *.dem на ваш диск.
Обычно путь похож на:
...\Steam\steamapps\common\dota 2 beta\game\dota\replays\
2) Через внешний источник с матчами
Вариант “со стороны сайта” обычно работает так: сервер отдает архив/сжатие .dem, а вам нужно получить обычный *.dem.
На практике это выглядит как:
- Скачивание файла формата
*.dem.bz2 - Распаковка в
.dem
Например логика распаковки:
bzcat <file>.dem.bz2 > <file>.dem
Как использовать парсер Clarity для обработки реплеев
Чтобы работать с *.dem, нужен разбор. Для этого используют парсер Clarity: он читает события и выдает удобный поток строковых JSON-объектов.
Один из рабочих подходов — поднять парсер в Docker (чтобы не мучиться с зависимостями).
Схема пайплайна
flowchart TD
A[Скачать replay .dem] --> B[Парсер Clarity]
B --> C[Получить .jsonlines с событиями]
C --> D[Python: фильтры и группировки]
D --> E[Урон между героями]
E --> F[Визуализация таймлайна]
F --> G[Кластеризация DBSCAN]
G --> H[Определение start/end хайлайта]
H --> I[Подготовка записи видео]
Типичный сценарий в Docker
Идея такая:
- Скачиваете/собираете Clarity-парсер
- Запускаете локальный веб-сервер парсера
- Отправляете
.demвнутрь и получаете.jsonlines
Результат удобно хранить рядом с проектом, например как:
6227492909.jsonlines
Python-обработка событий: от потока строк к таблицам
После парсинга матч превращается в примерно сотни тысяч событий. Это нормально.
Пример загрузки .jsonlines
Логика простая: читаем файл построчно и делаем json.loads.
import os, json
REPLAYS_DIR = "../replays/"
dem_path = os.path.join(REPLAYS_DIR, "6227492909.jsonlines")
with open(dem_path, "r") as fin:
jsonlines = [json.loads(event) for event in fin.readlines()]
print(len(jsonlines))
Быстрая проверка структуры
Первые элементы подсказывают, какие поля есть:
timetypevalue(иногда)- дополнительные поля для конкретных типов событий (например, имена атакующего и цели)
Как выделить события урона между героями
Главная цель — найти DOTA_COMBATLOG_DAMAGE и отфильтровать только случаи, где:
- атакующий — герой
- цель — герой
То есть условия примерно такие:
e['type'] == 'DOTA_COMBATLOG_DAMAGE'e['attackerhero']иe['targethero']равныTrue
Формирование таблицы урона
Дальше удобно собрать в pandas DataFrame:
import pandas as pd
df_damage = pd.DataFrame([
e for e in jsonlines
if e.get('type') == 'DOTA_COMBATLOG_DAMAGE'
and e.get('attackerhero')
and e.get('targethero')
])
Важная тонкость имен героев
В реплеях герой часто называется не “Magnus”, а тех.идентификатором вроде:
npc_dota_hero_magnataur
То есть вам придется работать с этими строками или сделать маппинг (таблицу соответствий) отдельно.
Визуализация таймлайна урона
Теперь мы строим простую картинку: “когда” герой наносит урон и “сколько раз/сколько урона”.
Таймлайн урона в точках
Например, берем только урон конкретного героя и строим точки по времени.
import matplotlib.pyplot as plt
mask = df_damage["attackername"] == "npc_dota_hero_magnataur"
df_player_damage = df_damage[mask].copy()
df_player_damage["ones"] = 1
fig, ax = plt.subplots(figsize=(19, 5))
plt.scatter(
x=df_player_damage["time"] / 60,
y=df_player_damage["ones"]
)
plt.plot()
plt.show()
- по оси X: минуты матча
- по оси Y: факт события (одна точка = одно событие урона)
Деление на “раннюю” и “основную” стадии
Чтобы не смешивать “лайнинг” и массовые драки, можно отрезать начало. Практическая логика такая:
- примерно до ~10 минут часто меньше ярких массовых моментов
- основная часть матча дает более плотные кластеры урона
Тогда:
df_player_late_damage = df_player_damage[df_player_damage["time"] > 10 * 60].copy()
И можно сделать “важнее те события, где урон больше”:
fig, ax = plt.subplots(figsize=(19, 5))
plt.scatter(
x=df_player_late_damage["time"] / 60,
y=df_player_late_damage["ones"],
s=df_player_late_damage["value"]
)
plt.plot()
plt.show()
Как применить DBSCAN для кластеризации урона
Идея хайлайтов простая и очень “человеческая”:
хайлайт — это не одно событие, а пачка событий рядом по времени
Если урон идет “сериями” (например, команда навалилась в файте), то события будут собираться в группы.
DBSCAN — алгоритм без учителя для кластеризации: он объединяет точки, которые лежат рядом.
Суть кластеризации для таймлайна
Мы можем кластеризовать не “урон”, а координату по времени:
epsзадает “максимальную дистанцию” между соседями по времениmin_samplesзадает “сколько соседей нужно”, чтобы точка считалась частью кластера
Пример подхода: считать, что “рядом по времени” — это около 30 секунд.
from sklearn.cluster import DBSCAN
dbscan = DBSCAN(eps=30, min_samples=2)
cluster = dbscan.fit_predict(df_player_late_damage[["time", "ones"]])
df_player_late_damage["cluster"] = cluster
Что мы получим
- Каждому событию присвоится номер кластера
- Метка
-1означает “выброс” (обычно отдельные одиночные события)
В терминах таймлайна:
- кластер = потенциальный интервал хайлайта
- выброс = мелочь или нерелевантные события
Как определить начало и конец временных промежутков для хайлайтов
Теперь осталось “склеить” события одного кластера в интервал.
Логика:
- Группируем по
cluster - берем первое
timeкакstart - берем последнее
timeкакend
Пример сборки интервалов
df_player_late_damage["stime"] = df_player_late_damage["time"].apply(
lambda t: f'{t // 60}:{str(t % 60).zfill(2)}'
)
df_action = df_player_late_damage.groupby("cluster").agg({
"stime": ["first", "last"]
})
df_action.sort_values(("stime", "first"), inplace=True)
print(df_action)
И получаете список “клипов по времени”, которые затем можно смотреть в игре или превращать в видео.
Идеи для усовершенствования поиска хайлайтов
Алгоритм уже работает, но его можно сделать “умнее”. Вот идеи, которые дают реальный прирост качества:
1) Упорядочить хайлайты по эпичности
Сейчас кластер определяет интервал, но не “насколько он лучший”.
Эпичность можно оценивать суммой урона в кластере:
- больше
valueсуммарно — вероятнее, что это настоящий файт
Можно сделать дополнительную метрику:
| Метрика | Что дает |
|---|---|
| сумма урона в кластере | выделяет драки, а не случайные тычки |
| количество событий урона | помогает, когда урон поровну, но событий больше |
| число убийств в интервале | делает хайлайт “жирнее” |
| наличие модификаторов (бафф/стан) | подчеркивает контроль и комбинации |
2) Использовать не только урон
Хайлайты — это не только DOTA_COMBATLOG_DAMAGE. Для богатого сюжета полезно добавить:
DOTA_COMBATLOG_DEATH- события модификаторов (стан, ультимейты и т.п.)
- возможно, события про покупки или резкие всплески золота
Тогда DBSCAN будет кластеризовать “интересные события”, а не только урон.
3) Улучшить параметры eps
Обычно “eps=30” работает как старт. Но разные стадии матча могут вести себя по-разному:
- в лейнинге драки редкие
- в мид-гейме плотность выше
Значит eps можно адаптировать:
- например, делать разные eps до и после определенной минуты
Как реализовать автоматическую запись видео моментов
Когда интервалы хайлайтов найдены (start/end), остается задача превратить их в видео.
Вместо ручных действий в клиенте можно использовать консольные команды перемещения по демке, например:
demo_gotodemo_gototick
Логика процесса такая:
- Запускаете матч-демо
- Делаете переход на
start - Включаете запись на время
end - start - Повторяете для каждого кластера
Мини-схема для автоматизации
flowchart LR
A[Интервалы start/end из DBSCAN] --> B[demo_goto на start]
B --> C[Запись от start]
C --> D[demo_gototick на end]
D --> E[Останов записи]
E --> F[Файл видео для хайлайта]
Короткое резюме процесса
| Шаг | Что делаем | Результат |
|---|---|---|
| 1 | скачиваем match_id.dem |
исходный файл реплея |
| 2 | прогоняем через Clarity | поток JSON событий |
| 3 | фильтруем DOTA_COMBATLOG_DAMAGE |
таблица урона между героями |
| 4 | строим таймлайн | график “когда били” |
| 5 | DBSCAN по времени | кластеры “пачек” урона |
| 6 | start/end по кластерам | список интервалов хайлайтов |
| 7 | запись демо командой | готовые видео фрагменты |
Если вы строите такой пайплайн впервые, главное — держать в голове простую мысль: реплей — это события, хайлайты — это их группировка по времени, а DBSCAN отлично подходит, когда “важные моменты” идут сериями, а одиночные эпизоды шумят.