Tactical Action · Features

Online Services, Networking & Esports

A focused page within the Tactical Action Features documentation. The full map and every sibling page live in the Features hub.

5sections13 minread1diagram

On this page

V4 is one tactical-action universe wearing six bodies, and what makes that a universe rather than six unrelated games is the online backbone every cell shares. A ranked Search & Destroy round, a 1v1 RTS ladder match, a Spies-vs-Mercs hunt, and a boss-rush leaderboard run all sign in through the same account, queue through the same skill-rated matchmaker, land on the same dedicated-server fleet, store the same server-validated replay, and — when an event spins up — feed the same in-client tournament stack. This page is the player-facing tour of three layers that sit next to each other: the online services plumbing (login, friends, parties, matchmaking, leaderboards, replays), the networking that decides how a given match is transported (client-server with lag-compensated hit registration, deterministic lockstep for RTS, rollback-emulated prediction for twitch PvP, plus the server fleet, LAN, and private rooms underneath them), and the esports toolkit that lets an organizer run a bracket, broadcast, and verify results without leaving the game. The store, season pass, and progression vault that ride this same backbone live in Live Service, Progression & Vault; the workshop, contract author, and community surfaces are in Content, Creator & Community; the engine-side treatment is the architecture companion, ../architecture/online-services-persistence.md. For the full feature scope this slots into, start at the hub: ../V4_features.md.

What ships, honestly#

The split is clean and worth stating up front. Two tiers are real code, and a small set of seams are labelled as deployment substrate rather than wired integrations.

The client tier is real Unreal C++. V4/ue/Source/V4OnlineServices ships fourteen UGameInstanceSubsystems — login, matchmaking, session, presence, friends, party, leaderboard, replay-upload, crossplay, live-service, moderation, compliance, online-contract, and contract-schedule — over a typed surface (V4OnlineTypes.h), and V4OnlineServices.Build.cs really declares the OnlineSubsystem, OnlineServicesInterface, OnlineServicesCommon, and EOSShared dependencies that make the EOS abstraction load-bearing rather than decorative. The networking module V4/ue/Source/V4Netcode is wired just as seriously: its V4Netcode.Build.cs pulls NetworkPrediction, Iris, ReplicationGraph, and OnlineSubsystem, and the implementations behind them are real algorithms, not stubs.

The service tier is real Rust + Axum. apps/v4/online-services is a v4-online-services crate whose service-manifest.json enumerates twenty-six services, and whose service_router() (src/lib.rs:24-47) nests twenty of them — login, matchmaking, sessions, replays, leaderboards, anti-cheat, moderation, compliance, store, esports, and the rest — each backed by a real handler in src/http.rs (the esports router alone is at http.rs:1366). The domain logic is not CRUD: a backtracking exact-fill matchmaker, a dependency-free SHA-256/PKCE OAuth implementation, and real Challonge/Start.gg bracket clients, each carrying inline #[cfg(test)] cases that assert computed answers.

Four honest qualifications travel with the rest of the page:

  • Services hold in-memory state. Each Rust service keeps a BTreeMap behind an Arc<Mutex<…>>. The PostgreSQL / Redis / ClickHouse / S3 stores the architecture names are the specified deployment substrate, not yet wired into these crates. The reference implementations are real and tested; the database backends are the next integration step.
  • Kernel anti-cheat is a plan; replay validation is real today. The UV4AntiCheatSubsystem names EasyAntiCheat as the client driver tier and validates its config, but that kernel driver is a planned surface. The load-bearing integrity layer that ships is server-side replay validation, which rides the deterministic sim the engine already guarantees.
  • V4 does not ride the @oshun plane. Unlike V2's event-bus-native backbone, V4's services are a self-contained Rust workspace (v4_shared::ServiceName); a grep for @oshun across apps/v4 and V4/ue source returns nothing. Where V2 publishes cross-domain events, V4 keeps its own service graph.
  • Bracket egress is proxied. The Challonge and Start.gg clients in src/brackets.rs speak the providers' real wire formats, but egress runs through the platform's configured TLS-terminating proxy (host:port), not a direct outbound socket.

The online backbone, from the player's seat#

Login, accounts, and sessions#

