Tactical Action · Features

Live Service, Progression & the Vault

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

6sections12 minread2diagrams

On this page

V4 is six genuinely different games — a Rainbow-Six breach, a Hitman sandbox, a Commandos squad puzzle, a Black-Myth boss duel, an Age-of-Empires ladder, a couch-co-op Contra run — wearing one roster, one account, and one online backbone. What turns that catalogue into a product that keeps growing rather than six boxes you finish is the live-service spine: a 90-day season cadence, a hundred-tier battle pass, two independent XP ladders, a year-long themed calendar, operator and mission DLC delivered without a binary patch, a region-aware store, and a per-account vault that keeps a player's best replays and speedrun ghosts forever. This page is the player-facing tour of that spine, and it keeps the same honesty line the rest of V4 holds to: the composition machinery is real, compiled C++ and tested Rust, while the specific content that machinery is built to deliver — the exact Year-1 cosmetics, the cooked operator art — is authored design data the code is designed to execute, not binary engine assets on disk. Where a claim is backed by code, this page names the subsystem and the function; where it is a plan, it says so. The section hub is ../V4_features.md.

The design choice underneath everything is the one V4 follows everywhere: a unit of live content is data plus a load plan, never a new C++ subclass. A season is an FV4SeasonCadence value; a DLC operator is an FV4DLCGameFeatureDefinition whose GameFeaturePluginURL is a plugin:// URI; a cosmetic is an FName token routed into a typed inventory. That keeps the whole live-service surface exhaustively unit-testable headless, long before any map is cooked, and it keeps post-launch content flowing through the same GameFeature plugin boundary the per-cell rulesets already use — never touching the deterministic gameplay core.

What ships, honestly#

The live-service spine is split across two real code tiers plus one honest "deployment substrate" seam.

  • UV4LiveServiceSubsystem — the live-ops domain layer, ~1,116 lines of C++ at V4/ue/Source/V4OnlineServices/Private/V4LiveServiceSubsystem.cpp. It exposes roughly forty BlueprintPure builders and validators for season cadence, battle-pass tracks, cell-themed cosmetics, operator/mission DLC, the themed calendar, ranked ladders, signature events, and brand partnerships. The composition is real arithmetic and real validation, not a manifest reader that echoes its input.
  • UV4ProfileSubsystem and UV4CurrencyLedger — the progression layer in V4/ue/Source/V4Persistence. The profile subsystem owns the operator and account XP ladders, the Codex, and the typed cosmetic inventory; the currency ledger is an overspend-rejecting, server-validated wallet.
  • The Rust online-services crateapps/v4/online-services mounts the back-end of the same surface: a ReplayService (the vault), a SpeedrunGhostArchive, a StoreService (currency rules, pricing, DRM, DLC offers), and a ContractAuthorService (daily/weekly contracts, faction rewards), all behind real Axum routes in src/http.rs.

Coverage is genuine. The V4Tests suite is ~92 .cpp files; LiveServiceSpec.cpp alone carries 84 Test* assertions over cadence, battle pass, DLC, calendar, ranked, and partnerships, and RoundTripSpec.cpp carries 92 over the profile, ledger, contract, and replay-store subsystems. The Rust crate adds 14 unit tests in lib.rs and 13 route tests in http.rs.

Four honest caveats, the same ones the architecture companion ../architecture/game-modes-live-service.md records. First, the cosmetic content is uncooked: every reward in these builders is an FName token (Skin.Winter.RavenParkas, WeaponSkin.Blacksite.Rifle), and a scan of the plugin tree finds JSON *.uasset.v4asset.json stand-ins and zero binary .uasset — the IDs are real, the art is a target artifact. Second, the Rust state is in-memory (BTreeMap, Arc<Mutex<T>>); the PostgreSQL/Redis/ClickHouse/S3 stores the deployment names are the production target, covered in ../architecture/game-modes-live-service.md. Third, the five-faction "season-long competition" is real reward data (ContractAuthorService::faction_rewards() mints five factions) riding a generic leaderboard primitive, not a dedicated faction-war engine. Fourth, Twitch Drops and the creator-code revenue program are owned by community-service and creator-relations-service, covered in Content, Creator & Community, not these packages.

Live service: seasons, the calendar & DLC#

The season cadence — and why client and server agree#

