Files
MagentaDolphin ecd87ac96d feat(skills): ship ue-design-skills bundle, licensing and delivery gate
Phase 0 of the handoff plan, as a marketplace rather than a flat skills/
directory. Content moved out of the LyraResearch archive and depersonalised:
addresses stay in the archive, recipes ship.

- plugins/ue-design-skills: 17 skills, 232 failure-mode entries, each with the
  six required fields; catalog.json as the harness-neutral source of truth and
  .claude-plugin/ as one adapter over it.
- _gate: 16 rules, one poisoned fixture per rule, plus surface coverage so a
  declared file cannot silently miss the line rules.
- ADR-0002 (harness-neutral bundle behind a marketplace) and ADR-0003 (split
  licensing: CC BY-ND 4.0 prose, Apache-2.0 code and metadata).
- LICENSE files at both levels, CONTRIBUTING.md, docs/licensing-options.md as
  the material the licence decision grew from.

Verified: gate.py 0 violations; test_gate.py 16/16 rules redden on their
fixtures with a clean baseline and 2 root files reaching the line rules.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-05 23:48:55 +07:00

17 KiB

name, description
name description
ue-multiplayer-authority Design and review the authority model of a networked Unreal Engine product: which side owns each fact, what is predicted versus replicated versus validated, where responsibility boundaries run between GameState, PlayerState, Controller, Pawn and components, RPC and validation discipline, hit registration and lag compensation, replication modes, relevancy and cost, and the cheat surface. Use when starting a multiplayer feature, porting single-player code to network, reviewing trust boundaries, or investigating desync, rubber-banding, "works in PIE but not on dedicated server", or suspected cheating.

UE multiplayer authority

The invariant:

Every fact in a networked game has exactly one owner. Authority is not a property of code that says HasAuthority — it is the guarantee that no other machine can produce that fact.

Measured patterns from a reference product: patterns. Twenty detection recipes for silent trust failures: failure modes.

Read the failure modes before adopting or reviewing a networked design. Each entry names the mechanism, why it stays silent, why the obvious check misses it, and a command you can run against your own tree.

Related skills: ue-gas-architecture, ue-gameplay-messaging, ue-modular-gameplay, ue-cosmetics-and-teams, ue-architecture-guardrails.

Method, not architecture — how to check any claim in this bundle before repeating it: ue-evidence-discipline.


1. Start from one question

Before any code: for each fact in the game, who is allowed to be wrong about it?

  • If a client being wrong is a cosmetic glitch → the client may own it.
  • If a client being wrong is an advantage → the server must own it, and must be able to produce the value independently.
  • If a client being wrong is unrecoverable → the server must own it and persist it.

This question, answered per fact and written down, is the authority model. Everything else in this document is enforcement of that table.

The three states of a networked fact

State Meaning Who can be wrong
Predicted Client computes it immediately for responsiveness; server may overrule Client, temporarily, visibly
Replicated Server computes it, clients receive it Nobody — clients only observe
Validated Client proposes it, server independently verifies before accepting Client may lie; server must detect

The third is the expensive one, and it is the one that is usually missing. "Replicated" is often mistaken for "validated": a value that travelled from the client to the server and was then replicated back out to everyone is client authored, no matter how authoritative it looks on arrival.


2. What actually enforces authority

Unreal offers several mechanisms that look similar and enforce very different amounts. Know which is which before relying on one.

Mechanism Enforces Does not enforce
HasAuthority() guard that this code path runs on the server anything about callers that skip the guard
UFUNCTION(Server) routing to the server that the arguments are truthful
WithValidation that a _Validate function runs and can disconnect anything, if _Validate returns true unconditionally
BlueprintAuthorityOnly that Blueprint cannot call this nothing at all for C++ callers
replicated property one-way flow server → client that the server computed the value itself
GetLifetimeReplicatedProps conditions who receives it who may change it

Rules

  1. A public mutator on a replicated fact must guard itself, not rely on its callers. if (!HasAuthority()) return; at the top of the function, not at the call site — call sites multiply.
  2. WithValidation with an empty _Validate is worse than none: it passes review as validation while enforcing nothing.
  3. BlueprintAuthorityOnly is a Blueprint editor affordance. Never cite it as a security property.
  4. Write the guard even when the only current caller is the server. The guard is documentation the compiler enforces; the caller list is not stable.

3. Responsibility boundaries by layer

Put each fact in exactly one place. The most common source of networked bugs is a fact that lives in two.

Layer Exists on Owns Must not own
GameMode server only match rules, spawning, scoring decisions anything a client must read
GameState server + all clients match-wide replicated facts: phase, timer, team scores per-player secrets
PlayerState server + all clients per-player facts everyone may see: name, team, score, abilities input, camera, UI state
PlayerController server + owning client input, camera, client-directed commands, UI ownership facts other players need
Pawn / Character server + all clients embodiment: transform, movement, physical state player identity, persistent progression
Components follow their owner one cohesive slice of the owner's facts facts belonging to a different layer

