Skip to main content

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


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

mgz_fast-1.0.0.tar.gz (20.4 MB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

mgz_fast-1.0.0-py3-none-any.whl (30.5 kB view details)

Uploaded Python 3

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

Hashes for mgz_fast-1.0.0.tar.gz
Algorithm Hash digest
SHA256 9d111dfe66a2306c161901546693be97b33d72334a6daa76f42d7a16c047b435
MD5 0c56cdb857de2a69a56aa538c8cc36ea
BLAKE2b-256 0036f338800447e64614b2abac883091098d9f2ac7fdcad7b47227e9d6ca4db6

See more details on using hashes here.

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

Hashes for mgz_fast-1.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 79489857034cce6ff4acd68fdb247241fa5ff5408cccdeb307996b609568257e
MD5 cc9b6a5e0cd7635a81a66c199ca0dc3c
BLAKE2b-256 4afe5fb1da9950cd9c58f63a19e4e19c9893c25064570e5dbe41527a2ac6b979

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page