Fighting Game · Architecture

V2 Product Promise & Cross-Reference Conventions

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

8sections13 minread1diagram3tables

On this page

V2 is one game — a fighting game built on Unreal Engine 5.5 — not a platform of seven loosely-bound modes. This page is the orientation door to the V2 architecture: it states the product V2 commits to shipping, and it defines the notation that lets four large planning documents, twenty-seven Unreal C++ modules, and an automated validation backbone all point at the same things without drifting. The promise and the conventions belong together because, in this repo, the convention is the mechanism by which the promise is kept honest: a bare §7.1 in any V2 doc resolves to a numbered task in V2_TODOS.md, that task owns a JSON contract under V2/ue/Content/, and a Python checker under V2/ue/Tools/ fails CI if the shipped tree drifts from the contract. The architecture file says it plainly — "Where a section names a module or path, it names a target artifact unless the path already exists in the repo. The TODO that first references the artifact owns creating it with tests and validation" (V2/V2_ARCHITECTURE.md:15).

That target-artifact discipline is why this orientation page can be precise about what is real versus planned: the same words that describe a subsystem are the words a checker greps for. The backlog carries 138 numbered top-level sections — a count that is not a stylistic claim but a machine invariant: V2/tools/validate-v2-docs.py:156 literally asserts if len(sections) != 138 and fails the docs gate otherwise. The determinism the netcode promise rests on is wired into the build target (V2/ue/Source/V2.Target.cs) as strict floating-point compiler flags and a deterministic cook policy, not merely asserted in prose. This page is the feature-and-promise companion to the architecture catalogue; the section hub is ../V2_ARCHITECTURE.md.

What ships, honestly#

The module skeleton is real and consistent. V2/ue/V2.uproject declares 27 modules and exactly those 27 directories exist under V2/ue/Source/ — there is no drift between the project file and the disk. Several modules carry genuine weight rather than stubs: V2Gameplay is ~218 C++ files (GAS abilities, attributes, match-state, the network-prediction movement component), V2Combat ~36, V2Netcode ~20, V2Input ~14. The determinism build policy is real: all three targets — V2.Target.cs, V2Server.Target.cs, V2Editor.Target.cs — turn off unity builds, add /fp:strict /fp:except- (MSVC) or -fno-fast-math -ffp-contract=off (Clang), and V2/ue/Config/ DefaultGame.ini:10 sets bEnableDeterministicCookOrder=True and bDeterministicShaderCodeOrder=True. The validation backbone is real and large: 826 *_Contract.json files under V2/ue/Content/ are paired with 1,155 check-v2-*.py checkers under V2/ue/Tools/, fed by a 196 KB CI workflow at .github/workflows/v2-build.yml. The gRPC pre-build step is realV2.Target.cs shells out to pnpm --filter @oshun/codegen run v2:grpc-stubs, which resolves to tsx src/v2-grpc-stubs.ts in tools/codegen/package.json. And the doc-convention gate is real: validate-v2-docs.py resolves every Markdown link to a file and anchor, checks every bare §N against V2_TODOS.md, and rejects re-introduced "open decision" language.

A handful of promise-level claims are spec-only or aspirational, and this page names them rather than implying they are shipped:

  • The preprocessor symbols V2_DETERMINISM / V2_STRICT_FP are added as ProjectDefinitions in the targets, but no .cpp/.h in V2/ue/Source/ branches on them — determinism is enforced today by compiler flags, cook settings, and the contract+checker gate, not by #if V2_DETERMINISM code paths. The defines are reserved seams.
  • The architecture glossary lists V2Peripherals, V2AntiCheat, V2DynamicMusic, and V2FrameDataPublisher as engine modules; none exist on disk as Source modules or plugins. V2AICommentary and V2AdaptiveAI are listed as engine modules but actually ship as plugins under V2/ue/Plugins/, not as V2/ue/Source/ modules.
  • The 60-plus V2Mode_*, V2Event_*, and V2RaceMode_* rows in the game-feature-plugin roster are planned GameFeature surfaces; the disk has five plugin directories (BellonaUnrealEditor, V2AICommentary, V2AdaptiveAI, V2AssetLinter, V2Editor) and none of the per-mode plugins yet. Mode identity currently lives in the V2Modes registry module.
  • The headline netcode numbers (rollback default for 1v1 + 2v2 tag, ≤ 8 frames of rollback at ≤ 100 ms RTT, 60 Hz) are stated targets in the promise, not measured ship metrics on this page; the rollback engine's real state is the subject of ./rollback-netcode-and-tag-team.md.