A season is FV4SeasonCadence, and BuildSeasonOneCadence fixes Season 1 at a 90-day run, a 10-day pre-season reset, 5 placement matches, and a mid-season balance patch on day 45. The predicates around it are small, real logic rather than flags: IsSeasonActiveDay bounds [0, 90), IsPreSeasonResetDay accepts [-10, 0), RequiresPlacementMatchesDuringPreSeason returns the 5 placements only inside that pre-season window, and IsMidSeasonBalancePatchDay fires exactly on day 45. The same constants are returned by the Rust launch_season_cadence() (lib.rs:645) and its is_preseason_reset_day / is_midseason_balance_patch_day mirrors — so the client UI and the server agree on the calendar by construction, not by a hand-synced config. The mid-season patch ships through the data-table hotfix channel, so the back half of the season plays a tuned meta with no binary update.

Battle pass, contracts & factions#

BuildBattlePassTracks mints two tracks per season at a flat 1000 XP per tier: a Free track of 50 tiers and a Premium track of 100 tiers, each carrying its reward-ID list. Tier progression is driven by Account XP, not a separate pass currency — operator-account-progression.json sets drivesBattlePassTierProgress: true, and the account ladder is the engine underneath. The pass cosmetics never touch balance: a reskin shares its base weapon's stats exactly, the line StoreService::can_share_cosmetic enforces by refusing any stat-changing cosmetic.

The recurring engagement loop is contracts, modeled in the Rust ContractAuthorService. daily_contracts mints three objectives at 250 XP each (Daily.Eliminate, Daily.Extract, Daily.Support); weekly_contracts mints seven at 1000 XP each, one per cell plus a social objective. Rotation is keyed to a UTC server reset: server_reset_key(epoch_hour, reset_hour) snaps to a normalized day with div_euclid(24), and should_refresh_at_server_reset returns true only when the previous and current reset keys differ — so dailies roll over at the reset boundary and not before. faction_rewards() defines the five factions the world-state fiction uses (Vanguard, Swarm, Choir, Modern Forces, Resistance), each with a 500-XP reward and a themed cosmetic track; faction reputation is per-faction and the aggregate ranking rides the service's generic leaderboard / submit_score primitive.

The themed calendar#

BuildLiveServiceThemeWindows returns the five recurring windows that mirror V4/liveops/live-service-calendar.json — Winter Operation (December), Lunar New Year (Jan–Feb), Spring (Mar–May), Summer Heat (Jun–Aug), Harvest (Sep–Nov) — each with a featured playlist, limited-time contracts, and themed cosmetics. ResolveLiveServiceThemeForMonth walks them through V4MonthInThemeWindow, which handles the December→February wraparound correctly (when StartMonth > EndMonth, a month matches if it is >= start or <= end). The calendar is self-checking: ValidateLiveServiceCalendar asserts exactly one theme covers every one of the twelve months, that the anniversary login bonus is active on Oct 1 but not Oct 8, that there is exactly one 100%-proceeds charity drive per year, and that the six platform cosmetic tie-ins cover all nine required platform IDs. The competitive line is enforced in the data itself: every FV4PlatformCosmeticTieIn carries bGameplayStatsAffected = false and a platform-holder approval ticket, and ValidatePlatformCosmeticTieIn rejects any tie-in that is not cosmetic-only and marketing-signed. Beyond the recurring windows, BuildSignatureEventModes authors the Year-1 event slate — Halloween "Night Shift" (a tactical-FPS PvE horror playlist with NPC reskins), Holiday "Hometown Skirmish" (festive map reskins across all six cells plus a limited-time RTS map), Pride "Signal Colors" (a creator-pick partner-charity cosmetic suite), and a mid-season Esports All-Stars exhibition — and ValidateSignatureEventModes pins each one's shape.

DLC over the GameFeature boundary#

