Parse Age of Empires 2 recorded games.
Project description
mgz-fast
A stripped-down version of aoc-mgz, tailored specifically for the needs of AoE2Insights. It contains only the bare essentials required for fast header and body parsing -- everything else has been removed.
Supported Formats
- Userpatch 1.5 (
.mgz) - HD Edition 5.8 (
.aoe2record) - Definitive Edition (
.aoe2record)
Installation
pip install mgz-fast
Usage
Parsing the Header
Extract game metadata like players, map, version, and settings from a recorded game file.
from mgz.fast.header import parse
with open("match.aoe2record", "rb") as f:
header = parse(f)
print(header["version"]) # Version.DE
print(header["game_version"]) # e.g. "7.7"
print(header["save_version"]) # e.g. 13.34
Players
from mgz.fast.header import parse
with open("match.aoe2record", "rb") as f:
header = parse(f)
for player in header["players"]:
print(player["name"])
print(player["civilization_id"])
print(player["color_id"])
print(player["position"]) # {"x": ..., "y": ...}
For Definitive Edition recordings, additional player data is available:
for de_player in header["de"]["players"]:
print(de_player["name"])
print(de_player["censored_name"])
print(de_player["team_id"])
Map and Game Settings
from mgz.fast.header import parse
with open("match.aoe2record", "rb") as f:
header = parse(f)
print(header["scenario"]["map_id"])
print(header["lobby"]["seed"])
print(header["lobby"]["population"])
print(header["lobby"]["game_type_id"])
print(header["metadata"]["speed"])
Parsing the Body (Actions)
Iterate over in-game operations like player actions, chat messages, and sync ticks.
import os
from mgz.fast import operation, meta
from mgz.fast.header import parse
from mgz.fast.enums import Operation, Action
with open("match.aoe2record", "rb") as f:
eof = os.fstat(f.fileno()).st_size
header = parse(f)
meta(f)
while f.tell() < eof:
try:
op_type, payload = operation(f)
except EOFError:
break
if op_type == Operation.ACTION:
action_type, action_data = payload
if action_type == Action.RESIGN:
print(f"Player {action_data['player_id']} resigned")
elif action_type == Action.RESEARCH:
print(f"Player {action_data['player_id']} researched {action_data['technology_id']}")
elif action_type == Action.BUILD:
print(f"Player {action_data['player_id']} built {action_data['building_id']}")
elif op_type == Operation.CHAT:
print(f"Chat: {payload}")
Extracting Chat Messages
import os
from mgz.fast import operation, meta
from mgz.fast.header import parse
from mgz.fast.enums import Operation
with open("match.aoe2record", "rb") as f:
eof = os.fstat(f.fileno()).st_size
header = parse(f)
meta(f)
while f.tell() < eof:
try:
op_type, payload = operation(f)
except EOFError:
break
if op_type == Operation.CHAT:
print(payload.decode("utf-8", errors="replace"))
Calculating Game Duration
import os
from mgz.fast import operation, meta
from mgz.fast.header import parse
from mgz.fast.enums import Operation
with open("match.aoe2record", "rb") as f:
eof = os.fstat(f.fileno()).st_size
header = parse(f)
meta(f)
elapsed_ms = 0
while f.tell() < eof:
try:
op_type, payload = operation(f)
except EOFError:
break
if op_type == Operation.SYNC:
increment, checksum, data = payload
elapsed_ms += increment
minutes = elapsed_ms / 1000 / 60
print(f"Game duration: {minutes:.1f} minutes")
CLI Tools
Installing mgz-fast also provides four command-line utilities for working with recorded game files.
mgz-parse-header
Parse the header of a recorded game and output it as JSON.
# Parse header and write JSON to file
mgz-parse-header match.aoe2record -o header.json
# Parse with debug logging (useful for troubleshooting)
mgz-parse-header match.aoe2record --debug
# Also works with ZIP archives
mgz-parse-header match.zip -o header.json
mgz-parse-body
Parse the body (actions/events) of a recorded game. Expects an extracted body file as input (see mgz-extract) and outputs JSON Lines to stdout.
# Parse body and print operations as JSON Lines
mgz-parse-body match.body.bin
# Pretty-print each operation
mgz-parse-body match.body.bin --indent 2
mgz-extract
Extract the header and body from a recorded game into separate binary files. The header is automatically decompressed.
# Extract to default paths (<name>.header.bin, <name>.body.bin)
mgz-extract match.aoe2record
# Specify custom output paths
mgz-extract match.aoe2record --header h.bin --body b.bin
mgz-dump
Hex-dump arbitrary byte ranges from a recorded game's header or body. Useful for reverse engineering and debugging.
# Dump first 256 bytes of the decompressed header
mgz-dump match.aoe2record header
# Dump 128 bytes starting at offset 0x2e0
mgz-dump match.aoe2record header --offset 0x2e0 --length 128
# Dump the beginning of the body
mgz-dump match.aoe2record body --offset 0 --length 64
Header Fields Reference
The dictionary returned by parse() contains:
| Key | Type | Description |
|---|---|---|
version |
Version |
Game version (USERPATCH15, DE, HD) |
game_version |
str |
Version string |
save_version |
float |
Save file version |
players |
list[dict] |
Player info (name, civ, color, position, diplomacy) |
map |
dict |
Map dimensions and tile data |
scenario |
dict |
Map ID, difficulty, scenario filename |
lobby |
dict |
Seed, population, game type, chat, lock teams |
metadata |
dict |
Game speed, perspective owner, cheats |
de |
dict|None |
DE-specific data (extended player info, settings) |
hd |
dict|None |
HD-specific data |
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file mgz_fast-1.0.0.tar.gz.
File metadata
- Download URL: mgz_fast-1.0.0.tar.gz
- Upload date:
- Size: 20.4 MB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.10.6 {"installer":{"name":"uv","version":"0.10.6","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9d111dfe66a2306c161901546693be97b33d72334a6daa76f42d7a16c047b435
|
|
| MD5 |
0c56cdb857de2a69a56aa538c8cc36ea
|
|
| BLAKE2b-256 |
0036f338800447e64614b2abac883091098d9f2ac7fdcad7b47227e9d6ca4db6
|
File details
Details for the file mgz_fast-1.0.0-py3-none-any.whl.
File metadata
- Download URL: mgz_fast-1.0.0-py3-none-any.whl
- Upload date:
- Size: 30.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.10.6 {"installer":{"name":"uv","version":"0.10.6","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
79489857034cce6ff4acd68fdb247241fa5ff5408cccdeb307996b609568257e
|
|
| MD5 |
cc9b6a5e0cd7635a81a66c199ca0dc3c
|
|
| BLAKE2b-256 |
4afe5fb1da9950cd9c58f63a19e4e19c9893c25064570e5dbe41527a2ac6b979
|