Provider-gated surfaces are honest seams, not failures: cross-play and cross-progression depend on the OnlineServices / OnlineServicesEOS plugins enabled in the uproject and on per-platform back ends; locale-aware gore opt-out depends on real rating-board data (V2/legal/rating-boards.json, platform-cert-bans.json). Those are real integrations awaiting credentials and platform sign-off, covered in ./online-backbone-and-competitive-integrity.md and ./security-compliance-and-sister-monorepo-integration.md.

The product promise#

V2's promise, as authored in V2_features.md § "V2 Product Promise" and the architecture's V2 Product Promise section, is a small set of non-negotiable commitments. The unifying idea is per-ruleset feel, one shared spine: when a match is in Mortal Kombat ruleset it must feel like MK; in Tekken ruleset, like Tekken; in WWE ruleset, like WWE — "no compromise blends" — yet all rulesets share one roster, one creator suite, one online backbone, and one story.

# Commitment Code-grounding / status
1 One product, not seven — per-ruleset feel with no compromise blends Per-ruleset feel lives in the swappable V2Modes registry + (planned) per-mode GameFeature plugins
2 Rollback netcode default for 1v1 and 2v2 tag; ≤ 8 frames @ ≤ 100 ms RTT; 60 Hz Target. V2Netcode module exists (~20 files); strict-FP build backs frame-accuracy. See ./rollback-netcode-and-tag-team.md
3 3v3 tag, Royal Rumble, ≥ 30-in-ring on client-server with rollback-emulated prediction Tiered netcode envelope (diagram below); V2_NetworkPredictionComponent present in V2Gameplay
4 BR-100, World Tour, Battle Hub, Convoy, Heist, World Boss on dedicated servers V2Server.Target.cs is a real TargetType.Server with a Linux-dedicated-server contract gate
5 Complete launch content — no "coming soon" panels Enforced socially by the backlog; validate-v2-docs.py rejects open-decision markers
6 First-class single-player (Story, MyCAREER, MyRISE, Universe, MyGM, MyFACTION, GOAT Career, …) each ≥ 10 h Planned V2Mode_* plugin surfaces; identity in V2Modes today
7 Honor the audience — gore opt-in/out & locale-aware, faithful UFC sim, SF unsmoothed motion inputs, Tekken juggle, Def Jam feel Locale data in V2/legal/; input fidelity owned by V2Input
8 Creator suite (CAF/CAW/CAS/CAA/CAE/CAM) as a first-class surface Spec-level; authoring tools in V2Editor
9 Cross-play & cross-progression across PC, PS5, XSX, Switch2, Steam Deck Provider-gated via OnlineServices/OnlineServicesEOS
10 Degrade safely under offline, partial-net, weak-grounding, low-bandwidth, controller-loss, mid-match-disconnect, provider-outage Network quality & fallback design; V2Netcode + V2OnlineServices
11 Launch only with verification, observability, runbooks, release gates, rollback plans, post-deploy monitoring The 1,155-checker + v2-build.yml gate plus apps/v2/* ops surfaces

The netcode envelope is a promise tier, not one switch#

Commitments 2–4 are really one decision expressed in three tiers, because rollback does not scale to arbitrary player counts at 60 Hz. The promise draws the line explicitly, and the architecture mirrors it: rollback is the default for the small-roster competitive core, prediction-on-client-server covers the mid-size scrums, and dedicated servers carry the large-world and free-roam modes where rollback is neither affordable nor meaningful.

flowchart TD A[Match request] --> B{Active fighters<br/>per side & mode} B -->|1v1 or 2v2 tag| C[Rollback netcode<br/>60 Hz · ≤8f @ ≤100ms RTT<br/>strict-FP determinism required] B -->|3v3 tag · Royal Rumble · ≥30 in-ring| D[Client-server<br/>+ rollback-emulated input prediction] B -->|BR-100 · World Tour · Battle Hub<br/>Convoy · Heist · World Boss| E[Dedicated server<br/>V2Server.Target.cs · authoritative] C --> F[V2Netcode rollback engine] D --> F E --> G[V2OnlineServices · ReplicationGraph · Iris]

The hard floor under tier 1 is determinism: rollback re-simulates past frames, so the same inputs must produce bit-identical state on both peers. That is why the build, not just the gameplay code, carries the determinism policy — it is a property of how every module is compiled, covered next and in depth in ./combat-system-gas-frame-data-and-determinism.md.

Cross-reference conventions#

Five V2 documents reference one another constantly. To avoid five competing numbering schemes, V2 fixes one rule and machine-checks it.

Bare §N and §N.M always resolve to V2_TODOS.md. The backlog is the only V2 document whose section numbers are used as hyperlinks. Verified counts: 138 top-level ## N. sections, 865 ### N.M subsections, and 19 #### N.M.K sub-subsections — 1,022 numbered headings in total. (The monolith's shorthand "~1000 numbered subsections" is best read as that all-numbered-headings figure; the strict subsection count is ~884.) The top-level 138 is the one figure the gate pins exactly.

Where ambiguity matters, references are doc-prefixed:

Form Resolves to Example
§N / §N.M V2_TODOS.md section (default) §80 → Compliance Deep; §52.8 → Live-service ops cadence
deps§N V2_DEPENDENCIES.md §N deps§57 → Hardware Peripheral SDKs
features§"anchor" V2_features.md by named anchor features§"V2 Product Promise"
arch§"anchor" V2_ARCHITECTURE.md by named anchor arch§"Rollback Netcode"

V2_features.md and V2_ARCHITECTURE.md deliberately use named anchors, not numbers, so they never duplicate the backlog's numbering. The docs-center pages you are reading (this one and its siblings) follow the same rule: link a backlog task by its §N, link a sibling page by its relative ./slug.md path.

The convention is enforced, not merely documented#

V2/tools/validate-v2-docs.py is a real gate, and its checks map one-to-one onto the rules above:

  • Section countif len(sections) != 138: errors.append(...) (validate-v2-docs.py:156). Add or delete a top-level backlog section without updating the docs and the gate fails.
  • Bare-ref resolutionBARE_SECTION_RE = §\s*(\d+)(?:\.(\d+))? (validate-v2-docs.py:39); every §N/§N.M must resolve to a real V2_TODOS.md section or the script reports "does not resolve to V2_TODOS.md".
  • Link integrity — every [text](href) must resolve to an existing file and (for #anchor links) a real heading slug.
  • No open decisions — markers like Owner: TBD, pending decision, placeholder task, and currently empty stub are rejected, which is the enforcement arm of promise #5 ("no coming soon").
  • Package-name honesty — every backticked @scope/pkg reference must be a real repo package, a package declared as planned in V2_DEPENDENCIES.md, or a name explicitly marked corrected/not-to-use.

Worked example: following §7.1 end to end#

Take the bare reference §7.1 (floating-point determinism). The convention says it resolves to V2_TODOS.md §7.1. That task owns a target artifact, and the artifact is a real contract on disk:

jsonc
// V2/ue/Content/V2/Netcode/Determinism/FloatingPointDeterminism_V2_Contract.json
{
  "schema": "v2.netcode.floatingPointDeterminism.v1",
  "task": "V2_TODOS.7.1.floating-point-determinism",
  "targets": ["V2.Target.cs", "V2Editor.Target.cs", "V2Server.Target.cs"],
  "compilerDefinitions": [
    "DETERMINISM=1",
    "V2_DETERMINISM=1",
    "V2_STRICT_FP=1",
  ],
  "strictFloatingPoint": {
    "msvc": ["/fp:strict", "/fp:except-"],
    "clang": ["-fno-fast-math", "-ffp-contract=off"],
  },
  "cookSettings": {
    "bEnableDeterministicCookOrder": true,
    "bDeterministicShaderCodeOrder": true,
    "referenceDdcRequired": true,
  },
  "automation": {
    "checker": "V2/ue/Tools/check-v2-floating-point-determinism.py",
    "workflow": ".github/workflows/v2-build.yml",
  },
}

The contract's automation.checker points at check-v2-floating-point-determinism.py, which reads the JSON and then cross-validates the actual tree: it confirms the three .Target.cs files declare the definitions and strict-FP flags, that Config/DefaultGame.ini carries bEnableDeterministicCookOrder=True and bDeterministicShaderCodeOrder=True, and that Build/Build.xml and the cook-all.sh / cook-all.ps1 wrappers pass -architecture=x64 with a reference DDC. That is the full chain a single §7.1 implies: doc reference → backlog task → JSON contract → cross-file checker → CI workflow. Multiply by 826 contracts and 1,155 checkers and you have the V2 verification backbone.

How the promise is enforced in the tree#

V1's domains ship their contracts as Zod schemas in libs/contracts/src/; V2 does not — there is no libs/contracts/src/v2. V2's "contracts" are a different shape because the artifact under test is an Unreal project, not a TypeScript service. Three enforcement layers carry the promise:

  1. Build-target invariants. V2.Target.cs (game), V2Server.Target.cs ([SupportedPlatforms(UnrealPlatformClass.Server)], editor-only data stripped), and V2Editor.Target.cs all call the same ConfigureV2DeterministicBuild and AddV2GrpcGenerationStep helpers, so determinism and the gRPC stub generation are uniform across game, server, and editor builds. bUseUnityBuild = false keeps translation-unit boundaries stable for reproducible builds.
  2. Contract + checker gates. The 826 *_Contract.json files are the machine-readable spec for each target artifact; the 1,155 check-v2-*.py scripts are the gates that fail when the tree drifts. Their names read like a table of contents for the game — check-v2-combo-counter.py, check-v2-hit-once-registry.py, check-v2-tekken-movement-layer.py, check-v2-chaos-cloth-presets.py — each owned by the §N task that introduced it.
  3. CI orchestration. .github/workflows/v2-build.yml runs the checkers and (on supported runners) the build/cook. The pre-build gRPC step keeps the service contracts and the engine client in lockstep: tools/codegen exposes both v2:grpc-stubs and v2:grpc-stubs:check, the latter for verifying generated stubs are current without rewriting them.

This is also why promise #11 ("launch only with verification, observability, runbooks, release gates") is structurally credible rather than a slogan: the gates already exist as code. The detailed treatment of those gates is ./telemetry-performance-testing-and-release-gates.md, and the build/cook side is ./build-cook-assets-data-and-production.md.

Module topology, briefly#

The glossary's engine-module table lists 30 names; the disk has 27 Source modules. The deltas are worth stating once here and are explored in full in ./glossary-and-module-topology.md:

  • In the glossary, absent from disk as Source modules: V2Peripherals, V2AntiCheat, V2DynamicMusic, V2FrameDataPublisher (no on-disk presence at all), plus V2AICommentary and V2AdaptiveAI (present, but as plugins under V2/ue/Plugins/).
  • On disk, absent from the glossary table: the umbrella V2 game module (the target's ExtraModuleNames.Add("V2")), V2Modding (a real UV2ModLoadingSubsystem GameInstance subsystem), and V2World (whose FV2WorldCatalog is annotated "§130 runtime resolution requests/results (deterministic; no RNG)" — the open-world/persistent-world catalog).

The honest reading: the glossary is a forward-looking vocabulary, and the on-disk module set is its current, internally-consistent realization plus two modules (V2World, V2Modding) the glossary has not yet absorbed.

Surfaces the promise spans#

V2 is one game, but the product reaches beyond V2/ue/. Verified repository surfaces:

Surface Path Notes
Game client V2/ue/ UE5.5 project, 27 modules, 5 plugin dirs
Marketing / wiki tile apps/oshun/web/src/app/v2/ glossary, roadmap, wiki, page.tsx
Standalone web surfaces apps/v2/web/ 16 subtrees (balance, calendar, community, dev-portal, e2e, …)
Online services apps/v2/ 91 service directories (cloud back end)
Balance authoring V2/balance/ 30 category trees (ai, audio, battle-royale, cinema, codex, …)
Esports / doc tooling V2/tools/ esports/ toolkit + validate-v2-docs.py
Legal / compliance V2/legal/ rating-boards.json, platform-cert-bans.json, sub-processors.md
Engineering docs V2/docs/ 34 entries (accessibility, asset-pipeline, audio, …)

The breadth is itself part of promise #11: the live-ops, store, and community back ends (./live-ops-store-progression-and-community.md), the esports/companion tooling (./esports-companion-and-ai-services.md), and the racing surfaces (./racing-and-vehicle-architecture.md) all hang off the same shared spine rather than forking it.

How this connects to neighbouring systems#

This page is intentionally shallow on subsystem detail — it owns the promise and the notation, and hands off everywhere else. The combat core and its determinism rules are ./combat-system-gas-frame-data-and-determinism.md; how inputs become deterministic frames is ./animation-and-input-pipeline.md; the netcode tiering above is detailed in ./rollback-netcode-and-tag-team.md. The single-player and special surfaces — training/replay, presentation, UI/VR/AR accessibility, and the open-world/co-op modes — are ./game-modes-training-and-replay.md, ./presentation-av-and-signature-content.md, ./ui-hud-vr-ar-and-accessibility.md, and ./open-world-coop-and-special-modes.md. When you see a §N in any of those pages, this page's convention tells you exactly where it points and how it is checked.