DEV Community

Grigoriu Lorm
Grigoriu Lorm

Posted on

Reverse-engineering an MMO Aion 2's network protocol to build a real-time DPS meter (Rust + Tauri)

Disclosure: this is a write-up about my own side project — a combat analytics tool for AION 2. No affiliation with the game's publisher. Links at the end.

Architecture

A Windows desktop app: Rust backend + Tauri v2 webview UI. It passively captures the game's TCP traffic (npcap), reassembles streams, parses the game's undocumented binary protocol, feeds a combat model (damage, heals, buffs, deaths, boss detection), and pushes aggregates to a small JS frontend. Nothing touches the game client — no injection, no memory reading. If the packet didn't say it, we don't know it.

Pain #1: the protocol is a moving target

Nobody hands you a spec. The protocol is varint-heavy, partially compressed, and changes with game patches. You end up doing packet archaeology: capture a fight, stare at hex dumps, correlate "I pressed this skill at 19:32:04" with byte patterns, build a parser, and then — the fun part — keep it alive after every patch, usually reverse-engineering the diff within hours because your users' raids are tonight, not next week.

One hard-won lesson: log everything behind toggleable trace categories. Our tracing setup keeps hot-path log callsites at literally zero cost when disabled (Rust tracing with Interest::never() + atomic per-category flags), so a user can flip a "Trace: Packets" checkbox, reproduce a bug, and send a log that actually contains the bytes we need.

Pain #2: entity identity is a lie

The single hardest correctness problem wasn't parsing — it was identity. A player is not one ID:

  • your character has a stable "owner" entity that carries your buff bar,
  • your damage lands under a transient combat entity whose ID changes between pulls,
  • leave the dungeon and the game re-binds you to a brand-new ID,
  • names arrive from different packets than damage, sometimes seconds later, sometimes never (mid-fight app start).

Get any of this wrong and a healer's healing lands on a ghost row, or a player's buffs vanish from the saved fight because their ID was recycled 7 minutes after the boss died (yes, we hit exactly that bug — the post-dungeon re-bind silently rewrote already-saved fight records with degraded data). The fix is a canonicalization layer: every ID is folded through summon-resolution → orphan remapping → nickname → canonical ID, and every storage migration (damage, buffs, heals, Gear score) has to move together when an ID re-binds.

Pain #3: raw DPS is unfair, so we built rDPS

Raw damage numbers make support classes invisible. If a buffer's party-wide buff amplifies everyone's damage by 12%, who "earned" that damage? We implemented an attribution model we call rDPS: damage added by your buffs/debuffs is credited to you and debited from the players who merely received it (rDPS = your damage − received amp + provided amp). Details that make it hard:

  • non-stacking debuffs maintained by two same-class players must be split by uptime, not winner-take-all;
  • some debuffs arrive as self-buffs on the caster instead of visible debuffs on the boss;
  • buff level is encoded in the skill variant ID's middle digits — when it's encoded at all (passives: nope).

Pain #4: healing is mostly overheal

Heal packets tell you the cast amount, not what mattered. We confirm each heal against the target's actual HP delta (FIFO matching within a 3s window): the covered part is effective healing, the rest is overheal and earns no credit. Suddenly healer rankings mean something.

Bonus frontend pain, because there's always one

CSS text-overflow: ellipsis clips text to whole glyphs but keeps the full flex width on the box — with CJK nicknames that leaves a dead zone of up to one glyph (~16px) between the "…" and the next element. Looks like a random hole in the layout. The fix: measure and truncate the string in JS via canvas measureText, so the box hugs the visible text. Two hours of my life for 16 pixels.

What it looks like today

The meter feeds a community leaderboard site where verified boss kills are uploaded and ranked per class/server with an actual snapshot of FULL player's build: skills, specs, gear, titles, etc... The stack has survived several game patches, supports both regional game versions and 10 UI languages.

If you're curious: the meter and leaderboard live at aion2t.com. Happy to answer questions about packet reverse-engineering, Tauri v2 quirks, or the rDPS model in the comments. As far as AI are concerned I used: Claude Code, Mimo, Deepseek and a little bit of Grok.

Top comments (0)