Gameplay code never branches on platform. Every menu and lobby calls a subsystem whose types speak one enum — EV4OnlinePlatform { EOS, PSN, XBL, NN, Steam, Apple, Google } (V4OnlineTypes.h:7) — and the platform stacks sit behind it. UV4LoginSubsystem is a clean client-side state machine over EV4LoginState { SignedOut, Pending, SignedIn, Failed }: BeginLogin moves an account to Pending, CompleteLogin resolves it to SignedIn or Failed, and the first- party V4 account is the primary identity. The heavy cryptography deliberately does not live in the client — it lives in the Rust login-service, where src/oauth.rs carries a dependency-free, FIPS 180-4 SHA-256 used for the RFC 7636 S256 PKCE code-challenge binding, so the RFC 6749 authorization-code exchange is genuinely cryptographic and deterministically testable rather than a synthesised token. Refresh is single-use with replay defense: a presented token is validated against its expected derivation, inserted into a consumed-token set, and a replay is rejected before the generation rotates. Account linking binds any of the seven providers to one first-party account; unlinking never deletes progression. If the login service is unreachable, the client falls back to offline play and reconciles to the cloud on the next successful sign-in.

Friends, parties, and voice#

The friends-service serves one cross-platform friend graph over a WebSocket presence channel, so a friend's current cell, mode, and in-match/in-lobby state are live. Party caps are per-cell — 4 for tactical PvP, 8 for RTS team modes, 2 for Wukong co-op, 4 for general co-op — and members who cannot meet a mode's platform or content requirement are flagged before queue rather than after. Voice runs over a WebRTC SFU (voice-sfu in the manifest) with positional or channel audio, push-to-talk or open-mic per player, and per-channel mute of any other player; a server-side whisper-transcription classifier feeds the report pipeline, and voice defaults off for under-18 accounts pending parental opt-in.

Matchmaking, skill, and region#

Matchmaking is the backbone's most fairness-sensitive job, and V4 implements it on both sides of the wire. The authoritative skill model is server-side and genuine: MatchmakingService (src/matchmaking.rs) runs a Glicko-2 expected-score fairness window. A candidate joins the match only when its expected score against the seed stays within FAIR_MATCH_EXPECTED_SCORE_WINDOW = 0.15 of an even 0.5, and find_match is a real depth-first backtracking exact-fill (fill_exact): it seeds on the longest-waiting solo (prefer_solo_queue, FIFO within party size), recurses to assemble exactly team_size players from mixed party sizes, requires one region every member can play in, then picks the region with the best worst-case latency. The tests prove behaviour, not shape: a 2400-rated smurf is excluded from a 1500 lobby and stays queued; a 3+2 backtrack fills a 5-stack while a lone solo waits; and SA wins over EU because its shared worst-case QoS is 45 ms versus 80. On the client, UV4MatchmakingSubsystem carries FV4MatchmakingTicket rows and a FV4MatchmakingDiagnostic that powers an explanation panel — PlayerMMR, OpponentAverageMMR, MMRDelta, bWithinTolerance — so a player can see why a match formed. The launch SLO is concrete: matchmaking p99 ≤ 35 s under 5× expected launch concurrency.

Leaderboards and the replay vault#

UV4LeaderboardSubsystem keeps FV4LeaderboardEntry rows under a BoardId and serves GetTopEntries/GetEntry; the service slices boards per mode, per cell, and per region with a global view, resets competitive boards on the 90-day season boundary, and archives the prior season. Integrity is server-authoritative: ladder and signature-mode entries (boss rush, contracts, Contra Survival) are validated by replay analysis before they post — runs that fail validation are rejected, not silently dropped. Replays default to a 14-day retention; starring moves one into a lifetime per-account vault capped at 500. Verified speedrun submissions feed a ghost archive where a faster ghost supersedes the active route ghost while preserving lineage, and a right-to-be-forgotten scrub anonymises a deleted account's pawns in preserved replays rather than erasing the match.

Networking: three netcodes on one server fleet#

V4's promise is per-ruleset feel, and that extends to the wire: a slow RTS macro game and a 60 Hz duel cannot share one transport. The product makes dedicated-server client-server the default, with deterministic lockstep for RTS and rollback-emulated prediction for tight twitch PvP — three models that all allocate off one fleet.

flowchart TD subgraph Client["V4 client · Unreal C++"] OSS["V4OnlineServices<br/>14 subsystems"] NET["V4Netcode<br/>3 transport models"] end OSS -->|OnlineSubsystem · EOSShared| EOS[(EOS · PSN · XBL · NN<br/>Steam · Apple · Google)] OSS -->|REST| GW[API gateway] GW --> RUST["v4-online-services · Rust + Axum<br/>service_router() · 20 routers"] RUST --> MM[matchmaking · Glicko-2] RUST --> ESP[esports · brackets · anti-collusion] MM -->|allocate| ROUTER["UV4DedicatedServerRouter<br/>10 regions"] ROUTER --> CS["Client-server<br/>+ lag-comp rewind"] ROUTER --> LS["Lockstep · 25 Hz<br/>desync recovery"] ROUTER --> RB["Rollback<br/>8 frames @ 60 Hz"] ESP -->|Challonge REST · Start.gg GraphQL| EXT[(External brackets)] RUST -.specified store.-> DB[(PostgreSQL · Redis<br/>ClickHouse · S3)]

