Fighting Game · Architecture

Subsystem Glossary, Module Map & Runtime Topology

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

6sections12 minread2diagrams1table

On this page

This is the orientation map for V2 — the page you read before any of the deep architecture pages, so that every later reference to "the V2Combat module" or "the dedicated-server target" or "the gRPC adapter layer" lands on a name you already trust. V2 is a frame-deterministic fighting game built on Unreal Engine 5.5, and its module split is not cosmetic packaging: it exists so that frame-critical combat stays in C++ behind an explicitly declared, acyclic dependency graph, so that a headless dedicated server can drop every rendering and audio module without touching gameplay, and so that the build itself can be compiled with strict floating-point and unity builds disabled for bit-stable results. The single source of truth is the engine project at V2/ue/: its .uproject enumerates 27 runtime/editor/tool C++ modules plus a curated plugin set, and each module under V2/ue/Source/ carries its own *.Build.cs declaring exactly what it links. This page reconciles that on-disk reality with the prose glossary in the architecture monolith, and names the surrounding TypeScript services and web surfaces that the game talks to. It is the companion to the catalogue at ../V2_ARCHITECTURE.md.

The reason to read the code rather than the glossary table is that the two have drifted. The monolith's "Subsystem Glossary" lists 30 engine modules; the disk has 27, and the overlap is imperfect in both directions. Where they disagree, this page treats the .uproject and the Build.cs files as authoritative and the glossary as the aspirational spec — and says so each time, because an honest "the glossary names a module that has no code yet" is worth more than a confident restatement of a table that the compiler would reject.

What ships, honestly#

Real and on-disk (the compiler agrees): the 27 C++ modules under V2/ue/Source/ are all present, each with a *.Build.cs, a Public//Private/ split, and a module class; the .uproject Modules array lists the same 27 names with explicit loading phases (verified: 27 source directories == 27 .uproject entries). The fighting-game spine is substantial rather than skeletal — V2Gameplay carries ~218 source and header files, V2UI ~172, V2Combat 36, and V2Tests holds 424 .cpp files — 30 module automation specs under V2Tests/Automation/ (24 of them *.Module.spec.cpp) plus a large Private/ suite of Gauntlet-style bot harnesses (V2CombatBot, V2BotHarness, V2QuestCompletionBot, V2RandomExplorationBot, V2RLTrainedExplorationAgent, V2BotCoverageMapping), which is what backs the glossary's "gauntlet drivers" line. The build targets are real and deterministic (V2.Target.cs, V2Server.Target.cs, V2Editor.Target.cs). Three project plugins ship with working modules (V2AdaptiveAI, V2AICommentary, V2AssetLinter) plus the synced BellonaUnrealEditor. V2Services contains a genuine gRPC adapter layer (V2GrpcChannelManager, generated stubs). Outside the engine, 91 service packages under apps/v2/ are real Nx libraries (all 91 have a project.json), and the web tile at apps/oshun/web/src/app/v2/ and the standalone site at apps/v2/web/ both exist.

Glossary drift (spec-only or mislabelled), found by reading the disk:

  • Four glossary modules have no code. V2Peripherals, V2AntiCheat, V2DynamicMusic, and V2FrameDataPublisher appear in the monolith's engine- module table but are not directories under V2/ue/Source/ and are not plugins. Treat them as named-but-unbuilt.
  • Two glossary "modules" are actually plugins. V2AdaptiveAI and V2AICommentary are listed as engine modules but live as GameFeature/runtime plugins under V2/ue/Plugins/, which is why V2Gameplay can take a hard module dependency on V2AdaptiveAI (V2Gameplay.Build.cs:20) — a Source module is allowed to depend on a plugin module.
  • Two real modules are missing from the glossary. V2World (persistent / open-world catalogue) and V2Modding (mod loading) are fully present on disk with Build.cs files but appear nowhere in the glossary table (a grep for V2World/V2Modding in the monolith returns nothing).
  • The per-mode plugins do not exist. The ~90 V2Mode_*, V2Event_*, and V2RaceMode_* rows are a planned GameFeature surface; mode identity today lives only in the V2Modes registry module. The monolith's own disk-status note (2026-06-12) already concedes this.
  • Plugins/V2Editor/ is a vestigial folder — it contains only a Resources directory, no .uplugin and no module. The real editor tooling is the Source module V2/ue/Source/V2Editor/, which the .uproject loads as an Editor-type module. Do not mistake the empty plugin folder for a plugin.
  • The Module Split diagram's arrows are partly inverted (detailed below), and its stated rule "V2UI does not depend on V2Netcode directly" is false in codeV2UI.Build.cs lists V2Netcode as a public dependency.
  • V2 has no central Zod contracts. Unlike V1 (whose domain contracts live in libs/contracts/src/<domain>), there is no libs/contracts/src/v2. V2's data contracts live in UE C++ UDataAsset/DataTable types and in per-service TypeScript schemas, not a shared contracts library.

