Files
suaveplan/openspec/changes/enterprise-gaming-game-state-schemas/proposal.md
2026-05-27 23:29:53 +01:00

4.4 KiB

OpenSpec Change: enterprise-gaming-game-state-schemas

  • Change ID: enterprise-gaming-game-state-schemas
  • Package: @suaveplan/game-state-schemas
  • Category: gaming/engine
  • Target path: gaming/engine/game-state-schemas
  • Status: PROPOSED
  • Date: 2026-04-24
  • Reference spec: /mnt/s/web/suaveplan/enterprise/game-state-schemas.md

Summary

Deliver @suaveplan/game-state-schemas on the genesis branch from scratch at enterprise quality. The package provides bit-level quantization schemas for multiplayer state compression — essential for reducing network bandwidth in real-time games. A BitPacker encodes typed fields (bool, uint, int, float, enum, string) into compact binary buffers using field descriptors. A StateSchema composes multiple BitField descriptors into a versioned schema with encode/decode round-trips. Genesis adds Zod schemas for all descriptor types, @suaveplan/serialize for framing, structured errors, OTel bandwidth gauges, and 100% coverage with round-trip property tests.


Current-state audit (genesis)

Package does not exist on genesis yet — fresh build.


Available workspace deps on genesis

Package Role
@suaveplan/error (workspace:^) GameStateError; GAME_STATE.* codes
@suaveplan/types (workspace:^) Shared primitives
@suaveplan/serialize (workspace:^) Framing for state snapshots
zod (catalog:forms) Field descriptor schemas
@opentelemetry/api (catalog:observability) Bandwidth histogram

Public API surface

// Field descriptors
type BitField =
  | { type: 'bool'; name: string }
  | { type: 'uint'; name: string; bits: number }
  | { type: 'int'; name: string; bits: number }
  | { type: 'float'; name: string; bits: number; min: number; max: number }
  | { type: 'enum'; name: string; values: string[] }
  | { type: 'string'; name: string; maxBytes: number }

// Schema
function defineStateSchema<T extends Record<string, unknown>>(
  name: string,
  version: number,
  fields: BitField[]
): StateSchema<T>

interface StateSchema<T> {
  readonly name: string
  readonly version: number
  readonly bitsPerFrame: number
  encode(state: T): Uint8Array
  decode(buffer: Uint8Array): T
  patch(prev: T, next: T): Uint8Array      // delta encoding — only changed fields
  applyPatch(prev: T, patch: Uint8Array): T
}

// BitPacker (low-level, exposed for custom schemas)
function createBitPacker(capacityBits: number): BitPacker

interface BitPacker {
  writeBool(value: boolean): void
  writeUint(value: number, bits: number): void
  writeInt(value: number, bits: number): void
  writeFloat(value: number, bits: number, min: number, max: number): void
  toBuffer(): Uint8Array
}

function createBitReader(buffer: Uint8Array): BitReader

interface BitReader {
  readBool(): boolean
  readUint(bits: number): number
  readInt(bits: number): number
  readFloat(bits: number, min: number, max: number): number
  readonly bytesRead: number
}

Error codes

type GameStateErrorCode =
  | 'GAME_STATE_BUFFER_OVERFLOW'
  | 'GAME_STATE_DECODE_FAILED'
  | 'GAME_STATE_SCHEMA_MISMATCH'
  | 'GAME_STATE_INVALID_FIELD'

New additions for genesis

  1. Delta/patch encoding — patch() writes only fields that differ; applyPatch() merges on receiver side.
  2. Float quantization — normalised float mapped to N-bit fixed-point; round-trip error bounded by (max-min) / 2^bits.
  3. OTel — game_state.encode_bytes histogram; game_state.decode_duration_ms histogram.
  4. Property tests — fast-check: decode(encode(state)) deepEquals state for all field types.

File layout

gaming/engine/game-state-schemas/
  package.json
  tsconfig.json
  vitest.config.ts
  src/
    index.ts
    error-codes.ts
    constants/index.ts
    types/common.ts
    types/common.md
    core/
      bit-packer/
        bit-packer.ts
        bit-packer.md
        bit-packer.types.ts
        bit-packer.test.ts
        index.ts
      state-schema/
        state-schema.ts
        state-schema.md
        state-schema.types.ts
        state-schema.test.ts
        index.ts
  tests/
    property.test.ts

Impact

Downstream: @suaveplan/r3f-networking uses state schemas for delta sync; @suaveplan/character-vehicle-picker uses BitPacker for catalog compression. Prerequisites: @suaveplan/error, @suaveplan/types, @suaveplan/serialize.