Fighting Game · Features

Live Service, DLC, Battle Pass & Companion

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

A fighting game in 2026 is not a box you finish — it is a service you keep making. V2's post-launch product is the part of the game that ships after launch and keeps shipping for years: paid DLC fighters, free balance patches, seasonal cosmetic drops, a 60-tier battle pass with daily and weekly challenges, limited-time events wheeled on and off the calendar, make-good grants when something breaks, and a read-only phone companion that follows the player out of the match. This page is the feature-side companion to the "Online, Esports & Live Service" group, and it is unusually honest about a split that runs straight through the middle of the subject: the live-ops machinery is real, shipped TypeScript under apps/v2/, while a good deal of the content that machinery is built to deliver — the specific Year-1 roster, the five-faction weekly war — is design and balance data the services are designed to execute, not engine artifacts on disk. Where a claim is backed by code this page names the package and function; where it is a plan it says so. The section hub is ../V2_features.md.

What ships, honestly#

Four live-ops service packages are real, validated, test-backed TypeScript libraries, each with a co-located .spec.ts:

  • @v2/season-pass-service (apps/v2/season-pass-service/src/season-pass-service.ts) — the battle pass: a 60-tier, 90-day XP track with free and premium lanes, server-authoritative idempotent XP grants, an XP-source model, and a daily/weekly challenge rotation. Fully implemented.
  • @v2/dlc-content-delivery-service (.../dlc-content-delivery-service.ts) — entitlement-gated, delta-aware download planning for fighter, story, and cosmetic packs. Fully implemented.
  • @v2/live-event-scheduling-service (.../live-event-scheduling-service.ts) — a server-authoritative event calendar resolving upcoming | active | ended with conflict validation. Fully implemented.
  • @v2/player-compensation-service (.../player-compensation-service.ts) — incident-scoped, evidence-backed, approval-gated make-good campaigns with an idempotent grant ledger. Fully implemented.

The content-delivery mechanism is real engine tech — Unreal's GameFeaturePlugin system — but the arch companion is explicit that only three .uplugin files exist on disk and none are the V2Event_* / DLC-pack plugins the Year-1 roadmap describes; those are target artifacts. The MKX-style five-faction system (Lin Kuei, Black Dragon, Special Forces, Brotherhood of Shadow, White Lotus, with a weekly faction war) is spec plus balance data, not one of these services — and the one faction package that is real, @v2/aje-faction-governance, is a different thing entirely (an opt-in fan-token governance gate, not the progression ladder; see the naming caveat below). Character Mastery is grounded in real balance CSVs. The companion app is real but, by deliberate design, read-only and not a play surface. The architecture-level treatment of all of this lives in the arch companion ../architecture/live-ops-store-progression-and-community.md.

Live service & DLC#

Content delivery: the three live-ops services#

Post-launch content rides three of the four services. The dlc-content-delivery-service models a DLC pack as a typed V2DlcContentPack carrying a platformSku, an entitlement:dlc:* reference, a sha256:-prefixed manifestHash, and a list of assets that each mount under /Game/ from an https:// URI. Its default catalogue ships one of every pack kind the validator requires — a Nyx Fighter Pack (~620 MB), a Storm Market Story Expansion (~1.25 GB), and a Golden Gi Cosmetic Pack (~90 MB) — and validatePackKindCoverage throws if any of fighter-pack, story-expansion, or cosmetic-pack is missing. The load-bearing function is planV2DlcContentDownload: it first verifies the account holds the pack's entitlement (returning lockedReason: 'entitlement-required' and a zero-byte plan if not), then computes a delta download by filtering out any asset whose contentHash is already in installedContentHashes, and sums totalDownloadBytes over only what is genuinely pending. Every plan is stamped serverAuthoritative: true and requiresClientPatch: false — the core live-ops promise that content reaches the player without a client rebuild.

The live-event-scheduling-service runs the calendar. A V2LiveEventScheduleDefinition carries startsAt / endsAt, an optional visibilityStartsAt (so an event can be revealed before it goes live), regions, activationTags, per-event game modes (each with a queueId, rulesetId, and optional minimumClientVersion), and rewards. The default schedule defines a global Summer Rivals Weekend (2026-06-24 → 06-30) and a region-scoped Storm Trials (NA/EU/APAC, 2026-07-04 → 07-11). resolveNormalizedScheduleState derives upcoming | active | ended purely from the current time against the window, and validateGameModeWindowConflicts refuses to build a schedule where two time-overlapping events share a modeId or queueId — so the calendar cannot accidentally double-book a queue. This is the same scheduling shape the signature-event cluster leans on through @v2/limited-time-game-mode-service (see ./signature-events-and-standalone-modes.md).

