Fighting Game · Features

Ranked, Esports Circuit & Spectator Modes

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

6sections12 minread1diagram

On this page
stateDiagram-v2 [*] --> Unranked Unranked --> Provisional: eligible ranked placement begins Provisional --> Ranked: placement evidence completes Ranked --> Promoted: rating crosses protected threshold Ranked --> Demoted: rating and hysteresis cross lower threshold Promoted --> Ranked: visible rank updates Demoted --> Ranked: visible rank updates Ranked --> Decaying: inactivity policy applies Decaying --> Ranked: eligible activity resumes Ranked --> Frozen: tournament edition or integrity hold Frozen --> Ranked: governed resolution Ranked --> SeasonClosed: immutable season snapshot

Hidden rating, visible rank, promotion/demotion hysteresis, inactivity decay, tournament freezes, disputes, and season closure are separate. Broadcast and spectator views project this ledger without owning it.

A fighting game's competitive surface is judged by two things players can feel but rarely see: whether the ladder they climb is honest, and whether the broadcast they watch is produced. V2 treats both as first-class engine data, not marketing copy. This page covers the three clusters that turn a deterministic 1v1 into a season-long sport — the ranked season architecture (90-day seasons, eight visible rank families over a hidden TrueSkill2/Glicko-2 pool, hysteresis, decay, and anti-throw integrity), the esports circuit (the TO admin console, the Tournament-Edition rule freeze, the Crown Pro Tour points overlay, and the public result archive), and the streamer / speedrun / coach / spectator modes that wrap a match for an audience. The unusual thing about V2's implementation is how much of this is real, validated Unreal C++ in the V2OnlineServices module rather than a backend stub: the season catalogue, the ten-part esports feature set, and their cross-checking validators all ship and are pinned by contract automation specs. Where a piece is a designed contract or a provider-gated capability, this page says so in code. The section hub is ../V2_features.md.

What ships, honestly#

The line between "shipped" and "designed" runs cleanly through this cluster, and it is worth stating up front.

  • The ranked season is a validated C++ catalogue. FV2RankedSeasonArchitectureCatalog (V2OnlineServicesTypes.h:12494) is a real USTRUCT carrying every season number — length, placements, tiers, regions, rewards, and four sub-policies (MMR, decay, Crown overlay, anti-throw). It is built by BuildDefaultRankedSeasonArchitectureCatalog() (V2OnlineServicesBlueprintLibrary.cpp:10419), gated by a cross-checking validator (IsValidCatalog, HasAllVisibleRankTiers), wired into UV2OnlineServicesSubsystem::ConfigureRankedSeasonArchitecture() (V2OnlineServicesSubsystem.cpp:622), and exercised end-to-end by the spec V2.Online.RankedSeasonArchitecture.AssetContract (V2/ue/Source/V2Tests/Private/Online/RankedSeasonArchitecture.spec.cpp).
  • The esports/streamer/coach/speedrun surface is one validated feature set. FV2EsportsTournamentFeatureSet (V2OnlineServicesTypes.h:7913) bundles ten sub-configs — spectator policy, TO admin, broadcast control, Tournament-Edition snapshot, Crown ranking, streamer privacy, coach session, speedrun support, caster API, and pro-ghost tutoring — each with its own IsValid… method, and an IsCompleteFeatureSet() that requires all ten. The module spec (V2Tests/Automation/OnlineServices.Module.spec.cpp:2267) builds it, validates every part, and configures it through the subsystem.
  • The ranked *tuning numbers live in C++, not in V2/balance/online. That directory currently holds only world-boss-community-raid.json; the ranked season's curves are the build-default catalogue above. The honest read is that ranked tuning is a code-default today, not yet a hot-editable JSON.
  • The broadcast/result operations are real TypeScript surfaces. apps/v2/web/esports/results is a static result archive and apps/v2/esports-tools (@v2/esports-tools) is the operator broadcast pipeline — both shipped, both explicitly off the deterministic path.
  • What is designed, not shipped: the kernel anti-cheat client (V2/ue/Plugins/V2AntiCheat/) and the libs/proto/v2/online gRPC contract are absent (see ../architecture/online-backbone-and-competitive-integrity.md), and the AI-produced broadcast (commentary, lip-sync, translation) is a provider-gated capability the orchestration calls, not a model V2 trains (see ../architecture/esports-companion-and-ai-services.md).

Ranked season architecture (§75)#

One catalogue, four policies, one validator#

