Chuyển tới nội dung chính

Add a presenter

A feature reaches the Client only as a presenter. Build it as one vertical slice, in this order.

1. Server contract​

In projectz-nakama (v3): add the operation, DTOs, error codes, auth / mutation / idempotency metadata, the projection policy and literal request/success/error/replay fixtures. Regenerate the contract artifacts with tools/contract-sync.sh; CI publishes the contract bundle. Tag the contract revision (contract/<major>.<minor>) when it changes.

2. Import the contract​

In ProjectV-SDK, import the validated bundle with ci/import-contract-bundle.sh (through the typed generator) and run ci/run-generator-gates.sh: the backend-dtos . --check drift check and the normalized contract gate. tools/ContractGuard fails on a missing, extra, renamed or metadata-mismatched operation.

3. Typed client​

In com.silvertiger.projectv.client: backend-neutral DTOs, a strict decoder, the typed capability client and its state slot / projection policy. Async surface uses Task; no Unity, no raw RPC, no client-side arithmetic for server-owned values.

4. Nakama binding​

In com.silvertiger.projectv.client.nakama: bind the typed operation to the Nakama transport and register it in the internal capability clients. Never add a generic RPC method as a shortcut.

5. Presenter​

In com.silvertiger.projectv.unity/Runtime/Presentation/<Feature>/ follow the existing layout (Shop is a complete example):

FileVisibilityContent
ProjectV<Feature>Contracts.cspublicIProjectV<Feature>Presenter, snapshot, status enum, error, command result
ProjectV<Feature>Snapshots.cspublicImmutable view types used by the snapshot
IProjectV<Feature>Backend.csinternalPort the presenter calls
ProjectV<Feature>SdkAdapter.csinternalImplements the port over the typed client and state store
ProjectV<Feature>Presenter.csinternalSingle-flight commands, snapshot publication, recovery state

Snapshot rules: immutable; Status + data + Error (+ Recovery on data screens); safe player text in SafeMessage or a LocalizationKey; typed IDs instead of raw strings.

Register the presenter in ProjectVPresentationLifetimeScope (com.silvertiger.projectv.integrations.vcontainer) as the interface only.

6. Screen contract​

A page-facing feature also gets a screen contract in ProjectV<Feature>Screen.cs (Shop and Campaign are small examples):

TypeVisibilityContent
ProjectV<Feature>ViewpublicImmutable view: IsLoading, LoadError and the confirmed data
IProjectV<Feature>ScreenpublicCurrent, Observe(render) and the commands, each returning ProjectVResult
ProjectV<Feature>ScreeninternalBuilt on ProjectVScreenCore<TView>: joins repeated commands, dispatches renders, keeps the last confirmed view
ProjectVScreens.<Feature>(presenter)publicFactory for hosts without VContainer and for tests

Rules: render runs only when the view data changes, so compare the data by reference and keep the shown view when a read answers from the cache. ProjectVResult.Message is player-facing text only. Register the screen as a singleton next to its presenter.

7. Tests​

Add tests for stale, duplicate, logout, replay, projection and failure paths, and run the suites with ci/run-projectv-tests.sh (the .local/ roots from Local development). The normal mutation path must be one RPC with zero follow-up reads.

8. Release and adopt​

Build a candidate and release it through BuildOps (Release pipeline), then pin it in the Client. Add the Client screen only against a released package, and add a page to the Presenter reference in the same change as the presenter (see Documentation standard).

Done criteria​

  • Contract guard and generator gates pass against the intended server revision.
  • Unknown, duplicate, malformed and incomplete projections fail closed.
  • Replays return the same projections.
  • The public API adds only the presenter and screen contracts and their snapshot and view types; no raw RPC, reflection activation or service locator.
  • The request budget in Architecture holds.
  • The presenter page exists and npm run build in website/ passes.