Everything below is grounded in those files; where a claim is the glossary's intent rather than shipped code, it is labelled.

The module graph as it is built#

The .uproject declares the load order, and that order encodes the layering. V2Core and V2Gameplay load in the PreDefault phase (V2.uproject:9–17); everything else loads in Default, except V2Editor (Editor type) and V2Tests (DeveloperTool type). PreDefault for V2Core matters because V2Core owns the native gameplay-tag registry (V2CoreGameplayTags) and the engine/game-instance subsystems that later modules query during their own startup; V2Gameplay is hoisted alongside it because it is the GAS spine that combat, modes, and UI all build on.

Reading every *.Build.cs and extracting the V2* dependencies yields a clean directed acyclic graph — the monolith's "no circular deps" claim holds in code. But the shape differs from the monolith's Module Split mermaid in several load-bearing edges. The diagram below is the graph as actually declared, grouped by layer; corrected edges are called out after it.

flowchart TB subgraph FOUND["Foundation (no V2 deps)"] direction LR Core[V2Core] Audio[V2Audio] VFX[V2VFX] Online[V2OnlineServices] end subgraph SPINE["Fighting-game spine"] direction LR Input[V2Input] Gameplay[V2Gameplay] Combat[V2Combat] Anim[V2Animation] Net[V2Netcode] Modes[V2Modes] UI[V2UI] end subgraph SAT["Satellites"] direction LR Cine[V2Cinematics] Persist[V2Persistence] Tele[V2Telemetry] World[V2World] Modding[V2Modding] Svc[V2Services] end AdaptiveAI[[V2AdaptiveAI plugin]] Input --> Core Gameplay --> Core Gameplay --> Input Gameplay --> AdaptiveAI Combat --> Core Combat --> Gameplay Anim --> Combat Net --> Combat Net --> Input Modes --> Gameplay Modes --> Net UI --> Combat UI --> Modes UI --> Net UI --> Modding Cine --> Audio Cine --> VFX Persist --> AdaptiveAI Persist --> Input Tele --> Net World --> Online World --> Persist World --> Tele Modding --> Core Svc --> Core

Corrections versus the monolith's Module Split diagram — each verified against the relevant Build.cs:

  • Animation depends on Combat, not the reverse. The monolith draws Combat → Animation; V2Animation.Build.cs declares V2Combat (Animation reads frame data to drive motion). Likewise the monolith's Animation → Audio / Animation → VFX edges do not exist — V2Audio and V2VFX have zero V2* dependencies. It is V2Cinematics that depends on V2Audio and V2VFX.
  • Netcode depends on Combat, not the reverse. The monolith draws Combat → Netcode; V2Netcode.Build.cs declares V2Combat and V2Input (the rollback simulation re-runs combat). V2Telemetry in turn depends on V2Netcode, so analytics can tap rollback and network-quality events.
  • UI reaches deep, including into Netcode. V2UI.Build.cs publicly depends on V2Combat, V2Gameplay, V2Input, V2Modes, V2Modding, and V2Netcode — directly contradicting the glossary rule that uses this exact pair as its example. In practice the HUD needs rollback/connection state, so the dependency is real; the rule is the thing that is wrong.
  • OnlineServices is a leaf; World is the aggregator. The monolith draws Online → Persistence and Online → Telemetry; in code V2OnlineServices has no V2* dependencies, and it is the (glossary-absent) V2World module that depends on V2OnlineServices, V2Persistence, and V2Telemetry.
  • Modes is leaner than drawn, and server-aware. V2Modes depends on V2Core/V2Input/V2Netcode (public) and V2Combat/V2Gameplay (private), and adds V2Animation only on non-server targets (if (Target.Type != TargetType.Server) in V2Modes.Build.cs). It does not depend on V2UI, V2Cinematics, or V2Persistence as the monolith suggests.

The composition root: the V2 module#

The module named simply V2 (V2/ue/Source/V2/, six files) is the composition root — the one module the Game and Server targets actually name via ExtraModuleNames.Add("V2") (V2.Target.cs:14). It hosts the concrete classes that fuse the spine together: AV2CombatCharacter and AV2GameMode. Its Build.cs is the cleanest statement of the runtime topology: it publicly depends on the gameplay/combat/modes/netcode/persistence/telemetry/world/racing modules unconditionally, and then wraps the presentation modules — V2Animation, V2Audio, V2Cinematics, V2Input, V2UI, V2VFX, V2VehicleAudio, V2VehicleVFX — in if (Target.Type != TargetType.Server). A dedicated server therefore never links animation, audio, UI, or VFX at all; the simulation half of the game compiles and runs without them. This is the module-level expression of the headless-server design that the netcode page relies on.

