Fighting Game · Architecture

Online Backbone & Competitive Integrity

A focused page within the Fighting Game Architecture documentation. The full map and every sibling page live in the Architecture hub.

5sections12 minread1diagram

On this page

A competitive fighting-and-racing game lives or dies on two promises that are really one: that the netcode is fair, and that the ladder is honest. V2's online architecture exists to keep both — to get players into a low-latency, rollback-clean match across PC, PlayStation, Xbox, and Switch, and to make sure the result of that match means something on a ranked ladder that cheaters, smurfs, and throwers cannot quietly poison. This page covers the online backbone — the in-engine V2OnlineServices subsystem and the session surfaces it fronts (auth, friends, parties, matchmaking, ranked, replay, ghost, store) — and competitive integrity: anti-cheat, the replay-hash validation that rides V2's deterministic simulation, and the cross-play / cross-progression model that decides who may be matched with whom and what travels with an account. The deterministic match runtime those services wrap lives one layer down in Rollback Netcode & Tag-Team and Combat: GAS, Frame Data & Determinism; this page is about everything that happens around the match. The honesty bar is the project's own: where the code is a deterministic in-engine model rather than a wired backend, the page says so plainly, because "designed, not yet built" beats implying a shipped service. The section hub is ../V2_ARCHITECTURE.md.

What ships, honestly#

The split here is sharp, and stating it up front keeps the rest of the page honest — sharper, in fact, than the prose around this module usually admits.

The client-side online surface is real Unreal C++, but it is a deterministic model, not a wired backend. V2/ue/Source/V2OnlineServices ships a UV2OnlineServicesSubsystem (a UGameInstanceSubsystem, V2OnlineServicesSubsystem.h) over a ~25k-line type library (V2OnlineServicesTypes.cpp) and a ~20k-line Blueprint library (V2OnlineServicesBlueprintLibrary.cpp). It is the in-engine seam every menu, lobby, and matchmaking screen calls, and it is substantial. But its Build.cs declares exactly three dependencies — Core, CoreUObject, Engine. There is no OnlineSubsystem, no EOS SDK, no HTTP/Json, no FSocket anywhere in the module. What the C++ actually does is build configuration structs (BuildDefault…Config), validate them (Validate…), plan operations (Plan…Operation, Resolve…Request), and fold the results into a large FV2OnlineServicesRuntimeSnapshot of counters and readiness flags. The "EOS / Steamworks / GOG Galaxy / PlayStation / Xbox" surfaces are config-and-validator pairs with operation planners — and the console ones are even named honestly: FV2PlayStationSdkStubConfig, FV2XboxGdkSdkStub…. This is precisely the "build a POD struct, never transmit" shape the sister V5 backbone flagged before V5 replaced it with a real FHttpModule transport; V2 has not yet replaced it. The model is real, deterministic, and heavily tested; the wire is not in this module.

The real TypeScript in apps/v2 is the spine around the backbone, not the backbone itself. There are 91 service packages — but they are the live-ops, telemetry, trust-and-safety, AI, and identity tiers (season-pass-service, the telemetry-* family, kuanyin-* / themis-* moderation, iris-* / isis-* / psyche-* AI, nous-anti-cheat-classifiers, oshun-identity-binding, cross-product-entitlement). None of them is a session-backbone service: there is no auth, matchmaking, ranked, replay, friends, party, lobby, or store package. That backbone is specified as a gRPC surface under libs/proto/v2/online/ — which does not exist in the tree — so the method signatures below are the designed contract, not a generated stub. The kernel anti-cheat client at V2/ue/Plugins/V2AntiCheat/ is likewise absent (the present plugins are V2AICommentary, V2AdaptiveAI, V2AssetLinter, V2Editor, BellonaUnrealEditor).

The one integrity claim that is architecturally real today is the one that needs no backend at all: because the match runs on a deterministic, integer-frame simulator, the server can re-simulate from inputs and compare a hash. That check rides the determinism the engine already guarantees — and it is where this page's honesty pays off.

The online backbone#

A deterministic in-engine service model#

