Fighting Game · Architecture

Live-Ops, Store, Progression & Community

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

7sections13 minread1diagram

On this page

A modern fighting/racing game is a service, not a box — it ships and then lives for years through seasons, balance patches, DLC fighters, cosmetic drops, limited-time events, and the community that argues about all of it. This page covers the four systems that keep V2 alive after launch: the live-operations machinery (how content and events reach players, and how a broken event is made good), the store and economy (how cosmetics and DLC are sold transparently, within spend limits, and priced fairly across the world), progression (the battle pass, fighter mastery, account level, and the codex), and the community and support surfaces (governed social spaces, ambassadors, support tickets, and the out-of-game web). The defining choice mirrors the rest of V2: the live-ops spine is a set of real, narrow apps/v2/ TypeScript packages composing the shared Oshun economy, calendar, governance, and content substrates — not a bespoke monolithic live-service backend — fronted in-engine by the real V2Services and V2OnlineServices modules. The section hub is ../V2_ARCHITECTURE.md.

What ships, honestly#

Three layers are real code, and one is honestly a plan.

The live-ops service packages are real TypeScript under apps/v2/ (part of the same ~91-package set the online backbone catalogues): season-pass-service (2.0 K lines), dlc-content-delivery-service, live-event-scheduling-service, live-event-content-delivery-service, patch-notes-service, and player-compensation-service are all fully-implemented pure-function surfaces with validation and tests, alongside the economy packages (economy-analytics-dashboard, economy-balance-tuning-interface, isis-cosmetic-pipeline, aje-web3-cosmetic-ownership, maat-finance-ledger) and the community ones (kuanyin-community-harmony, themis-community-governance, world-boss-community-raid-bridge). The per-platform store and entitlement seam is real Unreal C++: V2OnlineServicesTypes.h carries EV2OnlineServiceCategory::Store/Entitlements, an OpenStore/QueryEntitlements platform-SDK request surface, store-receipt validation (bStoreEntitlementReceiptValidation), and a typed cross-product entitlement claim (FV2CrossProductEntitlementClaim). The progression and codex data-of-truth is real: CSV tables under V2/balance/progression/ and V2/balance/codex/.