Plugins: engine, project, and the empty folder#

V2.uproject sets "DisableEnginePluginsByDefault": true (V2.uproject:6), so every plugin the game uses is enabled explicitly — there are 35 plugin entries. Thirty-one are stock engine plugins, and they read like a bill of materials for the genre: GameplayAbilities (GAS), EnhancedInput, CommonUI + ModelViewViewModel (UI/MVVM), Metasound + AudioModulation + AudioGameplayVolume (audio), Niagara (VFX), the animation stack (MotionWarping, AnimationWarping, PoseSearch, IKRig, Mover, Chooser, ACLPlugin), the deformer/grooming set (ChaosFlesh, ChaosClothAsset, HairStrands, AlembicHairImporter), the networking set (ReplicationGraph, NetworkPrediction, Iris), GameFeatures + ModularGameplay (the basis for the planned per-mode plugins), OnlineServices + OnlineServicesEOS, and Gauntlet + TraceSourceFilters for automation and tracing.

Four entries are project plugins. V2AdaptiveAI (runtime) and V2AICommentary (runtime) carry real modules — confirmed by their .uplugin Modules blocks — and V2AssetLinter is an Editor-type plugin. BellonaUnrealEditor is the synced editor-consumer plugin from the sister Bellona stack (it has a full Source/, Config/, and Binaries/). As noted above, Plugins/V2Editor/ is not among them — it is an empty Resources folder, and the editor module lives in Source/ instead.

Determinism is a build-target property, not a comment#

A fighting game with rollback netcode has to produce identical simulation results on two machines from the same inputs, and V2 enforces this where it cannot be forgotten — in the target rules. V2.Target.cs (:13–24) sets bUseUnityBuild = false, defines DETERMINISM=1, V2_DETERMINISM=1, and V2_STRICT_FP=1, and appends strict floating-point compiler arguments: /fp:strict /fp:except- on Win64 and -fno-fast-math -ffp-contract=off elsewhere (GetV2StrictFloatingPointCompilerArguments). The same deterministic block is duplicated in V2Server.Target.cs so the server simulates identically. These are not decorative: determinism markers appear throughout the modules that need them — V2Combat (V2CombatResolver, V2CollisionAuthorityComponent, V2CombatTypes, V2CombatRulesetData) and V2Netcode (V2RollbackSession, V2RollbackSimWorld, V2RollbackTransport, V2NetworkQuality). The full treatment lives in combat & determinism and rollback netcode; the point here is topological: determinism is a property the build target stamps onto every module, which is why the module split keeps anything frame-critical out of Blueprint and inside these C++ modules.

V2Server.Target.cs then carves out the dedicated-server slice (:40–69): it sets bBuildWithEditorOnlyData = false, defines a wall of server flags (V2_HEADLESS_SERVER, V2_NO_RENDERER, V2_NO_AUDIO, V2_DEDICATED_SERVER, Linux/Win64 binary-pipeline variants), and disables the rendering/audio engine plugins outrightAudioGameplayVolume, AudioModulation, CommonUI, Metasound, Niagara. Combined with the V2 module's presentation-slicing, the server target is a genuinely thin headless build, not a full client with the window hidden.

Runtime topology: client, dedicated server, services, web#

Three UE targets and two non-engine surface families make up the running system.

flowchart LR subgraph CLIENT["V2 Game Client (V2.Target.cs)"] spine["27 modules · GAS · rollback · CommonUI · Metasound · Niagara"] end subgraph SERVER["Dedicated Server (V2Server.Target.cs)"] hl["headless sim · no renderer/audio · rollback authority"] end svcbridge["V2Services module<br/><sub>gRPC channel mgr + generated stubs (HTTP/JSON)</sub>"] subgraph BACKEND["apps/v2 — 91 Nx packages"] direction LR tele[telemetry-*] online[matchmaking / seasons] live[live-ops / store / economy] ac[nous-anti-cheat-classifiers] end subgraph WEB["Web surfaces"] direction LR tile["apps/oshun/web/.../v2 (shell tile)"] standalone["apps/v2/web (standalone SPA)"] esports["@v2/esports-tools"] end CLIENT -- inputs/replays --> SERVER CLIENT --> svcbridge SERVER --> svcbridge svcbridge -- proto-generated --> BACKEND BACKEND --> WEB EOS[(EOS / OnlineServices plugins)] --- CLIENT EOS --- SERVER

