neon-sprawl/docs/decomposition/modules/contracts.md

7.3 KiB
Raw Blame History

What a “contract” is (Neon Sprawl)

Decomposition modules and the dependency register use contract as the name for a stable boundary: something two systems (client and server, two server subsystems, or server and content pipeline) can depend on without sharing an undocumented struct.

This doc fixes artifact types, ownership, and versioning so “Contract needed” in the register is actionable.

Baseline stack alignment: tech stack — authoritative server in C#; client in Godot/GDScript; Protobuf for versioned clientserver messages (JSON acceptable only for the earliest spike); JSON/YAML + JSON Schema for content tables. Who owns truth for movement, camera, and combat: client vs server authority.


Contract kinds

Kind Use for Authoritative form Typical consumers
Wire Player intents, state snapshots, RPC payloads crossing the network Protocol Buffer .proto definitions; generated C# on server; mirrored types or codegen path in GDScript client Client runtime, server host
Server-internal Shapes used only inside the ASP.NET process (persistence DTOs, internal queues) until promoted C# records/types in the server solution; behavior covered by tests Server modules only
Content Data-driven defs (SkillDef, RecipeDef, QuestDef, …) JSON or YAML files + JSON Schema (or CUE later) validated in CI Server load; tools; optional client bundles
Telemetry Analytics and ops events (E9.M1) Versioned event catalog (JSON Schema or equivalent) + transport envelope; align names with OpenTelemetry where practical Client emitters, ingest, dashboards
HTTP (optional) Health, admin, or tooling — not the primary game sim path OpenAPI if exposed; keep game loop on Protobuf per tech stack Ops, CI, external tools

A single logical name (e.g. MoveCommand) might appear as a protobuf message on the wire and a different internal server type that maps to/from it; the wire schema is what client and server must agree on. Until Protobuf exists for a message, a spike may use JSON with an explicit throwaway note in PRs and a tracked issue to migrate.


Mapping module “Key contracts” to kinds

Rough default (exceptions per module):

  • Intents and state sync (MoveCommand, PositionState, CombatAction, CombatResolution, …) → Wire (protobuf).
  • Catalog / defs (RecipeDef, QuestDef, ItemDef, SkillDef, …) → Content (files + JSON Schema).
  • Pure server resolution (e.g. internal threat tables, idempotency keys not sent to client) → Server-internal until a client or tool needs them; then promote to wire or document as admin-only HTTP.

When unsure, default to wire protobuf for anything the Godot client must send or interpret; default to content + schema for anything authored as bulk data.


Repository layout (convention)

Paths are conventions until the first codegen lands; adjust in one doc PR when you add the first artifact.

Artifact Planned location Notes
.proto files contracts/proto/ (repo root) Keeps schemas visible to both server and client tooling; generate C# into server/ and client bindings per chosen Godot/protobuf workflow
JSON Schema for content contracts/schemas/content/ (or co-located schema.json next to data) Validate in CI before server boot in strict modes
Telemetry catalog contracts/schemas/telemetry/ or under docs/ until ingest exists Must carry schema / semver for E9.M1

Server-only helpers stay next to features (e.g. NeonSprawl.Server/Game/...) until extracted to a shared assembly if multiple hosts need them.


Naming

  • Register and epics use PascalCase names (CraftRequest, LevelUpEvent). Protobuf messages use the same PascalCase style; protobuf field names follow snake_case in .proto if you adopt common style guides (generated C# still maps to idiomatic names).
  • JSON Schema $id and file names should include a version or folder (v1/) when breaking changes ship.

Versioning and “Ready”

  • Wire: additive changes via new fields and reserved numbers; breaking changes require a new message or a negotiated major bump and client rollout strategy.
  • Content: schema version in CI; server rejects or migrates unknown versions per policy.
  • Telemetry: bump EventSchemaVersion (E9.M1); document in changelog for dashboards.

A modules register Status can move to Ready when the contracts it exports are published in the agreed form (proto checked in, or schema in CI, plus a minimal consumer test or stub).


What this doc does not decide

  • Exact Protobuf package layout and service vs unary choices — decide when the first real RPC is added.
  • Godot protobuf library choice — tooling decision; still must consume the same .proto truth as the server.