Rules

  1. GameMode exists only on the server. Any client code path referencing it is a bug that PIE will hide.
  2. Persistent player facts belong to PlayerState, not the Pawn. Pawns die.
  3. Input and camera belong to the Controller. A Pawn reading input directly cannot be possessed by AI or spectated.
  4. A component's authority is its owner's authority. A component attached to a client-owned actor cannot be authoritative regardless of its internal guards.
  5. Simulated proxies have no controller. Any logic gated on "has a controller" silently excludes them; any logic that assumes one will null-deref on them.

4. RPC discipline

Choosing the direction

Client -> Server      UFUNCTION(Server, Reliable/Unreliable, WithValidation)
Server -> owner       UFUNCTION(Client, ...)
Server -> everyone    UFUNCTION(NetMulticast, ...)

Rules

  1. Every Server RPC carries WithValidation and a _Validate that actually checks. Range, ownership, state legality, rate. An RPC without validation is a client-side API into your server.
  2. Reliable is a budget, not a default. Reliable RPCs are ordered and queued; overflowing the queue disconnects the client. Anything cosmetic, frequent or idempotent should be unreliable.
  3. Never multicast what a replicated property already carries. The property handles joiners and relevancy; a multicast does not.
  4. Multicasts do not reach late joiners or newly relevant clients. Anything a late joiner must know is state, not an event.
  5. Validate on the server that the sender owns the thing it is acting on. Routing to the server proves nothing about the subject of the call.
  6. Rate-limit anything a client can call in a loop.

Debug and cheat commands

Cheat entry points reachable from a client are the highest-value target in the codebase. They must be compiled out of shipping builds — not merely hidden, disabled by a flag, or guarded by UI. A Server RPC that executes an arbitrary cheat string, present in a shipping binary, is a remote console.


5. Prediction

Prediction exists to hide latency, not to save server work. The server must still compute everything it accepts.

Safe to predict

  • movement through the engine's own prediction (correction path already exists);
  • animation, audio, VFX, camera;
  • UI affordances (cooldown wheels, ammo counters) that a correction can restore;
  • ability activation under a prediction key, where the server can reject.

Never predict

  • damage application;
  • score, currency, progression;
  • inventory grants;
  • death and elimination;
  • anything persisted.

Rules

  1. Every predicted value needs a defined correction path — what happens visually when the server disagrees. Undefined correction means the client keeps a wrong value forever.
  2. Prediction is a client-side optimisation of a server-side truth. If the server cannot independently produce the value, the feature is not predicted, it is client-authoritative.
  3. A prediction key is not validation. It reconciles ordering; it does not check whether the client was entitled to act.

6. Hit registration — the decision that shapes the whole product

This is where authority is usually lost, and it is a design decision that must be made explicitly and early, because retrofitting is expensive.

There are three positions:

Model Server does Cost Cheat exposure
Server-authoritative trace traces from its own state at the current tick cheap none, but shots feel like they miss under latency
Lag-compensated rewind stores per-tick historical positions, rewinds to the client's timestamp, re-traces expensive: memory, CPU, complexity low; the standard for competitive shooters
Client-reported hit accepts the client's hit result free total

Rules

  1. Choose consciously and write the choice down. Most projects arrive at the third by default, because the first feels bad and the second is work.
  2. If you accept client-reported hits, know exactly what a malicious client gains — typically arbitrary damage, arbitrary range, arbitrary target, arbitrary surface multipliers.
  3. The server cannot validate what it cannot reproduce. Without stored historical positions there is nothing to validate a client's hit against, so bolting on "validation" later is not a patch, it is building the rewind subsystem.
  4. Sanity checks are not validation, but they are not worthless. Maximum range, line-of-sight from the shooter's replicated position, fire-rate limits, team check, and "was the target ever near there recently" raise the cost of trivial cheats even without full rewind.
  5. Never let the client choose damage-modifying inputs. The surface/material that multiplies damage, the distance used for falloff, and the trace origin must all come from server-known state, even in a client-reported-hit model. These are the cheapest things to move server-side and the most valuable.

Recognising a stub

Watch for parameters threaded through many function signatures but never branched on, and for booleans initialised to a constant with a //@TODO. These are the attachment points of a rewind subsystem that was designed and never built. They make the code look like it validates. Recipes: NA-06, NA-07 in the failure modes.


7. Replication cost and relevancy

Authority decisions determine correctness; relevancy decisions determine whether the product ships.