The bridge from C++ to the TypeScript backend is the V2Services module. Its public surface is V2GrpcChannelManager, V2OshunDomainAdapters, and V2ServiceTypes, backed by Generated/V2GeneratedGrpcStubs.h. Those stubs are not hand-written: both targets register a PreBuildStep that runs pnpm --filter @oshun/codegen run v2:grpc-stubs (V2.Target.cs:37–47, V2Server.Target.cs:71–81), so the .proto definitions under libs/proto are regenerated into C++ on every build. V2Services.Build.cs adds the proto and generated include paths plus Json, JsonUtilities, and HTTP, which is how the module speaks to the services over HTTP/JSON-framed gRPC. Login, parties, and matchmaking ride the engine's OnlineServices + OnlineServicesEOS plugins on both client and server.

The service backbone#

apps/v2/ holds 91 packages, each a real Nx library (package.json + project.json + src/ + vitest.config.ts). They are not uniform in depth — telemetry-ingestion/src is ~1,000 lines, while a focused service like nous-anti-cheat-classifiers/src is ~170 — but every one is a buildable TypeScript package, not a placeholder folder. They cluster into the families the monolith's later sections describe: a large telemetry group (telemetry-ingestion, -data-pipeline, -schema-migrations, -privacy-compliance, -session-reconstruction, and more), live-ops & store (season-pass-service, dlc-content-delivery-service, limited-time-game-mode-service, economy-analytics, message-of-the-day-service), competitive integrity (nous-anti-cheat-classifiers, themis-* governance/dispute/DSR), and the sister-monorepo adapters that bridge V2 to other Oshun deities (oshun-adapter, oshun-identity-binding, hathor-npc-adapter, iris-realtime-translation, psyche-ai-director-hints, maat-finance-ledger). These are explored per-domain in online backbone & competitive integrity, live-ops, store & community, esports, companion & AI services, and telemetry, performance & release gates.

Web and companion surfaces#

Two web homes exist and neither replaces the other. apps/oshun/web/src/app/v2/ is the in-shell product tile: page.tsx renders a single <V2ShellSurface/> component, with glossary/, roadmap/, and wiki/ subroutes — the V2 entry inside the shared Oshun Next.js shell. apps/v2/web/ is the standalone marketing / community / dev-portal SPA (a Vite app with calendar/, community/, support/, esports/, dev-portal/, hub/, legal/, live-service/, and balance/ sub-sites). The esports toolkit is a separate package, @v2/esports-tools, at apps/v2/esports-tools/. Note that V2/tools/ otherwise contains only that toolkit and a validate-v2-docs.py linter — the glossary's "Build, release, esports, mocap, data tooling" is mostly aspirational, and V2/tools/release/ does not exist (the monolith already marks it "planned"). Balance authoring is its own tree at V2/balance/ (30 subdirectories: fighters/, racing/, stages/, feel/, progression/, …) that feeds DataTables; see build, cook, assets & data.

Glossary reconciliation table#

The fastest way to use the monolith glossary safely is to read it through this column. "Module" = a directory under V2/ue/Source/ with a Build.cs; "Plugin" = a module under V2/ue/Plugins/; "Service" = an Nx package under apps/v2/; "Spec" = named but not present in code.

Glossary name On disk as
V2Core … V2Telemetry Module (each present)
V2Racing / V2Vehicles / V2RacePhysics / V2RaceTracks / V2RaceModes / V2VehicleAudio / V2VehicleVFX Module (each present)
V2Editor Module (Source); the Plugins/V2Editor/ folder is empty
V2Tests Module (DeveloperTool; 424 .cpp = 30 automation specs + bot harnesses)
V2Services Module (gRPC adapter layer)
V2AdaptiveAI Plugin (runtime) — not a Source module
V2AICommentary Plugin (runtime) — not a Source module
V2AssetLinter (not in glossary table) Plugin (editor)
V2World / V2Modding (not in glossary) Module (both present)
V2Peripherals Spec — no module/plugin
V2AntiCheat Spec as a module; the nous-anti-cheat-classifiers Service is the present-day surface (no EAC/BattlEye plugin is enabled in the .uproject)
V2DynamicMusic Spec — no dedicated module; the audio subsystem is V2Audio, and euterpe-commentary-ducking is a present Service
V2FrameDataPublisher Spec — frame data lives in V2Combat DataAssets; no publisher module
V2Mode_* / V2Event_* / V2RaceMode_* Spec — registry-only in V2Modes; planned GameFeature plugins

Where to go next#