The server fleet and allocation#

UV4DedicatedServerRouter (V4ClientServerNetcode.cpp) configures ten regions — NA-East, NA-West, EU-West, EU-East, LATAM, APAC, AU-NZ, ME, Africa, India — each with a provider code, endpoint host, capacity, and estimated ping. AllocateServerForMatchmaking honours a preferred region when it is enabled and under capacity, otherwise scans for the lowest-ping enabled region with free slots, increments its active allocations, and mints a QueueId.Region.MatchId server id; ReleaseAllocation decrements it back. The same router also triages anti-cheat: RouteAntiCheatSignal kicks-and-escalates a severity ≥ 8 signal to anti-cheat.eac-review, escalates severity ≥ 4 to anti-cheat.live-review, and otherwise logs to anti-cheat.telemetry.

Client-server with lag-compensated hit registration (default)#

The default model is server-authoritative with a real rewind. A shot is an FV4HitRegistrationRequest carrying the client's fire time, trace, a Line or Cone shape, and a stack of FV4PenetrationLayers; the server rewinds targets to buffered FV4LagCompensatedSamples (server time, location, box extent), clamps the rewind, and returns an FV4HitRegistrationResult whose booleans are honest — bHit, bRewindClamped, bRejectedByAntiCheat, bBlockedByPenetration, plus the rewound time/location and remaining penetration energy. Hit acceptance is the server's call, not the client's claim, and a rejected-by-anti-cheat flag is a first-class output rather than an afterthought.

Deterministic lockstep (RTS)#

RTS modes (1v1, 2v2, 4v4) run UV4RTSLockstepSubsystem at a 25 Hz tick. Clients exchange only FV4LockstepInputBundles (frame, match-config hash, RNG seed, per-player inputs); each peer advances the same ComputeDeterministicHash over a sorted input bundle so the simulation stays bit-identical. Desync is detected, not hoped against: CheckRemoteFrameHash compares the local frame's StateHash to a peer's and, on mismatch, computes a RecoveryFrame; RecoverFromDesync rolls the sim back to it and adopts the authoritative hash. The subsystem also draws match randomness through DrawMatchRandomInt and exposes ValidateRngConsumptionOrder, so a replay can prove the RNG was consumed in the same order on every machine — the foundation that makes RTS replays true-rewindable and lockstep matches auditable. Late joiners rebuild from BuildLateJoinerRebuildFrame, and a pause threshold backs the design's pause-on-disconnect behaviour.

Rollback-emulated prediction (twitch PvP)#

Tight shooter PvP (TDM, Spies-vs-Mercs) runs UV4RollbackEmulatedComponent. Its profile predicts movement and firing up to MaxPredictionFrames = 8 at PredictionFrameRateHz = 60 — roughly a 133 ms prediction budget — SimulatePredictedInputFrame advancing the local model and ReconcileAuthoritativeInputFrame correcting it against an FV4RollbackAuthoritativeFrame: when the predicted location is inside the server's ErrorTolerance no correction applies, otherwise the component replays the intervening frames and reports the delta. Fire is server-authoritative (bRequiresServerFireAuthority), so a rejected shot surfaces as bServerRejectedFire rather than a phantom kill.

Bandwidth budgets, Iris migration, and host migration#

Each match type gets a tuned replication profile. UV4ReplicationGraphProfileCatalog ships per-mode band budgets and graph nodes for Tactical5v5, CoD6v6, BattleRoyale (four distance bands from an 8192-byte critical tier to a 1024-byte far-dormant tier), and SpiesVsMercs, loaded into the budgeter. UV4IrisMigrationValidator keeps rollback-sensitive movement channels on legacy replication while migrating inventory, objective, cosmetic, and social-stealth channels to Iris, and BuildMigrationReport computes the bandwidth-reduction fraction. For peer-hosted private rooms, UV4HostMigrationCoordinator::BuildMigrationPlan picks the best-scoring eligible peer and ValidateSubTwoSecondSwap confirms the estimated swap stays under two seconds; matchmade dedicated-server play never migrates host.

LAN, server browser, private rooms, and QoS#

For offline events V4 ships a LAN lobby for RTS and tactical modes that runs with no backend — local-network discovery, peer-hosted, no rating recorded. The server browser lists community-hosted RTS, R6-style, and private rooms, filterable by cell, mode, map, ping, player count, and mod-whitelist state, honouring the room's password and content rules on join. Private rooms are friends-only scrim/bracket spaces whose host configures cell, mode, map pool, rules, and team assignment, and can snapshot state so a multi-game series pauses and resumes. The client probes its NAT type at startup and warns on strict/symmetric NAT (which degrades private-room peering; dedicated-server modes are unaffected), and pings all ten regions to cache a sorted latency list that matchmaking and the browser surface as the best three.