What is plan, not artifact, and labelled as such throughout: the Year-1 roadmap (which four fighters, which seasons, which drops) is a schedule the services execute; the gRPC wire contracts the monolith specifies (libs/proto/v2/{live,progression,community}/*.proto) do not exist in the tree (libs/proto/v2 is absent), so those method signatures are the designed contract, not generated stubs; the GameFeaturePlugin delivery mechanism is the documented engine system the download plans target, not a built V2 plugin; and the premium-currency name "Crowns" is a design label — the live currency IDs in service code are fight-coins, season-credits, and a soft earned currency.

Live operations & content delivery#

The season calendar and event scheduling#

The live calendar is not a standalone V2 schema — authority is the shared Maat strategy plane. @v2/maat-live-service-calendar (maat-live-service-calendar.ts:31) wraps buildMaatV2FightingGameLiveOpsCalendar from @maat/strategy and asserts standaloneV2LiveCalendarReplaced / noStandaloneV2CalendarSchema. Maat owns seasons, the Crown Points seasonal pool (the ranking/esports points currency, distinct from spend currency), the Pro Circuit calendar, anniversary events, and charity events. The surface even pins the cross-domain wiring it depends on as concrete arrays: requiredGrpcSurfaces (Rank.GetSeasonState, Rank.GetCrownPoints, Esports.GetBracket, Esports.QualifyForFinals) and requiredEventTopics (v2.rank.season.started, v2.crown-points.granted, v2.live-ops.anniversary.started, …) — designed contracts, named honestly.

Individual events are scheduled by live-event-scheduling-service. A V2LiveEventScheduleDefinition (live-event-scheduling-service.ts:35) carries startsAt/endsAt, an optional earlier visibilityStartsAt (so an event can be announced before it is playable), regions, activationTags, the gameModes it turns on (each with a queueId and rulesetId), and its rewards. The service resolves each into an upcoming / active / ended state and emits per-mode and per-reward activations — the engine flips a queue on by reading state, never by shipping a patch.

DLC and live-event content delivery#

Post-launch content rides Unreal's GameFeaturePlugin system (the documented delivery target): each DLC fighter pack, story expansion, and cosmetic drop is a modular plugin delivered, activated, and — for license-conditional content — hot-removable over the same per-fighter chunk boundaries the asset pipeline defines. The dlc-content-delivery-service makes that delivery entitlement-gated and incremental. V2_DEFAULT_DLC_CONTENT_PACKS (dlc-content-delivery-service.ts:105) covers all three required pack kinds — fighter-pack (the 620 MB "Nyx" fighter), story-expansion (the 1.25 GB "Storm Market" chapter), cosmetic-pack (the 90 MB "Golden Gi") — each with sha256: content hashes, https:// asset URIs, and /Game/V2/DLC/... mount points, all validated. planV2DlcContentDownload (line 221) is the real logic: if the caller's entitlementRefs don't include the pack's entitlementRef it returns lockedReason: 'entitlement-required' with a zero-byte plan; otherwise it diffs installedContentHashes against the pack's assets and returns only the pending assets plus the exact totalDownloadBytes. live-event-content-delivery-service mirrors this for time-boxed event content (map / cosmetic / npc-dialogue bundles) with the same entitlement-and-diff download plan. Both are serverAuthoritative: true, requiresClientPatch: false — content is a download, not a binary update.

Patch notes and player compensation#

Every balance hotfix pairs with a human-readable record. patch-notes-service (patch-notes-service.ts:12) structures each release into three categories — balance, bug-fixes, new-content — and renders a localized in-game payload (buildV2PatchNotesInGamePayload) of display cards grouped by category, carrying locale, publishedAt, and minClientVersion. The data-only hotfix mechanism those notes describe is documented under Security & Compliance.

When something goes wrong — a broken event, a mispriced item, downtime — make-goods are a first-class capability. player-compensation-service (player-compensation-service.ts) models compensation campaigns keyed to an incidentId, covering both required reasons (bug and outage), each with an explicit approval chain (approvals must be non-empty — approvalRequired: true), a grant bundle of currency and items, and a list of affectedPlayers each carrying an evidenceRef (e.g. telemetry:ranked:reconnect:…) and a severity. The two load-bearing properties are evidence and idempotency: buildGrantRecord derives a deterministic grantId of comp:{campaignId}:{accountIdHash} (line 506), and applyV2PlayerCompensationGrant (line 340) refuses to double-pay — if the ledger's processedGrantIds already contains the grant it returns duplicate: true, applied: false and leaves balances untouched, otherwise it merges currency and item grants into the ledger. A player is compensated exactly once per incident, with a paper trail.

The Year-1 shape the services execute: 4 character DLC packs, a mode-expansion DLC, and seasonal cosmetic drops on the paid side; balance patches, new stages, new tower content, new tutorials, and accessibility on the free side — the split that keeps the competitive playerbase unified (everyone gets balance and stages) while monetising cosmetics and roster.

Store, currency & PPP pricing#

Per-platform store, entitlement, and cross-product claims#

The store integration is per-platform-store-native: Steam, PSN, Microsoft Store, Nintendo eShop, and the Epic Games Store each have their own consumable/entitlement/refund/family-share semantics, surfaced through a thin IPlatformStore-style adapter behind the real UV2OnlineServicesSubsystem. In engine, V2OnlineServicesTypes.h carries the typed store surface — EV2OnlineServiceCategory::Store/Entitlements, the OpenStore / QueryEntitlements SDK requests, store-receipt validation (bStoreEntitlementReceiptValidation), platform-specific entitlement queries (PS Plus, Smart Delivery, Game Pass) that fail closed (bPsPlusEntitlementsFailClosed), and a verification surface whose issue types (MissingUserEntitlement, EntitlementNotOwned, EntitlementExpired) make a revoked or refunded purchase a real, handled state. Cross-product grants are typed too: FV2CrossProductEntitlementClaim (issued by, e.g., Lilith, of grant type Cosmetic, defaulting to PrivateInventory visibility) is how a cosmetic earned in a sister product appears in V2 without leaking its source. iOS and Android stores are companion-app subscription only — they never grant game items.

Currency, spend limits, and the make-good economy#

The economy is deliberately player-protective, by construction, not policy. Premium currency buys transparent items — no real-money loot boxes — with daily spend limits, and per-platform IAP credit is kept ledger-separate from earned currency for store-compliance reasons (see the cross-progression model in Online Backbone). The economy is instrumented and tunable through real services: economy-analytics-dashboard composes @maat/finance and classifies currency flow as source-heavy / sink-heavy / balanced over real event topics (v2.economy.currency.earned/spent, v2.cosmetic.purchased, supply/wealth snapshots); economy-balance-tuning-interface composes @v2/remote-config-service to expose drop-rate / price / reward tuning families through slider / currency-input / stepper controls, so rebalancing the economy is a config change, not a redeploy. Cosmetics are purely cosmetic — nothing sold touches the deterministic combat sim, a hard competitive-integrity line.

PPP regional pricing — a real formula, not a table lookup#

Fair global pricing is real, tested math. @v2/maat-finance-ledger (maat-finance-ledger.ts) composes @maat/finance, whose calculateV2MaatFinanceRegionalPriceMinor (libs/maat/finance/src/v2-persistence-ledger.ts:88) computes min(priceCeiling, max(floor, round(baseline × pppRatioBasisPoints / 10_000))) — baseline price scaled by a per-region purchasing-power ratio, clamped to a ceiling (capped at baseline, so no region pays a premium) and a floor (the priceFloorRatioBasisPoints is the arch's ratio × 0.4 = 4000 bps guard against inversion attacks). The same module's buildV2MaatFinanceLedgerDecision is the spend gate: a spend whose unitPriceMinor disagrees with the computed regional price, or whose gross ≠ price × quantity, is rejected region-price-mismatch; a spend over the per-transaction cap or that would push the day's total past the regulatoryDailySpendCapMinor (or the stricter minorDailySpendCapMinor for minor accounts) is rejected regulatory-cap-exceededthis is the "daily spend limit," enforced in code, not prose. Refunds require a refundReferenceId and fall inside refundWindowDays or they are refund-window-expired. And because a console can go offline mid-purchase, the engine keeps an in-cache ledger (offlineLedgerCacheMode: 'v2-engine-ledger-cache-reconcile-on-sync'): an offline-approved spend is marked reconciliationRequired and replayed against the Maat source-of-truth on next sync. The whole ledger is offRollback: true, mayInfluenceRollback: false — money never touches a rollback frame.

Cosmetic pipeline and opt-in on-chain ownership#

Cosmetics originate in isis-cosmetic-pipeline (cosmetic-pipeline.ts), a real four-stage pipeline — model_generation (@isis/3d-generation) → ai_texturing (@isis/ai-texturing) → universal_rigging (@isis/universal-rigging) → bellona_cook_enqueue (@bellona/unreal) — covering ten cosmetic slots (outfit, hair, mask, gear, entrance prop, weapon skin, vehicle skin, decal, palette, avatar accessory) across five platforms, each variant stamped with a V2CosmeticVariantProvenance rights record (first_party / licensed_brand / ugc_creator / live_ops). On-chain ownership is the clearly-bounded, opt-in, fail-closed surface: aje-web3-cosmetic-ownership (aje-web3-cosmetic-ownership.ts:117) only approves a wallet-linked cosmetic when the player explicitly opted in and accepted the exact warning text and the platform-cert policy hasn't banned the surface and has explicitly cleared it and the verifiable credential and ERC-721 metadata validate — every other path returns a typed blocked-* reason. It is off-rollback-opt-in-cosmetic-only; the default is that nothing is exposed.

flowchart LR Cal["maat-live-service-calendar<br/>(seasons · Crown Points)"] --> Sched[live-event-scheduling] Sched --> Del["dlc + live-event<br/>content delivery"] Del -->|"GameFeaturePlugin · entitlement-gated plan"| Client[V2 client] Store["UE Store / Entitlements"] --> Ent["entitlement verification<br/>(fail-closed)"] Ent --> Client PPP["maat-finance-ledger<br/>regional price + spend caps"] --> Store Econ["economy-analytics + balance-tuning"] -.tunes.-> Store Cos[isis-cosmetic-pipeline] --> Store Issue["outage / mispricing / bug"] --> Comp["player-compensation-service<br/>(idempotent make-good)"] --> Client

Progression & codex#

Progression spans the battle pass, fighter mastery, account level/prestige, the codex, the unified quest log, and the reward inbox — all part of the cross-progression shared state bound to one @oshun/identity root, so a player's progress and purchases follow them across PC, PlayStation, Xbox, and Switch (detailed in Online Backbone & Competitive Integrity).

The battle pass is the real, tested heart of it. season-pass-service (season-pass-service.ts) builds an XP-based track of up to 60 tiers over a ≤ 90-day season, with parallel free and premium reward lanes and rewards at every level — verified by tests that assert maxTier === 60, durationDays === 90, and rewards present at each tier. XP is server-authoritative and idempotent: applyV2SeasonPassXpGrant (line 931) dedups by grantId, so the spec's replayed grant leaves totalXp at 1200 twice over; the four XP sources (match-completion 150–500, daily-challenge 500–1000, weekly-challenge 1500–2500, event-bonus 250–2000) require replay evidence and anti-cheat validation; and premium-lane rewards lock with lockedReason: 'premium-entitlement-required' rather than silently granting. A weighted daily/weekly challenge pool auto-rotates a deterministic selection per window, paying XP plus the season-credits currency.

The codex and broader progression are data-of-truth, not a service stub. There is no dedicated progression-codex package; instead the real artifacts are the CSV tables that round-trip into UE DataTables: V2/balance/codex/ (codex-categories.csv, codex-fighter-entries.csv) and V2/balance/progression/ (account-level-rewards.csv, fighter-mastery-tracks.csv, style-mastery-tracks.csv, prestige-tiers.csv, mastery-level-rewards.csv, xp-gain-rates.csv, quest-log-catalog.csv, reward-inbox-rules.csv, long-form-goals.csv). The gRPC Progression/Codex/QuestLog/RewardInbox service surface the monolith specifies is the designed contract (the libs/proto/v2/progression package is absent), with computation and the reward inbox living server-side and mirrored to the client for HUD display. The principle throughout: progression unlocks content and cosmetics, never competitive advantage — fighters and their frame data are balance-locked, not pay-gated.

Community & support#

Community is governed, not unmoderated, and the governance is composed shared infrastructure rather than a V2 reimplementation. kuanyin-community-harmony (kuanyin-community-harmony.ts) wraps @kuanyin/community-harmony to bring emotional-temperature scoring, raid detection, and defense-mode/trusted-only escalation to the V2 crew / faction / battle-hub spaces; themis-community-governance wraps @themis/community (association- and sports-club-governance managers) for the rules-of-the-space layer; and world-boss-community-raid-bridge composes @hathor/simulation politics with the Kuanyin raid-defense toolkit so cooperative community-event content is both driven and protected. All three are off-rollback, trust-and-safety-only. Crucially, discipline routes out to the same audited Themis/Kuanyin plane that governs every Oshun product over the event bus, so a community ban and a match-conduct ban are the same auditable, appealable action.

The out-of-game surfaces are real web: apps/v2/web/community (an index.html with community.css/js/json and a meetups.ics calendar feed) and apps/v2/web/hub, alongside the read-only mobile companion covered in Esports, Companion & AI Services. The support backend — a priority-tier SLA router (P0 account/payment/harassment first-response ≤ 1 h through P3 feedback), Zendesk webhook integration, Canny-style feature voting, an ambassador moderation console, a Hall of Fame, and a 24-hour news-localization SLA across 18 launch locales — is designed as a gRPC/REST contract (libs/proto/v2/community/*.proto, absent today). The in-engine adapter list that binds all of this — @maat/finance, @maat/strategy, @kuanyin/moderation, @themis/community, @isis/* and the rest — is the real, validated BuildRequiredAdapters registry in V2OshunDomainAdapters.cpp:39, every entry flagged bMayInfluenceRollback = false.

Edge cases & failure modes#

  • A make-good pays once. applyV2PlayerCompensationGrant dedups by grantId; a replayed campaign grant returns duplicate: true and changes no balances. Every grant carries an evidenceRef and a non-empty approval chain.
  • DLC won't download without entitlement. planV2DlcContentDownload returns lockedReason: 'entitlement-required' with zero bytes for an unentitled caller; an entitled caller only downloads the assets whose content hashes aren't already installed.
  • Price can't drift or be spoofed. A spend whose unitPriceMinor ≠ the computed regional price, or whose total ≠ price × quantity, is rejected region-price-mismatch; the regional price is clamped between a floor and a baseline ceiling so no region is overcharged and inversion attacks are bounded.
  • Daily spend limits are enforced server-side, with a stricter cap for minor accounts, returning regulatory-cap-exceeded — and an offline purchase is cached and reconciled on next sync, never silently lost.
  • On-chain ownership fails closed. Missing opt-in, an unaccepted warning, a cert-banned or un-cleared surface, or an invalid credential/metadata each yield a typed blocked-* reason; nothing is exposed by default.
  • The wire contracts are honest. The gRPC calendar/progression/community services and the GameFeaturePlugin/EAC client tiers are designed contracts — libs/proto/v2 and V2/ue/Plugins/V2AntiCheat/ are not in the tree — while the pure-function ledgers, download planners, and the UE store/entitlement surface are real.

Where this connects#