Turn Dota 2 replays into analysis-ready Python data.
Parse Source 2 .dem files offline into typed models, pandas DataFrames,
JSON, Parquet, and interactive HTML reports.
Documentation · Quickstart · API reference · Changelog
Install
Gem requires Python 3.10 or newer.
pip install gem-dota
Using another package manager? Run uv add gem-dota or poetry add gem-dota.
Parquet export additionally requires pyarrow or fastparquet; see the
DataFrame and export reference
for current engine notes.
Quickstart
import gem
match = gem.parse("match.dem")
print(f"Score: {match.radiant_score}–{match.dire_score}")
for player in match.players:
hero = gem.constants.hero_display(player.hero_name)
print(f"{hero:20} {player.kills}/{player.deaths}/{player.assists} {player.net_worth:,} NW")
Convert the same structured result to JSON.
json_payload = gem.to_json(match, indent=2)
Need tables instead?
frames = gem.parse_to_dataframe("match.dem")
players = frames["player_summary"] # one row per player
series = frames["player_timeseries"] # sampled gold/XP/net worth
positions = frames["positions"]
combat = frames["combat_log"]
Or use the CLI:
python -m gem match.dem
python -m gem match.dem --format json --output match.json
python -m gem batch replays/ --format parquet --output out/ --workers 4
Why Gem?
| | |
|---|---|
| 🐍 Python-native
Typed match objects plug directly into notebooks, pandas, ML pipelines, and ordinary Python code. | 🔎 Replay-first
Analyze local replays without depending on third-party match-history availability. |
| ⚔️ Full-match context
Draft, combat, economy, vision, movement, objectives, items, chat, and fights in one model. | 📦 Flexible outputs
Work with dataclasses, DataFrames, JSON, Parquet, batch exports, or a self-contained HTML report. |
Gem is named after the Gem of True Sight: it reveals the structured match state hidden inside dense replay bytes. The parser is an independent Python implementation, cross-checked against Manta, Clarity, the OpenDota parser, and replay-derived validation fixtures.
Match reports
Gem can turn a parsed replay into a self-contained interactive report with overview, combat, laning, farming, fight, vision, economy, draft, and movement views.
Interactive HTML report generated from a real .dem replay.
Vision — scrub through observer and sentry ward activity. |
Fights — inspect positions, damage, abilities, and reveals. |
from gem.reports import write_html_report
write_html_report(match, "match-report.html")
Hero and item icons are optional local assets. See the asset-cache guide for setup and the report API for customization.
What you get
| Domain | Examples | |---|---| | Match and players | Scores, winner, duration, teams, K/D/A, level, GPM/XPM, final net worth | | Draft and objectives | Picks/bans, towers, barracks, Roshan, Aegis, Tormentor, building status | | Combat | Normalized combat log, damage/healing, kills, ability and item usage, fights | | Economy | Gold, XP, net-worth and minute-aligned advantage curves, purchases, buybacks | | Map state | Player positions, lane heatmaps, wards, smoke groups, courier snapshots | | Items | Final inventories, neutral-item finds, consumed upgrades, Roshan drops and banner plants | | Analysis | Nearby heroes, point-in-time lookups, ability levels, smoke lifecycles, vision, Roshan conversion | | Exports | DataFrames, JSON, Parquet, multi-replay processing, interactive HTML reports |
Useful entry points include:
gem.parse()→ParsedMatchgem.parse_to_dataframe()/gem.to_json()/gem.to_parquet()gem.parse_many*()for parallel replay batchesgem.find_player(),gem.position_at_tick(), andgem.fight_at_tick()gem.fetch_replay()for OpenDota/Valve replay download and decompression
MatchDetails data enables exact postgame
duration, damage, healing, GPM, and XPM values. Older or incomplete replays use
replay-derived fallbacks. Fields whose absence is meaningful—such as consumed-upgrade
flags—remain None; other outputs use the documented defaults for their field type.
Documentation
The hosted documentation covers both the public API and the replay format itself:
- Getting started
- Architecture
- Parser internals
- CLI reference
- Parser performance
- Experimental analysis
Performance and scope
Gem is a pure-Python parser optimized for research, batch analysis, and direct use in the Python data ecosystem. Multi-replay APIs distribute work across processes, while bounded Parquet export avoids retaining every completed match at once. The v0.8 performance study records the benchmark method, compatibility checks, and current optimization boundary without claiming an apples-to-oranges win over Go or Java parsers.
Some outputs are necessarily reconstructed:
- Vision estimation, farming-pattern analysis, and Roshan conversion are
- Incomplete replays can return partial output, and some exact postgame fields require embedded match details.
- Reliable versus unreliable gold and Healing Lotus pickups are not available from the replay event stream.
- Hero/item icons and map imagery are optional assets and are not shipped in the wheel.
Development
git clone https://github.com/whanyu1212/gem-dota.git
cd gem-dota
uv sync --group dev
uv run pytest
uv run pytest -m "integration and not network"
uv run ruff check src/ tests/
uv run mypy src/gem/
Contributions are welcome. Read CONTRIBUTING.md for the workflow and PR checklist. Parser changes should be checked against the pinned upstream reference revisions listed in CLAUDE.md and accompanied by focused regression tests. Additional tool guidance is available in AGENTS.md.
Acknowledgements
Gem builds on years of open work by the Dota replay community: Manta, Clarity, OpenDota parser, and dotaconstants.
No source code was copied from these projects; they are reference implementations used to understand protocol behavior and validate Gem's independent Python implementation. See THIRD_PARTY_LICENSES for license texts.