V2OnlineServices is best understood as the shape of the online backbone expressed in validated, deterministic C++. Each session surface is a triple: a config builder, a validator, and — where an operation is involved — a planner that turns a typed request into a typed result without touching a socket. The platform-abstraction layer is the clearest example: BuildDefaultPlatformSdkAbstractionSpecValidatePlatformSdkAbstractionSpecResolvePlatformSdkRequest, with provider-specific siblings PlanEosSdkOperation, PlanSteamworksSdkOperation, PlanGogGalaxySdkOperation, and the explicitly-named PlanPlayStationSdkStubOperation / PlanXboxGdkSdkStubOperation. The EOS "baseline" is enforced as a validation rule, not a link dependency: EV2OnlineBackendProvider::EOS must bind the PC OnlineServicesEOS subsystem, and a valid backend requires EOS-on-PC plus Sony, Xbox, and Switch shims (V2OnlineServicesTypes.cpp:1108–1155) — so the topology the monolith promises is checked, even though nothing dials out. The whole runtime is observable through UV2OnlineServicesSubsystem::CaptureRuntimeSnapshot() (V2OnlineServicesSubsystem.cpp:3729), which renders the entire backbone — friends, parties, matchmaking, ranked seasons, replay theater, moderation, fleet, server health — into one inspectable FV2OnlineServicesRuntimeSnapshot. That snapshot is what the automation suite asserts against, and it is why the model is testable without a server.

Enums anchor the surface: EV2OnlineAuthState (V2OnlineServicesTypes.h:7), EV2MatchmakingTeamRole (:159), EV2PlatformFamily (:392), EV2OnlineBackendProvider (:421), and EV2OnlineInputMethod for input-class pairing. Gameplay code never branches on platform — it asks the subsystem and reads the snapshot.

flowchart TB subgraph Client["V2 client · Unreal C++ — deterministic model, no transport"] SUB["UV2OnlineServicesSubsystem<br/>config · validate · plan"] SNAP["FV2OnlineServicesRuntimeSnapshot<br/>CaptureRuntimeSnapshot()"] SUB --> SNAP end SUB -. designed gRPC · libs/proto/v2/online absent .-> GW[API gateway / EOS baseline] GW --> AUTH[auth] & FR[friends · parties] & MM[matchmaking] & RANK[ranked · leaderboards] & REP[replay · ghost] & ST[store] MM --> SK[(Glicko-2 / TrueSkill2 profile defaults)] RANK --> SK REP --> OBJ[(object storage)] SUB -. emits v2.match.replay.hash .-> DET{{re-sim 1% · compare state hash}} DET -->|drift greater than 0| REVIEW[manual review] SUB -. publishes v2.* .-> EB[("@oshun/event-bus")] EB -. real TS consumers .-> CONS[themis · kuanyin · maat · iris · nous-anti-cheat-classifiers] KERNEL["V2AntiCheat kernel plugin — absent"] -. planned .-> AUTH

The session surfaces and the event-bus contract#

