Skip to content

Universal Messages Guide

AI Summary

Parse any of 272 message types using parse_universal(). Filter by callback name (substring match). Common messages: CDOTAUserMsg_ChatMessage (chat), CDOTAUserMsg_LocationPing (pings), CDOTAUserMsg_ItemPurchased (items), CDOTAUserMsg_UnitEvent (abilities), CDOTAUserMsg_OverheadEvent (damage numbers). Each message has tick, net_tick, type, and data dict. Always set max_messages to avoid memory issues. Message data structure varies by type.


Overview

The universal parser can extract any of the 272 message callback types from a replay. It's the most flexible parsing method.

from python_manta import MantaParser

parser = MantaParser()
result = parser.parse_universal("match.dem", "CDOTAUserMsg_ChatMessage", 100)

for msg in result.messages:
    print(f"[{msg.tick}] {msg.type}: {msg.data}")

Message Filtering

Substring Matching

The filter matches callback names as substrings:

# Matches CDOTAUserMsg_ChatMessage, CDOTAUserMsg_ChatEvent, etc.
chat = parser.parse_universal("match.dem", "Chat", 100)

# Matches CDOTAUserMsg_LocationPing
pings = parser.parse_universal("match.dem", "Ping", 50)

# Matches multiple message types containing "Unit"
units = parser.parse_universal("match.dem", "Unit", 200)

Exact Callback Names

For precise matching, use the full callback name:

result = parser.parse_universal("match.dem", "CDOTAUserMsg_ChatMessage", 100)

All Messages

Use empty string to capture all message types (use with caution):

# WARNING: Can produce millions of messages!
all_messages = parser.parse_universal("match.dem", "", 1000)

# See what types were captured
types = set(msg.type for msg in all_messages.messages)
print(f"Captured {len(types)} different message types")

Common Message Types

Chat Messages

result = parser.parse_universal("match.dem", "CDOTAUserMsg_ChatMessage", 200)

for msg in result.messages:
    player_id = msg.data.get("source_player_id")
    text = msg.data.get("message_text", "")
    chat_type = msg.data.get("chat_type", 0)  # 1=all, 2=team, etc.

    type_name = {1: "All", 2: "Team", 3: "Spectator"}.get(chat_type, "Unknown")
    print(f"[{msg.tick}] Player {player_id} ({type_name}): {text}")

Map Pings

result = parser.parse_universal("match.dem", "CDOTAUserMsg_LocationPing", 100)

for msg in result.messages:
    player_id = msg.data.get("player_id")
    ping_data = msg.data.get("location_ping", {})
    x = ping_data.get("x", 0)
    y = ping_data.get("y", 0)
    ping_type = ping_data.get("type", 0)

    print(f"[{msg.tick}] Player {player_id} pinged ({x}, {y}) type={ping_type}")

Item Purchases

result = parser.parse_universal("match.dem", "CDOTAUserMsg_ItemPurchased", 500)

from collections import defaultdict
purchases = defaultdict(list)

for msg in result.messages:
    player_id = msg.data.get("player_id")
    item_id = msg.data.get("item_ability")

    purchases[player_id].append(item_id)

for player_id, items in purchases.items():
    print(f"Player {player_id}: {len(items)} items purchased")

Unit Events (Ability Usage)

result = parser.parse_universal("match.dem", "CDOTAUserMsg_UnitEvent", 200)

for msg in result.messages:
    event_type = msg.data.get("msg_type", 0)
    entity = msg.data.get("entity_index")

    # Event types: 1=spawn, 2=speech, 3=add_gesture, etc.
    print(f"[{msg.tick}] Entity {entity}: event type {event_type}")

Overhead Events (Damage Numbers)

result = parser.parse_universal("match.dem", "CDOTAUserMsg_OverheadEvent", 300)

for msg in result.messages:
    event_type = msg.data.get("message_type", 0)
    value = msg.data.get("value", 0)
    target = msg.data.get("target_player_entindex")

    # Types: 1=gold, 2=crit, 3=heal, 4=damage, 5=mana, 6=miss, etc.
    print(f"[{msg.tick}] Type {event_type}: {value} on entity {target}")

Game Rules State

result = parser.parse_universal("match.dem", "CDOTAUserMsg_GamerulesStateChanged", 20)

for msg in result.messages:
    state = msg.data.get("state", 0)

    state_names = {
        0: "INIT", 1: "WAIT_FOR_PLAYERS_TO_LOAD", 2: "HERO_SELECTION",
        3: "STRATEGY_TIME", 4: "PRE_GAME", 5: "GAME_IN_PROGRESS",
        6: "POST_GAME", 7: "DISCONNECT"
    }
    name = state_names.get(state, f"UNKNOWN({state})")
    print(f"[{msg.tick}] Game state changed to: {name}")

Message Data Structure

Each MessageEvent contains:

Field Type Description
type str Callback name (e.g., "CDOTAUserMsg_ChatMessage")
tick int Game tick when message occurred
net_tick int Network tick
data dict Message-specific payload
game_time float Game time in seconds (negative before horn)
game_time_str str Formatted game time (e.g., "-0:40", "5:32")

The data dictionary structure varies by message type.


Message Categories

User Messages (CDOTAUserMsg_*)

Game events visible to players: - CDOTAUserMsg_ChatMessage - Chat text - CDOTAUserMsg_ChatEvent - Chat events (kills, items) - CDOTAUserMsg_LocationPing - Map pings - CDOTAUserMsg_ItemPurchased - Item buys - CDOTAUserMsg_UnitEvent - Unit actions - CDOTAUserMsg_OverheadEvent - Floating numbers - CDOTAUserMsg_ParticleManager - Visual effects - CDOTAUserMsg_CreateLinearProjectile - Projectiles

Network Messages (CNETMsg_*)

Low-level network data: - CNETMsg_Tick - Tick sync (very frequent!) - CNETMsg_SetConVar - Console variables - CNETMsg_StringCmd - String commands

Service Messages (CSVCMsg_*)

Server-to-client: - CSVCMsg_ServerInfo - Server info - CSVCMsg_CreateStringTable - String tables - CSVCMsg_UpdateStringTable - Table updates - CSVCMsg_PacketEntities - Entity updates

Demo Messages (CDemo*)

Recording-related: - CDemoFileHeader - File header - CDemoFileInfo - Match info - CDemoFullPacket - Full state snapshot


Analysis Examples

Chat Timeline

result = parser.parse_universal("match.dem", "CDOTAUserMsg_ChatMessage", 500)

print("Chat Timeline:")
print("-" * 60)

for msg in result.messages:
    tick = msg.tick
    player = msg.data.get("source_player_id", "?")
    text = msg.data.get("message_text", "")

    if text:  # Skip empty messages
        print(f"[{tick:>7}] Player {player}: {text}")

Ping Heatmap Data

result = parser.parse_universal("match.dem", "CDOTAUserMsg_LocationPing", 500)

from collections import defaultdict
ping_locations = defaultdict(int)

for msg in result.messages:
    ping = msg.data.get("location_ping", {})
    # Bucket to 500-unit grid
    x = (ping.get("x", 0) // 500) * 500
    y = (ping.get("y", 0) // 500) * 500
    ping_locations[(x, y)] += 1

print("Ping Hotspots:")
for loc, count in sorted(ping_locations.items(), key=lambda x: -x[1])[:10]:
    print(f"  ({loc[0]}, {loc[1]}): {count} pings")

Message Type Statistics

result = parser.parse_universal("match.dem", "", 10000)

from collections import Counter
type_counts = Counter(msg.type for msg in result.messages)

print("Message Type Distribution:")
for msg_type, count in type_counts.most_common(20):
    print(f"  {msg_type}: {count}")

Item Build Order

result = parser.parse_universal("match.dem", "CDOTAUserMsg_ItemPurchased", 1000)

from collections import defaultdict
player_items = defaultdict(list)

for msg in result.messages:
    player_id = msg.data.get("player_id")
    item_id = msg.data.get("item_ability")

    player_items[player_id].append({
        "tick": msg.tick,
        "item": item_id
    })

# Print build order for each player
for player_id, items in sorted(player_items.items()):
    print(f"\nPlayer {player_id} item order:")
    for i, item in enumerate(items, 1):
        print(f"  {i}. Item {item['item']} at tick {item['tick']}")

Performance Warning

Warning

Some message types generate thousands or millions of entries per match. Always set max_messages to prevent memory exhaustion.

High-Frequency Messages

Message Type Frequency Notes
CNETMsg_Tick ~1M+ Every network tick
CSVCMsg_PacketEntities ~500K+ Entity updates
CDOTAUserMsg_ParticleManager ~100K+ Visual effects
CDOTAUserMsg_OverheadEvent ~50K+ Damage/heal numbers
# Safe limits for common types
chat = parser.parse_universal("match.dem", "ChatMessage", 1000)
pings = parser.parse_universal("match.dem", "LocationPing", 500)
items = parser.parse_universal("match.dem", "ItemPurchased", 2000)

# For analysis, sample large types
tick_sample = parser.parse_universal("match.dem", "CNETMsg_Tick", 100)

All 272 Callbacks Reference

For a complete list of all available callbacks, see the Callbacks Reference.

Common useful callbacks:

Callback Use Case
CDOTAUserMsg_ChatMessage Player chat
CDOTAUserMsg_LocationPing Map pings
CDOTAUserMsg_ItemPurchased Item builds
CDOTAUserMsg_UnitEvent Unit actions
CDOTAUserMsg_OverheadEvent Combat numbers
CDOTAUserMsg_GamerulesStateChanged Game phase changes
CDOTAUserMsg_ChatEvent Kill/event notifications
CMsgDOTACombatLogEntry Detailed combat data
CDemoFileInfo Match metadata