Let's talk

How a GAS multiplayer desync was diagnosed and fixed

Abilities fired twice and health read differently on the host once a second client connected. The cause was not a pile of bugs but one missing decision: who has authority. This 54-second breakdown walks through the diagnosis, the migration order and how the fix was verified, from case file 01.

The video

54 seconds · on-screen captions, no sound · reconstruction from case file 01, client footage withheld under NDA

Transcript

The video has no voiceover; this is its on-screen text, in order.

The problem (0:00–0:11)

  • 0:00 Case file 01 · Action RPG · under NDA. It worked in single player.
  • 0:03 Then a second client connected. Host: HP 100. Client: HP 72. Fireball ×2. (Illustrative values.)
  • 0:07 10 weeks to Early Access. 3-person team.

The engineering (0:11–0:35)

  • 0:11 Three triggers into one ability: client input, animation notify (every machine), server AI. Not ten bugs. One missing decision: who has authority?
  • 0:16 State lived in unreplicated floats. Client cooldown: ready. Server cooldown: 0.8 s left. Client fires, server rejects. (Illustrative values.)
  • 0:20 Migrated in an order that kept it playable: 1 Gameplay Tag taxonomy · 2 Attribute sets · 3 ASC on Player State · 4 Abilities, simplest first · 5 Gameplay Cues, last.
  • 0:26 Replication mode, chosen once: Full for nothing here, Mixed for player characters, Minimal for AI characters.
  • 0:31 Predict what the player feels. Confirm what the player keeps.

The result (0:35–0:54)

  • 0:35 Verified under latency and loss: 4-client PIE · Net PktLag · Net PktLoss. After each ability, server and client match on health, stamina and cooldown tags.
  • 0:40 20+ abilities replicated · 0 ability desyncs after QA · 10 weeks to Early Access.
  • 0:45 Documented, so the 3-person team owns it: tag taxonomy, replication modes and why, predict/confirm rule, how to add an ability without reading the C++.
  • 0:49 Same desync in your project? Book a free 45-minute teardown.

Root cause

Combat had been built as methods on character classes, with cooldowns and resources as plain floats on the character. That is coherent in single player. Across a listen server with up to four players it meant three things at once: abilities started from three different places with no single owner, the state that decided whether an ability could fire was never replicated, and there was no prediction record to roll back when the server disagreed.

The reported symptom sounded cosmetic. The real problem was that the authority model had never been written down, so every ability had quietly invented its own.

Engineering decisions

  • Use the Gameplay Ability System rather than a bespoke framework, because it ships a prediction and rollback model Epic maintains. A three-person team had no time to build and debug its own.
  • Migrate in dependency order, keeping the game playable at every step: tags, attribute sets, ASC on the Player State, abilities from simplest to hardest, cues last.
  • Choose replication modes once and write down why: Mixed for players, Minimal for AI, for bandwidth reasons rather than taste.
  • Predict only what is cheap to correct. Movement and cosmetic starts predict; anything that grants items, spends a contested resource or lands a death blow waits for the server.

How it was verified

Correctness on a networked system is not established by playing it. Every ability was bound on a dedicated test map and run in a four-client Play-In-Editor session with simulated latency and packet loss (Net PktLag, Net PktLoss). Server-side attribute logs were compared with each client's view after every activation, so a divergence showed up as a number rather than a feeling. Result as reported at handoff: 20+ replicated abilities and zero ability-state desyncs through QA and the Early Access launch.

What the video does not show

Truthfulness

The video is a diagram reconstruction. Client footage and code are withheld under mutual NDA. The HP and cooldown values on screen are illustrative; the outcome figures (20+, 0, 10 weeks, 3-person team) are as reported at handoff. The case file gives no latency or loss values, so none are shown.

The full write-up, including the replication mode table and the failure mode worth naming, is in case file 01: GAS migration on a live combat codebase.

Same desync in your project?

Start with a free 45-minute teardown on your project.