Files
2026-05-27 23:29:53 +01:00

4.8 KiB

OpenSpec Change: enterprise-gaming-building

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

Summary

Deliver @suaveplan/building on the genesis branch from scratch at enterprise quality. The package provides a player building system with snap placement, structural integrity simulation, and blueprint serialization. A BuildingPieceRegistry holds typed piece descriptors (walls, floors, roofs, foundations); a PlacementEngine validates snap socket connections and surface collisions; a StructureGraph tracks piece adjacency for structural integrity (floating pieces detected by DFS); Blueprint captures a structure as a serializable set of piece instances. Genesis adds Zod piece schemas, structured errors, OTel placement counter, and 100% coverage.


Current-state audit (genesis)

Package does not exist on genesis yet — fresh build.


Available workspace deps on genesis

Package Role
@suaveplan/error (workspace:^) BuildingError; BUILDING.* codes
@suaveplan/types (workspace:^) IdGenerator
@suaveplan/events (workspace:^) building:placed, building:removed, building:unstable events
@suaveplan/serialize (workspace:^) Blueprint serialization
zod (catalog:forms) Piece descriptor + blueprint schemas
@opentelemetry/api (catalog:observability) Piece count gauge, placement counter

Public API surface

type PieceCategory = 'foundation' | 'wall' | 'floor' | 'roof' | 'door' | 'window' | 'stair' | 'decoration'

interface SnapSocket {
  id: string
  position: Vec3           // local space offset from piece center
  rotation: Quat
  acceptsCategories: PieceCategory[]
}

interface PieceDescriptor {
  id: string
  name: string
  category: PieceCategory
  sockets: SnapSocket[]
  bounds: { min: Vec3; max: Vec3 }
  health: number
  resourceCost?: Record<string, number>
}

function createBuildingPieceRegistry(): BuildingPieceRegistry
interface BuildingPieceRegistry {
  register(piece: PieceDescriptor): void
  get(id: string): PieceDescriptor | undefined
  listByCategory(category: PieceCategory): PieceDescriptor[]
}

interface PieceInstance {
  instanceId: string
  pieceId: string
  position: Vec3
  rotation: Quat
  health: number
  connectedTo: string[]   // instanceIds of adjacent pieces
}

function createPlacementEngine(registry: BuildingPieceRegistry): PlacementEngine

interface PlacementEngine {
  findSnap(piece: PieceDescriptor, cursorPosition: Vec3, existingPieces: PieceInstance[]): SnapResult | null
  validatePlacement(pieceId: string, position: Vec3, rotation: Quat, existingPieces: PieceInstance[]): ValidationResult
  place(pieceId: string, position: Vec3, rotation: Quat): PieceInstance
}

interface SnapResult {
  position: Vec3
  rotation: Quat
  socketId: string
  targetInstanceId: string
}

interface ValidationResult {
  valid: boolean
  reason?: string
}

function createStructureGraph(): StructureGraph
interface StructureGraph {
  addPiece(instance: PieceInstance): void
  removePiece(instanceId: string): void
  getUnstable(): string[]    // instanceIds with no path to foundation
  isStable(): boolean
}

// Blueprint
function captureBlueprint(pieces: PieceInstance[]): Blueprint
function applyBlueprint(blueprint: Blueprint, origin: Vec3): PieceInstance[]

interface Blueprint {
  id: string
  name: string
  pieces: Array<{ pieceId: string; relativePosition: Vec3; rotation: Quat }>
  createdAt: number
}

Error codes

type BuildingErrorCode =
  | 'BUILDING_PIECE_NOT_FOUND'
  | 'BUILDING_PLACEMENT_INVALID'
  | 'BUILDING_NO_FOUNDATION'
  | 'BUILDING_UNSTABLE'
  | 'BUILDING_SOCKET_MISMATCH'

File layout

gaming/world/building/
  package.json
  tsconfig.json
  vitest.config.ts
  src/
    index.ts
    error-codes.ts
    constants/index.ts
    types/common.ts
    types/common.md
    core/
      piece-registry/
        piece-registry.ts
        piece-registry.md
        piece-registry.test.ts
        index.ts
      placement-engine/
        placement-engine.ts
        placement-engine.md
        placement-engine.types.ts
        placement-engine.test.ts
        index.ts
      structure-graph/
        structure-graph.ts
        structure-graph.md
        structure-graph.types.ts
        structure-graph.test.ts
        index.ts
      blueprint/
        blueprint.ts
        blueprint.md
        blueprint.types.ts
        blueprint.test.ts
        index.ts
  tests/
    integration.test.ts

Impact

Downstream: @suaveplan/r3f-building renders ghost preview and placed pieces. Prerequisites: @suaveplan/error, @suaveplan/types, @suaveplan/events, @suaveplan/serialize.