The subsystem fronts the full session set: auth (platform-token sign-in exchanged for a V2 session, with refresh), friends & presence (including a cross-platform unified friends list that resolves identities and de-duplicates aliases), parties (≤ 8, the unit matchmaking submits on a player's behalf), matchmaking (per-mode queues, below), lobby (custom rooms and the Battle Hub), ranked / leaderboards (seasonal, per mode), replay cloud & ghost store (UploadReplayFileToCloud, V2OnlineServicesSubsystem.cpp:926, plus share-by-code and Tekken-ghost export), and store (the cosmetic catalogue, detailed in the live-ops sibling).

The backbone's integration contract is event-bus-native by design. The monolith specifies the lifecycle topics it publishes — v2.player.session.started/ended, v2.match.ended, v2.match.replay.hash, v2.player.reported, v2.cosmetic.purchased, v2.matchmaking.ticket.completed — and the cross-domain signals it consumes — themis.appeal.resolved, maat.balance.recommendation, iris.commentary.generated, kuanyin.action.taken. The honest seam: the C++ model does not itself publish to a bus (it has no transport); the live publication rides the platform @oshun/event-bus, and the consumers are the real TS services in apps/v2 (themis, kuanyin, maat, iris). This is what lets moderation, balance, and AI commentary act on match outcomes without V2 reaching into their databases.

Matchmaking and the ranked ladder#

Matchmaking is the backbone's most fairness-sensitive job, and the model carries it as data plus deterministic policy. A ticket is an FV2MatchmakingTicket submitted through SubmitMatchmakingTicket(…) (V2OnlineServicesSubsystem.cpp:342); each queue is per-mode (ranked 1v1, 2v2 tag, FFA, the racing modes) and each player carries an FV2MatchmakingSkillProfile. That profile is a genuine dual-model record with the canonical default constants: TrueSkill μ = 1000, σ = 83.333 and Glicko-2 rating = 1500, RD = 350, volatility = 0.06 — the same constants Glickman's reference uses and the same family the platform's @oshun rating services use elsewhere. HasExplicitSkillProfile (V2OnlineServicesTypes.cpp:774) treats any deviation from those defaults (or a non-zero placement-match count) as "this player has played," which is how placement-vs-established is told apart. Role balancing is real logic, not a flag: ContainsPointAndPartnerRole requires a Point plus an Assist / Anchor / Support for tag and FFA team formation, and the snapshot exposes bHasRoleBalancedFourPlayerQueue. Region weighting, a ping cap, and search-radius expansion over wait time are modeled as bRegionAwareMatchmakingReady, LastRegionAwareMatchmakingPingMs, bLastRegionAwareMatchmakingWithinPing, and bLastMatchmakingUsedExpansion.

The honest qualification: this is a skill-and-policy data model with validators, not a live rating solver. Unlike the V5 backend — which runs an actual Glicko-2 update with an Illinois root-find on the volatility step — V2's module stores the profile and its defaults and records results. RecordRankedMatchResult (:681) delegates to ApplyRankedMatchResult and copies the resulting skill into the ladder entry rather than re-deriving a rating from a worked formula. The seasonal ladder on top is modeled in depth: ApplyRankedSeasonReset (:747), a hidden-MMR policy (bRankedSeasonHiddenMMRReady, asserted to keep hidden MMR never-shown and to split the primary and secondary skill models), a unified cross-platform MMR pool with cross-platform leaderboards, placement, promotion/demotion thresholds, and a per-season reset. The designed SLOs are concrete enough to test against once the backend exists: ticket p50 ≤ 12 s, p99 ≤ 35 s under 5× expected launch concurrency, auth p99 ≤ 200 ms, replay upload p99 ≤ 8 s for a 10-MB replay, and ≥ 99.9% per-region availability. One correction to earlier drafts of this page: the ranked/matchmaking tuning does not live in V2/balance/online — that directory holds only world-boss-community-raid.json. The matchmaking defaults are the C++ constants above, and the tag-pairing curves live in V2/balance/tag/.

Competitive integrity#

Integrity is defended in depth, and — critically — the load-bearing line of defense is the one that is architecturally real. Because the match runs on a deterministic, integer-frame simulator (see Rollback Netcode & Tag-Team), the server can do what most fighting games cannot: re-simulate a match from its inputs and compare the result hash. Every match emits a golden-replay hash on v2.match.replay.hash; the design re-runs ~1% of matches end-to-end on a Linux validator pool, and any non-zero frame drift flags manual review. The runtime snapshot already carries the gate result (bLastRankedReplayDeterminismGatePassed). That check needs no kernel driver — only the determinism the engine guarantees — which is why it is the integrity claim this architecture can stand behind today.

The other tiers are real as validated contracts and modeled state, and the test surface here is genuinely large: V2/ue/Source/V2Tests/Automation/OnlineServices.Module.spec.cpp carries 736 assertions, and V2Tests/Private/Online/ holds 53 dedicated contract specs.

  • Server-side input/state validation. Input-rate sanity (≤ 180 inputs/s, bursts > 240 flag) and per-ability gameplay-effect whitelisting, exercised by the ServerMovementValidation, ServerHitValidation, ServerDamageValidation, ServerEconomyValidation, SpeedHackDetection, AimbotDetection, MovementHackDetection, WallhackMitigation, and StatisticalAnomalyDetection specs. These ride the same authoritative path the netcode owns, and a fabricated effect application invalidates the match. Modeled and tested; the live server that would run them is the absent backend.
  • Kernel client anti-cheat. EAC primary / BattlEye fallback on PC, platform-native anti-tamper on console (PSN ban-list, Xbox TruePlay, Switch NEX). This is the V2/ue/Plugins/V2AntiCheat/ surface that is not present — but the client-signal contracts are modeled and contract-tested (DriverIntegrity, ProcessScanner, MemoryIntegrityScanner, OverlayHookDetection, HardwareFingerprint, ThirdPartyAntiCheatIntegration, and AntiCheatModeOptIn / MinimalFootprint / ModuleObfuscation / SelfUpdate / ReportSystem), backed by ClientHeartbeat and the FairPlay* snapshot counters (FairPlayDetectionSignalCount, HwidBanCount, FairPlayForensicLogCount). Treat the kernel driver as the planned client tier, not a shipped one.
  • ML cheat classifiers. nous-anti-cheat-classifiers (real TS) over @nous/training + @nous/safety — smurf, win-trading, coordinated-throw, geographic-anomaly. Its governing rule is the honest one: classifier output can prioritise human review but cannot issue automatic discipline and cannot touch rollback frames (bLastSmurfBlockedRankProgression, bLastTournamentVpnBlocked are queue/priority signals, not bans). Model-driven moderation never silently bans, matching the platform's review-only, appealable posture and the @oshun/audit-platform forensic-retention contract.

Discipline, shadow-bans, and appeals are modeled (FairPlayBanSystem, FairPlayShadowBan, FairPlayBanAppealWorkflow, ReplayCheatReview, TrustedPlayerProgram) and route out to the shared trust-and-safety plane — Themis (themis-dispute-resolution, DSA Statement-of-Reasons) and Kuanyin — over the event bus, so a ban is auditable and reversible by the same machinery that governs every Oshun product. Rage-quit escalation (5-minute → 30-minute + 50 points → 24-hour ranked ban + 150 points) is carried as bLastRageQuitPenaltyEscalated.

Edge cases and failure modes

  • Unconfigured or malformed backend. Every operation returns through an OutFailureReason seam; an invalid ticket or backend config fails the validator loudly rather than silently "succeeding" — a fail-loud surface, not a fabricated ack.
  • Replay determinism drift. A re-sim mismatch surfaces as bLastRankedReplayDeterminismGatePassed = false and routes to ReplayCheatReview, never an automatic ban.
  • Console with no kernel driver. The SdkStub path validates platform-native anti-tamper bindings (PlayStationSdkStub, XboxGdkSdkStub specs) instead of asserting an EAC driver that cannot exist on console.
  • Classifier false positive. Output prioritises a review queue; the appeal workflow (FairPlayBanAppealWorkflow) is the only path to discipline, with a ≤ 0.5%-overturn threshold as the model-retrain trigger.
  • Cross-process hash drift. Mitigated upstream by the netcode's stable, content-based state hash, so a regression surfaces as a golden-replay mismatch in CI rather than a desync — or a false cheat flag — in the wild.

Cross-play & cross-progression#

Cross-play decides who can match whom; cross-progression decides what travels with the account. Both are policy-rich, and both are modeled as validated state in the subsystem (bCrossPlayPolicyValid, bCrossProgressionSnapshotValid) with the implementations specified as the backend services above.

Cross-play is on by default for ranked 1v1, 2v2 tag, custom lobbies, the Battle Hub, and World Tour across PC, PS5/PS5 Pro, XSX, and Switch 2, with per-platform system opt-out honoured at session start (toggle changes mid-session never affect the current match — bLastCrossPlatformMatchmakingOptOutRespected). The subtle part is input-method pairing: a ticket carries an EV2OnlineInputMethod, and matchmaking compares TicketInputMethod against CandidateInputMethod (V2OnlineServicesTypes.h:7353), so KBM, gamepad, fight-stick, fight-pad, arcade-stick, and adaptive-controller classes can be paired or segregated per mode. Cross-class is opt-in in ranked and default-on in unranked, and adaptive controllers are always opt-in for both sides, so accessibility hardware is never forced into a competitive mismatch. The iOS/Android companion is explicitly read-only and not a play surface. The CrossPlatformSocial spec covers the unified friends list, cross-platform party/lobby, and the central-server-routed voice (native voice disabled, moderation enabled) the platform TRCs require.

Cross-progression roots every player in one identity. @oshun/identity is the single account root; per-platform shadow accounts (PSN, Xbox Live, Nintendo Account, Steam) bind to it. The real TS composition is oshun-identity-binding over the shared @oshun/identity contract, mirrored in-engine as FV2OshunIdentityAccountBinding, with re-binding gated behind a 30-day cool-off and a TOTP step. The subsystem models the unified identity service (bUnifiedPlayerIdentityServiceValid, internal-id-keyed so display names stay distinct from platform usernames), cross-platform progression sync (CrossPlatformProgressionLinkedPlatformCount), cross-platform ban synchronisation (bCrossPlatformBanSynchronizationSystemValid — an Oshun-side ban applies everywhere, a platform-side ban stays on that platform), and the account-data privacy plane (bAccountDataPrivacyComplianceSystemValid, with DSAR export and deletion propagated to every linked platform). Shared state spans profile, the Crowns currency ledger (with per-platform IAP credit kept separate for store-compliance reasons), cosmetic inventory, fighter unlocks, ranked rank, Battle Pass progress, and the single-player career saves; conflict resolution is last-write-wins for non-cosmetic state and union-of-unlocks for cosmetic and fighter unlocks. Platform-only entitlements — a disc-bundle cosmetic that cert forbids transferring — are verified through PlatformEntitlementVerification and the real cross-product-entitlement service, and region-locked cosmetics are region-stamped so a cross-region account sees them locked rather than seeing the wrong variant. The identity root is the platform's, not V2's — which is exactly why a ban or a deletion request resolves consistently across every surface a player touches.

Where this connects#