New playable content rides the same plugin boundary as the rulesets. BuildOperatorDLCDefinitions and BuildMissionDLCDefinitions emit FV4DLCGameFeatureDefinitions whose GameFeaturePluginURL is plugin://<PluginName> (built by V4MakeDLC). The free/paid split is encoded, not described: an operator DLC is valid only when it is free for premium-pass holders at price 0 (ValidateDLCGameFeatureDefinition requires bFreeForPremiumPass && PriceUsdCents == 0 for the Operator type), while a mission DLC must price inside the $9.99–$19.99 band (ValidateMissionPriceTier bounds [999, 1999] cents). The Rust StoreService::dlc_gamefeature_offers() returns the same two offers — the season-1 Nyx operator at 0 cents free-for-pass, and the Blacksite campaign at 1499 cents — so the store back-end and the in-engine builder describe one catalogue.

flowchart LR Cad[BuildSeasonOneCadence / launch_season_cadence] -->|90d · 10d reset · day-45 patch| Pass[BuildBattlePassTracks] AccXP[Account XP ladder] -->|drives tier progress| Pass Contracts[daily/weekly contracts] -->|XP| AccXP Cal[BuildLiveServiceThemeWindows] -->|themed cosmetics + LTM contracts| Client[V4 client] DLC[BuildOperator/MissionDLCDefinitions] -->|plugin:// GameFeature, no client patch| Client Pass --> Client

Progression: operator, account & the Codex#

Two XP ladders, one anti-grind cap#

UV4ProfileSubsystem runs two independent tracks. Operator XP levels each named operator 1→100 (max 99,000 XP); Account XP levels the player 1→1000 (max 999,000 XP). Both share a flat 1000-XP-per-level curve through the shared helpers V4CalculateLevelFromXP and V4XPRequiredForLevel, and both clamp at their ceiling rather than overflowing — AddXP and AddOperatorXP each pin the running total to V4MaxXPForLevelTrack(...). The anti-grind rule is real arithmetic: ClampOperatorDailyXPGrant returns 0 once an operator hits the 5,000-XP daily cap and otherwise clamps a grant to the remaining headroom. RoundTripSpec.cpp pins exactly this — ClampOperatorDailyXPGrant(4900, 250) returns 100, CalculateAccountLevelFromXP(999000) returns 1000, and the operator-100 threshold is 99000. Reward milestones (DA_ProgressionModel mirrors them) hang Codex entries, titles, perk slots, cross-cell skins, and the Level-100 / Level-1000 mastery cosmetics off the two ladders.

The currency ledger#

UV4CurrencyLedger (V4/ue/Source/V4Persistence) is the wallet, and it is fail-closed. ApplyTransaction credits and debits a supported currency but refuses an overspendRoundTripSpec.cpp drives a balance of 60 OperatorTokens and asserts a −100 debit is rejected while the audited balance stays 60. Hard currency takes a stricter path: ApplyServerValidatedTransaction for CombatPoints requires a real platform and a server token, and the test confirms a NAME_None platform is rejected. The back-end agrees on the shape: StoreService::currency_rules() defines three currencies — OperatorTokens and EliteTokens (soft, cross-platform) and CombatPoints (hard, per-platform) — and can_share_currency / is_per_platform_currency encode the rule that soft currency crosses platforms while hard currency cannot, the code-level reason hard-currency balances are per-platform.

Codex & the cosmetic inventory#