When live service goes wrong, the player-compensation-service is a first-class capability, not an afterthought. A V2PlayerCompensationCampaign is scoped to an incidentId, tagged with a reason of bug or outage (the validator requireDefaultReasonCoverage insists the system carries both), backed by per-player evidenceRefs (telemetry pointers) and a severity of minor | major | critical, and approval-gated — every campaign must carry at least one approval record naming approvedBy, approvedAt, and an approvalReason. The two default campaigns model exactly the live-ops reality: a ranked-reconnect make-good (a bug, 500 season-credits plus a reconnect cache to two affected accounts) and a matchmaking-outage apology (an outage, 250 fight-coins plus an apology banner to one critically-affected account). planV2PlayerCompensationGrants mints a deterministic comp:<campaign>:<account> grant id per affected player and skips any already in processedGrantIds; applyV2PlayerCompensationGrant then folds the bundle into a per-account ledger, returning applied: false, duplicate: true on a replayed grant. That idempotency is the difference between "we compensated you" and "we compensated you four times because the job retried."

flowchart LR Sched[live-event-scheduling] -->|active window| Modes[event queues + rewards] Dlc[dlc-content-delivery] -->|entitlement + delta plan| Client[V2 client] Pass[season-pass-service] -->|XP grants + tiers| Client Incident[outage / bug] --> Comp[player-compensation] -->|idempotent ledger| Client

The Year-1 shape, the plugin lifecycle, and the no-coming-soon rule#