The whole season hangs off FV2RankedSeasonArchitectureCatalog. Its scalar frame is concrete and asserted: SeasonLengthDays = 90, PlacementMatchesAtSeasonStart = 10, OffSeasonTournamentCycleDays = 14, plus the booleans that encode the design's promises — bPerRegionSeasonOverlapEnabled, bOffSeasonTournamentCycleUsesSandboxedMMR, bVisibleRanksUseHysteresis, bPlacementUsesPriorSeasonMMRSeed, and bLegendIsTop100Global. Hanging off it are four arrays/policies — the visible tiers, the region seasons, the season rewards, and the MMRPolicy, DecayPolicy, CrownOverlay, and AntiThrowIntegrity sub-structs. The spec asserts the negative cases as well as the positive: it constructs the default, checks IsValidCatalog, then drives the subsystem and reads the runtime snapshot back, demanding RankedSeasonVisibleTierCount == 18 and RankedSeasonRegionCount == 5. That round-trip — build → configure → snapshot — is what makes this a checked spec rather than decorative data.

Visible tiers over a hidden rating#

The visible ladder is eight familiesEV2RankedVisibleTierFamily { Bronze, Silver, Gold, Platinum, Diamond, Master, Grandmaster, Legend } (V2OnlineServicesTypes.h:128) — expressed as 18 FV2RankedVisibleTierSpec entries (:12203). The default builder lays them out as Bronze/Silver/Gold/Platinum/Diamond each in three divisions (a MaxDivision = 3 family) climbing on MinimumHiddenMMR from 0 up through 2250, then the three apex singletons: Master (2400), Grandmaster (2600), and Legend (2850, the only tier with bTop100Global = true). HasAllVisibleRankTiers() (V2OnlineServicesTypes.cpp:12329) is not a length check — it iterates the five divisional families requiring each of divisions 1-3 at MaxDivision == 3, then requires a distinct Legend tier, so dropping Gold 2 or collapsing Diamond's divisions fails validation outright. Every tier carries PromotionHysteresisMatches = 3 and DemotionHysteresisMatches = 3 with bRequiresMinimumRankChangeMatches, the field that implements the source's "hysteresis prevents rapid promotion/demotion oscillation," and a bPublicRankIconEligible flag the provisional system reads.