Rules

  1. Pick an ability-system replication mode deliberately. Full replicates all gameplay effects to all clients; Mixed replicates effects to the owner and tags/cues to everyone; Minimal replicates no effects to anyone. Player-controlled actors want Mixed; AI want Minimal.
  2. Net cull distance is a gameplay decision, not a performance knob. Setting it beyond the map size disables relevancy culling entirely — every actor replicates to every client, which is correct for a small arena and fatal for a large map.
  3. Prefer replicated state over event spam. State is idempotent, survives packet loss, and reaches late joiners.
  4. FastArraySerializer for collections that change incrementally, with the PreReplicatedRemove / PostReplicatedAdd / PostReplicatedChange callbacks all implemented — an empty callback is a client-side desync with no symptom on the server.
  5. Replication graph before you need it, or never. Enabling it late changes which actors clients see, and every relevancy assumption in gameplay code has to be re-verified. If it ships disabled, treat routing configuration as untested.
  6. Cosmetic state should not replicate. Replicate intent (which skin, which team), realise presentation locally.

8. Lifecycle across the network

  1. Readiness is a state chain, not a timing assumption. Actor spawned, controller assigned, PlayerState replicated, data initialised, gameplay ready — each an explicit state with explicit transitions.
  2. Client and server reach each state in a different order. Any code that assumes an order will work for the listen-server host and fail for remote clients.
  3. A simulated proxy passes through the chain without ever acquiring a controller. Gates must handle that case explicitly rather than by omission.
  4. Every dynamic addition needs its exact removal: granted abilities, applied effects, registered listeners, spawned cues, added tag paths. Test the removal on both server and client, in the same session, more than once.
  5. Death is not destruction. Define the full sequence — death started, effects removed, input released, cues cleaned, actor destroyed or respawned — and give each step one owner.

9. The cheat surface review

Enumerate, for the shipping build, every path a malicious client can reach:

1. Every Server RPC              -> does _Validate independently verify?
2. Every RPC argument            -> is any of it trusted without recomputation?
3. Every cheat/debug entry point -> is it compiled out, not just disabled?
4. Every damage input            -> which come from the client?
5. Every fail-open error path    -> what does the code do when a check errors out?
6. Every client-side gate        -> is there a matching server-side one?

Item 5 deserves attention: a permission check that returns "allowed" when it cannot determine the answer converts an unknown state into an exploit. Failure to determine must deny.


10. Test matrix

PIE is not a network test. A listen-server host shares memory with its client; half of the failure modes here cannot occur in it.

Mandatory configurations

  • dedicated server + two remote clients;
  • one client with 150 ms latency and 2% packet loss;
  • a client joining mid-match;
  • a client disconnecting mid-action;
  • an actor becoming relevant and then irrelevant.

Per feature

  • does it work for a remote client, not just the host?
  • does a late joiner reach the correct state?
  • what does a simulated proxy see?
  • what happens if the client sends the RPC twice, out of order, or with absurd arguments?
  • is the state correct after death and respawn, twice?

Authority regression

  • for each fact in the authority table, assert on the server that the client cannot change it — with a test client that tries.

11. Review checklist

  • Every fact has a named owner in a written authority table.
  • Each fact is classified predicted / replicated / validated.
  • Public mutators of replicated state guard themselves.
  • Every Server RPC has WithValidation with a real _Validate.
  • No trust in client-supplied damage inputs: origin, distance, surface, target.
  • Hit registration model is chosen and documented.
  • Cheat entry points are compiled out of shipping.
  • Error paths deny rather than allow.
  • Replication mode and net cull distance chosen per class, not inherited.
  • FastArray callbacks all implemented.
  • Every addition has a verified removal.
  • Feature tested on a dedicated server with a remote client under latency.
  • BlueprintAuthorityOnly is not cited as enforcement anywhere.

12. When client authority is the right answer

Deliberate client authority is legitimate when:

  • the fact is purely presentational;
  • the game is cooperative or single-player-with-friends and the threat model is "no adversary";
  • the cost of server validation exceeds the value of the protected fact;
  • an anti-cheat layer outside the game handles the threat.

In every one of these cases, the decision is written down with its threat model. The failure worth avoiding is not "we trusted the client" — it is "we did not know we trusted the client."

That distinction is what this skill exists to preserve. A codebase can be an excellent architectural reference and simultaneously an unsafe network reference; those are separate axes, and copying the first without noticing the second is the common accident.


Provenance

The findings behind the failure modes come from a line-by-line source audit of Epic's Lyra Starter Game on Unreal Engine 5.6, read rather than run. That project is an outstanding architectural reference and a poor network-security reference; those are independent axes, and the audit exists to keep them apart. Source addresses stay in the research archive that produced this skill; what ships is the detection recipe, because an address in someone else's tree is not something you can act on and a recipe is.

Each entry carries a stable identifier (NA-01, NA-02, …) that resolves back to the audited location in that archive. If you need the original address to settle a dispute, it exists and can be produced.

Evidence boundary

The measured material refers to one specific reference project on one engine version in one workspace. It is evidence of failure modes, not a guarantee about other engine versions or other samples. Several claims in it are explicitly marked as unanswerable without a dedicated-server build, and they stay unanswered. Re-run the recipes against your own tree before trusting any specific claim.