Architecture
Layers
ProjectV Client: scenes, views, gameplay
-> IProjectV*Presenter + snapshots com.silvertiger.projectv.unity
(VContainer scope) com.silvertiger.projectv.integrations.vcontainer
(native Google sign-in) com.silvertiger.projectv.integrations.google
-> ProjectV contracts, admission, state store com.silvertiger.projectv.client
-> ProjectV Nakama binding com.silvertiger.projectv.client.nakama
-> Shared SDK com.silvertiger.crosssdk.{core,contracts,auth,state,
readmodel,lobby,realtime,nakama,unity}
-> ProjectV Nakama V3 (projectz-nakama v3 + nakama-core-module)
| Package | Responsibility |
|---|---|
projectv.client | Backend-neutral contracts, ProjectVAdmissionGate, ProjectVStateStore, typed capability clients, composition and lifecycle. No Unity, no raw RPC |
projectv.client.nakama | The only package that binds typed operations to Nakama transport; strict decoders; mission realtime binding. Exports no public consumer types |
projectv.unity | Standard Unity runtime: ProjectVSettings, ProjectVSdkHost, presenters, Setup tooling, diagnostics |
projectv.integrations.vcontainer | ProjectVPresentationLifetimeScope and the scene-scope bases. Borrows the root; owns no infrastructure |
projectv.integrations.google | Native Android/iOS and Editor Google authentication |
projectv.integrations.purchasing | Unity IAP v5 orchestration; not in the current Client profile (IAP disabled) |
Only SilverTiger.ProjectV.Unity and SilverTiger.ProjectV.Unity.VContainer
are auto-referenced by consumers; every other assembly is explicitly
referenced, so game code cannot reach it by accident.
Runtime sequence
ProjectVSdkHost (one persistent root) builds the runtime from ProjectVSettings
-> StartAsync: admission (app/config, app/version) — fail closed on
maintenance, update, contract/capability drift
-> restore secure session (optional) or AwaitingLogin
-> authenticate (device, password, Google, Apple)
-> hydrate with one player/sync: every syncable snapshot into the read-model cache
-> load the mission and character catalogs in the background
-> realtime only when a feature needs it
-> presenters publish snapshots; screens render only changed views;
commands return projected state
-> on returning to the foreground: player/sync again
-> LogoutAsync: stop realtime, clear state, logout session
DisposeAsync ends the runtime's lifetime, not the account session; a scene
unload is not a logout.
State
ProjectVStateStore keeps immutable, revisioned per-capability snapshots and
the shared ISessionEpochOwner. Refreshes coalesce; reads and mutations
capture the session epoch; logout advances it before any await, so results
for an old account are dropped. Mutations apply complete server projections in
one batch (see Mutation projections).
Server reads go through the Shared SDK read-model cache
(SessionReadModelStore). Each read model has a descriptor in
ProjectVCacheDescriptors with a fresh and a stale window taken from the
backend contract's read_models: five minutes fresh for the player
capabilities that every mutation projects (characters, loadout, progression,
skills, equipment, skins, economy, inventory, mission status, progress and
history), a day for the character catalog and the mission catalog payload
(whose manifest is checked every five minutes), and shorter windows for
stamina and view state. ProjectVReadModelProjectionSink writes mutation
projections and the player/sync snapshots into the same cache, so a screen
opened afterwards reads them without a request. Concurrent reads of one key
join one fetch.
Request budget
| Action | Requests |
|---|---|
| Startup or foreground hydration | 1 (player/sync), plus background catalog loads after startup |
| Opening Lobby, Campaign or Character within the fresh window | 0; Character reads the skill tree and cast skills on its first open, then keeps them fresh for five minutes |
| Any projected mutation (purchase, upgrade, level-up, mission transition) | 1, no follow-up reads |
| Beginning a mission | 1 (mission/admit), plus a reread of any admission fence older than its fresh window |
A change that adds a request to one of these rows needs a reason in its pull request.
Admission
ProjectVAdmissionGate follows the major.minor contract revision: it admits
a server of the same major whose minor is at least the client's and that still
offers every client capability; a newer major is UpdateRequired; older
servers, schema or capability drift, maintenance and minimum-version updates
fail closed. Readers ignore unknown response members and map unknown enums to
Unknown, so additive server changes keep released clients working.
Extension rule
New features are vertical slices: server operation and contract → typed client and decoder → Nakama binding → projection policy and state slot → presenter and snapshot → tests → binary release → Client UI. See Add a presenter.