The rating that actually drives matchmaking is hidden. FV2RankedSeasonMMRPolicy (:12308) pairs a PrimarySkillModel = TrueSkill2 with a SecondarySkillModel = Glicko2 (the same rating family the platform's @oshun services use), keeps both behind hidden profile ids with bHiddenMMRNeverShown = true, seeds a new season from the prior one at PriorSeasonMMRSeedCarryoverPercent = 65, and unifies the pool across { PC, PlayStation, Xbox, Switch } with bUnifiedCrossPlatformMMRPool, bCrossPlatformLeaderboard, and bPlatformIconLabels. SupportsLaunchPlatforms() verifies all four are present — the spec calls this "cross-platform unified MMR covers PC PS5 XSX Switch2." bSmurfDetectionBlocksLowRankFarming is the hook that keeps a sharp new account out of Bronze.

Placement, reset, decay, provisional#

The per-ladder runtime lives in FV2RankedLadderConfig (:11900) and FV2RankedPlayerState (:11946): PlacementMatchCount, StartingRating = 1000, SeasonalResetFloorRating = 900, SeasonalResetCarryoverPercent = 50, and a TierThresholds array resolved by ResolveTierId(Rating). The seasonal reset is a real deterministic algorithm, not a flag — UV2OnlineServicesBlueprintLibrary::ApplyRankedSeasonReset() computes the rating offset from the ladder's starting rating, scales it by the carryover percent, floors the result at SeasonalResetFloorRating, re-resolves the visible tier, and re-arms the placement requirement:

cpp
const int32 RatingOffset = CurrentState.SkillRating - LadderConfig.StartingRating;
const int32 ResetRating  = LadderConfig.StartingRating
    + (RatingOffset * LadderConfig.SeasonalResetCarryoverPercent) / 100;
OutResetState.SkillRating = FMath::Max(LadderConfig.SeasonalResetFloorRating, ResetRating);
OutResetState.TierId      = LadderConfig.ResolveTierId(OutResetState.SkillRating);

FV2RankedSeasonDecayPolicy (:12356) implements the rest of the lifecycle: decay applies only to { Grandmaster, Legend } after MasterPlusInactivityDays = 14 (bDecayAppliesAboveMaster), reactivation costs one placement match, ProvisionalMatchesRequired = 25 keeps the public rank icon hidden for new accounts (bRankIconHiddenDuringProvisional), and WeeklyRetentionBonusPoints = 15 rewards consistent weekly play. Each is asserted by the spec, which demands the provisional gate be exactly 25 and the retention bonus be non-zero.

Anti-throw integrity#

The competitive-integrity layer specific to ranked behaviour (as distinct from the netcode anti-cheat covered in the online-services sibling page) is FV2RankedSeasonAntiThrowIntegrityConfig (:12444). It tracks loss streaks against opponents more than LowerMMRDeltaThreshold = 250 rating below you, flags an AnomalousLossStreakThreshold = 3, queue-penalizes chronic decliners above a ChronicDeclineRatePercent = 35 for QueuePenaltyMinutes = 15, and detects collusion when the same opponent recurs past CollusionRepeatOpponentThreshold = 2. The booleans — bSandbaggingThrowDetection, bAutoDerankConfirmedThrowing, bMatchAcceptanceDeclineRateTracked, bCoordinatedThrowDetection, bUsesFairPlaySignals — encode the source's "sandbagging / throwing detection, match acceptance/decline rate tracked, coordinated-throw detection for tournament play." Critically, bUsesFairPlaySignals means this consumes the same fair-play event stream the ML classifiers do, and — matching the platform's review-only posture — confirmed throwing de-ranks rather than silently banning; discipline routes out to the shared trust-and-safety plane.

The esports circuit (§55)#

The TO admin console and the rule freeze#

A tournament organizer drives V2 through FV2TournamentAdminEventConfig (:7433): bracket, seed-manifest, schedule, broadcast run-of-show, and auto-result webhook ids, a SupportedBracketFormats list over EV2TournamentBracketFormat { SingleElimination, DoubleElimination, RoundRobin, Swiss, Gsl }, a NoShowTimeoutSeconds = 300, a MaxRoundExtensionSeconds = 600, and the TO-control booleans (bManualSeedingEnabled, bDisqualificationEnabled, bRoundExtensionOverrideEnabled, bAutoResultReportingEnabled). The single most important integrity feature is the rule freeze: FV2TournamentEditionRuleSnapshot (:7557) captures a RulesHash, a ReplayHeaderId, the ImportedRulesFileName, and the locked-down competitive toggles — bClassicControlsOnly, bDefaultStagesOnly, bDefaultCostumesOnly, bDisablesMercyFriendshipBabality, bForcedP1LeftP2Right, bFinisherPolicyLocked — and stamps them with bSavedInReplayHeader = true. This is the determinism dividend the architecture leans on everywhere: because the match runs on an integer-frame simulator, freezing the TO's ruleset into the replay header makes a recorded set reproducible against the exact conditions it was played under. The cryptographic pull-and-verify of third-party results (Start.gg / Challonge / Battlefy) against that replay hash is the designed server-authoritative contract — the replay-hash determinism that backs it is the part that is architecturally real today.

Crown Pro Tour points#

The season-long competitive ranking is the Crown overlay. FV2RankedSeasonCrownOverlayConfig (:12394) wraps a FV2CrownProTourRankingConfig (:7628) and layers points from four sources — EV2RankedSeasonPointSource { Ranked, Tournament, Invitational, EvoBadge } — weighted RankedPointWeight = 1, TournamentPointWeight = 3, InvitationalPointWeight = 5, EvoBadgePointWeight = 2, so a deep run at a major outscores a month of laddering. The ranking config sets a QualificationCutoff = 32, awards points from both official and partnered tournaments, surfaces an in-game Crown leaderboard widget and a profile badge, and exposes a read-only third-party API. bYearEndGrandFinalInvitesTopCrownPlayers closes the loop: the season's top Crown players are invited to the year-end Grand Final. The broader per-region major/regional calendar the source describes (NA/EU/JP/KR/LATAM/SEA…, qualifier → regional final → world final) is layered on top of this points engine; the racing modes carry their own parallel FV2RacingEsportsBroadcastCatalog (:26074) with explicit per-region series, validated separately in the module spec.

The public result archive#

apps/v2/web/esports/results is the shipped, static face of the circuit (§55.8). Its results.json carries a v2.esports.resultsArchive.v1 schema with per-event, per-match, per-player, per-fighter records and rankingWeights (championship 120, major 90, regional 55, local 25, plus an 8-point win bonus and a 2-point replay bonus); results.js filters by fighter, player, date, or event and computes the Crown-linked community power ranking. Its README is an integrity statement as much as a description: the archive exposes only public competitive results — never raw account identifiers, contact information, private moderation notes, or unpublished replay links.

Streamer, speedrun, coach & spectator modes (§55)#

These four player-facing modes are the remaining sub-configs of the one validated FV2EsportsTournamentFeatureSet, each with a real cross-checking validator.

Spectator & broadcast#

FV2EsportsSpectatorPolicy (:7383) caps in-match observers at MaxInMatchObservers = 8 (clamped), throttles spectator chat to ChatMessagesPerMinute = 6, sets an AntiLeakDelaySeconds = 120 anti-spoiler buffer, and enables the production toolkit — director-cam, scriptable cuts, picture-in-picture, the side-by-side stick cam, drawing tools, freeze-frame, slow-motion, name plates, and the score overlay. The caster's surface is FV2BroadcastControlSurface (:7499): a manual control surface with configurable SupportedDelaySeconds (0/30/60/120), output integrations over EV2BroadcastOutputIntegration { Obs, Ndi, Spout, BrowserSource, JsonWebSocket }, replay-marker clip-cut, a post-match recap reel, and a documented public SDK at apps/v2/web/dev-portal/caster-api/schema.json. Third-party overlay developers consume FV2CasterOverlayApiContract (:7828) — a documented WebSocket route (/v2/caster-api/ws) and REST route, gated by bPerEventApiTokenRequired so only a verified TO's per-event token can read live match state. The operator side of this — the actual program/clean-feed switcher, multiviewer, and stream-health dashboard — is the shipped @v2/esports-tools package (apps/v2/esports-tools/esports-broadcast-pipeline.ts), which composes @uzume/broadcast and tags itself off-rollback-broadcast-operations; its OBS WebSocket use is scoped to "streamer-mode-notification-suppression-only," and result-reporting journalism runs through @veritas/fact-checking. None of it can perturb the sim.

Streamer mode & stream-safe music#

FV2StreamerModePrivacyProfile (:7672) is the broadest of the validators. IsValidProfile (V2OnlineServicesTypes.cpp) refuses the profile unless the anti-stream-snipe match-start delay is configurable inside 1-5 minutes (MinAntiStreamSnipeDelaySeconds = 60, Max = 300, with Min <= Max) and every privacy flag is set: hide handles and region, show the opponent as "Opponent," default voice chat off, blur non-friend avatars/emotes/lobby chat, suppress capture-aware notifications, auto-activate stream-safe music, and persist the pause/main-menu quick toggles. A second clause requires the streamer toolkit — the overlay generator, Twitch/YouTube/Kick channel binding, OBS browser-source widgets, subscriber-tier cosmetic gift triggers, and stream-aware notification suppression. The "stream-safe music automatically swaps licensed tracks for cleared analogues" promise is a different module: FV2StreamerSafeMusicCatalog (V2Audio/Public/V2AudioTypes.h:6205) carries per-track license metadata (bPerTrackLicenseMetadataRequired), a DMCA-safe toggle, and a ResolveTrack() that returns a cleared replacement for a restricted track, with bAutoApplyDefaultPresetInStreamerMode wiring it to the privacy profile's bAutoActivatesStreamSafeMusic. It is validated by its own FV2StreamerSafeMusicSpec.

Coach mode & async coaching#

FV2CoachSessionConfig (:7746) models both halves of the source's coaching story. The live half binds a PlayerAccountId and CoachAccountId into a training lobby (bTrainingOnlineLobby) where the coach gets a separate camera (bCoachSeparateCamera), can record and play back dummy inputs (bRecordPlaybackDummyInputs), annotate moments (bMomentAnnotations), and push notes to the player (bPushNotesToPlayer). The async half is bAsyncReplayAnnotations with bPlayerNotifiedForAnnotatedReplay: a coach attaches annotations to a submitted replay and the player is notified to watch it back. The deterministic replay pipeline this rides on is documented in the mode catalogue & replay page.

Speedrun support#

FV2SpeedrunSupportConfig (:7790) ships the built-in run timer (bBuiltInRunTimer) with per-mode split definitions (SplitDefinitionSetId = "Speedrun.Splits.LaunchModes"), a pace tracker against personal best, and two integration seams: a local LiveSplit bridge on PC (bLiveSplitLocalWebSocketPc, LiveSplitLocalWebSocketPort = 16834) and a read-only ingest from the speedrun.com partner program (SpeedrunPartnerProgramId = "speedrun.com.partner.readonly"). The read-only framing is deliberate — V2 displays community speedrun standings without letting an external surface write into the competitive record. (The Speedrun Race multiplayer stipulation — racing the Arcade ladder side-by-side — is a separate mode-catalogue spec, FV2SpeedrunRaceModeSpec, covered in signature events & standalone modes.)

How it connects#

Ranked seasons and the esports feature set are both V2OnlineServices C++ catalogues validated by contract specs, and they share a spine: the deterministic, integer-frame match. That determinism is what lets the Tournament-Edition snapshot freeze a ruleset into a replay header, lets the anti-throw layer and the server validators re-derive a result from inputs, and lets the AI-produced broadcast sit entirely off the simulation path. The numbers that tune the ladder are a C++ build-default today, not yet hot-editable JSON; the kernel anti-cheat client and the gRPC wire contract are designed, not present; and the generative half of the broadcast is a provider-gated capability the orchestration calls. Everything player-facing here — the eight rank families, the ten-part esports feature set, the streamer privacy profile, the public result archive — is shipped and pinned by a spec that would fail on a stub. The section hub is ../V2_features.md.