The roadmap the services execute — 4 character DLC packs, a mode-expansion DLC, two-paid-fighters-per-year minimum, quarterly cosmetic drops, and a free side of balance patches, stages, tower content, tutorials, and accessibility — is honestly a plan, the split that keeps the competitive base unified (everyone gets balance and stages) while monetising cosmetics and roster. A DLC plugin's documented lifecycle (authored → certified → staged → live → sunset), the hot-removal of license-conditional content, and the "every advertised mode ships content-complete, no coming-soon panels" promise are product commitments the GameFeaturePlugin mechanism is built to satisfy; the requiresClientPatch: false flag the delivery service emits is the code-level expression of the "staged plugins unlock at the calendar date without a patch" behaviour. A canary-failed patch's automatic rollback to the prior signed manifest, and the premium-currency spend/refund/region rules (owned by @maat/finance per the monolith), live across the Security & Compliance and store layers rather than in these four packages. One honest note on currency: the implemented services traffic in earned/grant currencies (season-credits, fight-coins); the premium store currency (the design docs' "Crowns"/"Krystals") and its spend ledger are an @maat/finance concern, not hardcoded here.

Battle pass, daily/weekly challenges & factions#

The battle pass — fully implemented#

The season-pass-service is the deepest of the four. Its default season, season-2026-summer-rivals ("Summer Rivals", 2026-06-21 → 2026-09-19), is a V2_SEASON_PASS_MAX_TIERS = 60, V2_SEASON_PASS_MAX_DURATION_DAYS = 90 track where each tier's xpRequired is index × 1000 (tier 1 at 0 XP, tier 60 at 59,000), and every tier carries both a free and a premium reward — the validator normalizeTierDefinitions rejects a track whose tiers are non-sequential, non-monotonic in XP, or missing a lane. buildRewardUnlocks walks tiers up to the player's current tier and marks each premium reward claimable only when premiumEntitlement is true, otherwise stamping lockedReason: 'premium-entitlement-required' — the code-level guarantee that the premium track grants cosmetics, titles, and emotes but never gates a competitively relevant item behind a paywall. Tier position is computed by resolveV2SeasonPassTierProgress, which caps total XP at the final tier and reports xpIntoTier, xpToNextTier, and a completionPercent.

XP is server-authoritative and idempotent. applyV2SeasonPassXpGrant short-circuits and returns the unchanged state if a grantId is already in processedGrantIds, so a retried grant never double-credits. The XP-source model (V2_DEFAULT_SEASON_PASS_XP_SOURCE_DEFINITIONS) defines four sources, each serverAuthoritative: true, with a base and a hard per-grant ceiling: match-completion (150 base / 500 max), daily-challenge (500 / 1000), weekly-challenge (1500 / 2500), and event-bonus (250 / 2000). The match-and-challenge sources are flagged replayEvidenceRequired and antiCheatValidated — the source-side hook for "the client attaches the deterministic replay as evidence and the server validates completion against it" (cross-ref the anti-cheat path in ./ranked-esports-circuit-and-local-coop.md). calculateV2SeasonPassXpGrantFromSource clamps every award to min(baseXp × multiplier, maxXpPerGrant), so an event multiplier can never blow past the ceiling.

Daily and weekly challenges#

The challenge system is a real deterministic rotation, not a random shuffle. buildV2SeasonPassChallengeSystemSurface draws from a typed challenge pool (five daily, four weekly entries by default — "Win 6 rounds", "Land 8 special moves", "Complete 5 ranked sets", "Land 40 combo finishers", and so on), each tagged with a cadence, a category (combat | engagement | ranked | event), an objectiveKey, a targetValue, a weight, and XP/currency rewards. selectRotatingChallenges scores each candidate by buildHashUnit(<rotationId>:<challengeId>) / weight and takes the lowest — a weight-biased but fully deterministic selection keyed to the rotation id, so the same UTC day reproduces the same dailies and a heavier-weighted challenge is more likely to surface. buildChallengeRotationWindow snaps the daily window to UTC midnight and the weekly window to the preceding Monday (the source's "dailies rotate at 00:00, weeklies rotate Monday"). The spec (season-pass-service.spec.ts) pins this precisely — one test asserts dailies auto-rotate while weeklies stay stable during the same UTC week. Completion runs through completeV2SeasonPassChallenge, which issues both an XP grant (routed to the matching daily-challenge / weekly-challenge source) and a season-credits currency grant under one deterministic, idempotent challenge:<season>:<account>:<rotation>:<challenge> key. Because the daily challenge's XP feeds the same pass track, "one objective set feeds both" is literal here, not aspirational.

The faction system — spec, with one real namesake to disambiguate#

This is the page's sharpest honesty line. The MKX-style five-faction war the monolith describes — pick one of five factions at first launch, every match contributes faction XP to a global leaderboard, a weekly faction-vs-faction event grants the winning faction an exclusive cosmetic, a 7-day switch cooldown forfeits the week's contribution — is not implemented in these services. The season pass's XP sources are match/daily/weekly/event; there is no faction-quest source and no faction-war leaderboard in the live-ops packages. The faction concept surfaces only as balance data (faction quests appear in V2/balance/progression/quest-log-catalog.csv). The one package whose name collides — @v2/aje-faction-governance — is a genuinely different system: an opt-in, off-rollback fan-token governance gate (fan_token_gated_gameplay) that links an external token balance to faction votes, with a loud consent warning before it exposes anything outside the client. It is not the progression ladder; treating it as the MKX faction system would be a category error, so this page flags the collision the way the signature-events page flags the KOF. prefix trap.

Character Mastery, by contrast, is grounded: fighter-mastery-tracks.csv defines a per-fighter Curve.Mastery.Standard to max_level = 30, each pointing at mastery-level-rewards.csv and a seasonal boost_rotation_theme — the 1-30 mastery ladder unlocking intros, poses, color slots, and the Mastery-30 signature kit (cross-ref ./relationships-progression-and-creator-suite.md).

The companion app#

The companion is a shell-native iOS/Android app that is read-only by design, real on disk at apps/oshun/mobile/v2/ (V2CompanionApp.tsx, companionAppModel.ts, a V2CompanionLaunchTile.tsx, and a .test.tsx). Its V2CompanionModel types out the out-of-game surfaces the monolith promises: a V2CompanionSecondScreenMode (a low-fidelity stream with a timeline scrubber, chapter tags, and coach-note review — cosmetic-only, no gameplay effect), a list of V2CompanionCoachAnnotations delivered coach-stream or push-preview (e.g. an "anti-air gap" or "oki repeat" note a coach attaches to a submitted replay), V2CompanionPushNotificationTopics for the per-category opt-out push handoff (match-end, party-invite, tournament-round, daily-challenge, pass-tier), plus wearable glances and cross-feature deep links. The arch companion is blunt about the boundary: a phone is not a deterministic match client, so the app is explicitly not a play surface and not a cross-play participant — kept entirely outside the competitive path, composing the same backbone (auth, replay, store) read-side only.

The public API is a real artifact too: libs/openapi/v2/companion.yaml ("V2 Companion Public API", ~30 KB) is the typed contract the app and third-party integrations read. It carries x-ratelimit-limit / x-ratelimit-window-seconds headers on its endpoints and an authenticated tournament-organizer bracket-creation endpoint (single/double-elimination and round-robin), which is the OpenAPI expression of the monolith's rate-limit tiers (100 req/min unauthenticated, 1000 for a registered app, 30 per account on personal data) and TO webhooks. The knowledge surfaces (/v2/wiki, /v2/glossary, /v2/roadmap) and the broader companion/AI-broadcast story are covered in ../architecture/esports-companion-and-ai-services.md.

How it connects#

The through-line is consistent with the rest of V2: the machinery is real service code composing the shared Oshun economy, governance, and content substrates, and the page is precise about where shipped TypeScript ends and the Year-1 roadmap (and the faction war) begins. Entitlements and cross-progression that bind a pass, a vault, and a DLC unlock to one identity are the online backbone's; the spend limits, region pricing, and hotfix-rollback mechanics are the compliance layer's; the limited-time-mode scheduling these events share is the signature-event cluster's.