Esports: run the whole event from the client#

V4's tournament stack is real service code with a strict completeness discipline, backed by EsportsService (src/lib.rs) and the bracket clients in src/brackets.rs.

The tournament server build and integrity#

configure_tournament_build refuses any config that is not fully competitive: the build flag must be V4Tournament and all of broadcast tools, observer cameras, pause-on-disconnect, and forced replay-export must be on, or it returns InvalidTournamentBuild. There is no half-configured tournament build. Sponsor branding is data, not code: register_branding_hooks requires at least two crews, enough faction icons, and a broadcast-approved banner card per crew, so the esports-service populates crew names, team icons, and banner cards per event rather than hard-coding them. validate_tournament_mods enforces a whitelist — when reject_unlisted_mods is set, any loaded mod outside allowed_mod_ids is rejected so every match runs on a known content set.

Bracket sync — real provider wire formats#

The bracket clients speak the providers' real APIs. ChallongeClient::fetch_matches issues GET /v1/tournaments/{id}/matches.json?api_key=… and parses Challonge's {"match": {...}} envelopes; StartggClient::fetch_event_sets POSTs a real GraphQL EventSets query to /gql/alpha with a bearer token and maps the sets.nodes back to a common BracketMatch. Both are tested against a spawned local HTTP server that asserts the exact request line, headers, and GraphQL variables. Internally, create_first_party_bracket requires server-authoritative reporting, and the design pulls and cryptographically verifies external results against the replay hash before they count — results a client cannot forge.

Anti-collusion and anti-cheat#

Collusion detection is tuned for the modes where it pays. evaluate_rts_team_collusion scores shared-vision seconds, resource-transfer spikes, and mirror-pathing events and flags a pairing only past an evidence-window-and-score threshold; evaluate_search_and_destroy_collusion scores plant/defuse avoidance and thrown rounds the same way — each emits an AntiCollusionFinding with a severity, a review queue, and an FNV-style evidence hash, and flagged matches route to human review rather than auto-discipline. On the client, UV4AntiCheatSubsystem backs this: EvaluateBehavior flags impossible recoil (≥ 20 shots at ≥ 0.98 control) and wallhack patterns, CalculateTrustScore is 100 − strikes·25 − investigations·10, BuildBanTiers enumerates the mute/match/cell/account ladder, and ValidateLadderReplay insists the replay, server-state, and input hashes are all present and independently produced before a run is valid. The EAC config validation requires kernel mode plus ranked-and-BR protection across PC and current-gen consoles — but, per the honest label above, that driver is planned; the replay-hash check is the part that ships real.

Pro circuit, broadcast, and the Hall of Fame#

The first 12-month calendar is authored data, not prose: V4/esports/pro-circuit/pro-circuit-2026.json defines Major / Minor / Open tiers with circuit points (1200 / 450 / 120), prize pools, and a twelve-event calendar from a Launch Open in October 2026 to a World Finals Major in September 2027, plus a broadcast SLA of 8 / 4 / 2 observers by tier, a 365-day replay archive, and a 90-second integrity delay; esports/calendar/first-12-months.json mirrors the month-by-month cadence so Open results feed Minor seeding and Minor feeds Major. The web spectator portal adds a per-camera picture-in-picture grid, a director-cut observer, true replay-rewind on lockstep matches, and the same 90-second delay. Finally, EsportsService enshrines lifetime champions: enshrine_lifetime_champion_banner validates a one-of-one art commission anchored to Lobby.BattleHub.Zone.HallOfFame, hall_of_fame_induction_criteria encodes per-cell evidence requirements (two Major wins for Tactical FPS, a Grandmaster season plus two Major top-4s for RTS, a world-first boss kill for ARPG), and a community-veto policy (/hall-of-fame/community-veto, 30-day window, one-account-one-vote) keeps the honour accountable to the players.

Where this connects#

  • Sideways to live service: Live Service, Progression & Vault — the store, battle pass, currency ledger, and replay vault that ride this same backbone and account root.
  • Sideways to creators: Content, Creator & Community — the workshop mods the tournament whitelist gates, the Hitman contract author, and the gallery/newsletter community surfaces.
  • Down to the engine: ../architecture/online-services-persistence.md — the EOS abstraction, the Glicko-2/PKCE service internals, the persistence and cross-progression model, and the specified PostgreSQL / Redis / ClickHouse / S3 substrate these services deploy onto.
  • The feature hub: ../V4_features.md