The Codex is an unlockable in-game encyclopedia across eight categories (BuildCodexCategories: Operator, Weapon, Gadget, Map, Faction, Civilization, Ruleset, Vehicle), each FV4CodexEntryDefinition carrying lore, an asset gallery, and design commentary plus level gates. CanUnlockCodexEntry is a real predicate — it requires the account to meet RequiredAccountLevel and (when set) the named operator to meet RequiredOperatorLevel — and BuildLaunchCodexEntrySamples ships nine fully written sample entries (Cobra's dossier, the Clubhouse map, the MH-6 Little Bird, and so on). Cosmetics route into a typed inventory across nine categories (BuildLaunchCosmeticCategories); UnlockCosmeticItem rejects an unknown category, records source and a cross-platform flag, and HasCrossPlatformCosmetic reads it back — the round-trip test confirms an operator skin's cross-platform flag survives a save/load cycle.

The vault, the ghost archive & the store#

The replay vault#

The per-account vault is the Rust ReplayService, and it is real retention logic. A fresh upload gets DEFAULT_REPLAY_RETENTION_DAYS = 14 and a DefaultCloud14Day tier. Starring a replay (star_archive_at) flips it to LIFETIME_REPLAY_RETENTION_DAYS (u16::MAX) and a LifetimeStarredArchive tier — a starred replay survives the expiry sweep, which expired_replays computes as created_at + retention_days·86,400 ≤ now, skipping anything starred. The hard launch quota is 500 starred per account: once starred_count hits MAX_STARRED_REPLAYS_PER_ACCOUNT, the next star returns LimitReached. The decisions are exhaustive — Archived, AlreadyArchived, NotFound, LimitReached, AccountMismatch, and (on unstar) RestoredDefaultRetention, which returns the replay to normal 14-day expiry. starred_archive returns the account's vault ordered by archive time then ID. And account-deletion is handled: scrub_deleted_account_from_replay clears the deleted account's identifiers, repaints its pawn track to Skin.Anonymous.DeletedAccount, and stamps the record privacy_scrubbed. The vault test exercises all of it — stars to exactly 500 then asserts LimitReached, asserts AccountMismatch when the wrong account stars a replay, and asserts the scrub anonymizes the pawn track while preserving the replay.

The ghost data archive#

SpeedrunGhostArchive keeps the fastest verified run per route so other players can race it in-game. Submission is gated hard: submit_verified_speedrun_ghost returns RejectedInvalid if any field is empty or the duration is zero, RejectedUnverified unless verified_by_server is set, and RejectedSlowerOrEqual if the run does not beat the current active ghost. Every accepted ghost carries the replay's SHA-256, an input-stream hash, and checkpoint hashes. A faster run supersedes the prior one: the previous record flips to Superseded with a superseded_by_ghost_id pointer and the new record records its parent_ghost_id, so ghost_lineage keeps the full chain rather than overwriting history. The test submits a 510,500 ms run, rejects a slower 512,000 ms run as RejectedSlowerOrEqual, accepts a faster 498,250 ms run, and asserts the lineage retains the superseded entry with the correct supersession pointer.

The store & region-aware pricing#

StoreService is the store back-end, and its routes are real (store_router() in http.rs mounts purchases, currency-rules, battle-pass, dlc-offers, regional prices, per-region lookup, and a pricing-transparency endpoint). regional_prices() returns six PPP-anchored regions — US ($59.99 base, tax-exclusive), EU, JP, BR, IN, TR — each with a ppp_index, a tax_display of inclusive/exclusive, and minor-unit prices for the standard edition, the battle pass, and a 900-CombatPoints pack; platform-store-features.json and DA_PlatformStoreFeatures are the authored mirror. validate_pricing_transparency is a genuine gate: it requires all six regions present and every row to carry a non-empty currency, positive prices, a positive PPP index, and a valid tax-display mode — the code behind the public /store/pricing page that publishes method, last-updated time, and refund policy. DRM is an allow-list, not a wrapper: verify_drm_receipt accepts only the seven first-party providers (Steam, EOS, PSN, Xbox, Nintendo, App Store, Google Play) and explicitly returns ThirdPartyDrmRejected for a Denuvo wrapper — the code expression of V4's no-third-party-DRM commitment. Achievements unify across platforms too: platform_achievement_providers() maps one six-achievement design onto PSN trophies (with the Platinum), Xbox gamerscore, Steam, Nintendo, Apple Game Center, and Google Play Games, each server-authoritative with an offline queue and account-link backfill.

flowchart TD Match[match / speedrun] -->|upload| RS[ReplayService 14-day default] RS -->|star_archive| Vault[(lifetime vault · 500 cap)] Vault -->|unstar| RS RS -->|account deletion| Scrub[scrub → anonymous skin · privacy_scrubbed] Match -->|verified submission| Ghost[SpeedrunGhostArchive] Ghost -->|faster supersedes| Lineage[(retained lineage)] Store[StoreService] -->|regional_prices · DRM allow-list| Pricing[/store/pricing transparency/] Store -->|dlc_gamefeature_offers| Client[V4 client]

How it connects#

The through-line is the same as the rest of V4: the machinery is real — compiled C++ composition over typed manifests, tested Rust domain services behind Axum routes — and the page is precise about where shipped code ends and the authored Year-1 content (the cooked cosmetics, the faction-war aggregation) begins. The accounts, cross-progression, matchmaking, and replay transport that this pass, vault, and ledger sit on top of are the online backbone's; the cooked-content, cert, and store-of-record mechanics are the platform layer's.