// Package playermapping defines the durable mapping between platform // users and engine player handles owned by Game Master. // // One PlayerMapping mirrors one row of the `player_mappings` PostgreSQL // table (see // `galaxy/gamemaster/internal/adapters/postgres/migrations/00001_init.sql`). // The composite primary key `(game_id, user_id)` and the unique // `(game_id, race_name)` index live in the SQL schema; the domain model // captures the per-row invariants enforced from the application side. package playermapping import ( "errors" "fmt" "strings" "time" ) // PlayerMapping stores one (game_id, user_id) → (race_name, // engine_player_uuid) projection installed at register-runtime. type PlayerMapping struct { // GameID identifies the game owning this mapping. GameID string // UserID identifies the platform user this mapping refers to. UserID string // RaceName stores the in-game race name reserved for the user in // the original casing presented by the engine. RaceName string // EnginePlayerUUID stores the engine-side player handle returned by // the engine /admin/init response. EnginePlayerUUID string // CreatedAt stores the wall-clock at which the row was inserted. CreatedAt time.Time } // Validate reports whether mapping satisfies the player-mapping // invariants implied by the README §Persistence Layout / player_mappings // columns and the SQL primary-key + unique-index constraints. func (mapping PlayerMapping) Validate() error { if strings.TrimSpace(mapping.GameID) == "" { return fmt.Errorf("game id must not be empty") } if strings.TrimSpace(mapping.UserID) == "" { return fmt.Errorf("user id must not be empty") } if strings.TrimSpace(mapping.RaceName) == "" { return fmt.Errorf("race name must not be empty") } if strings.TrimSpace(mapping.EnginePlayerUUID) == "" { return fmt.Errorf("engine player uuid must not be empty") } if mapping.CreatedAt.IsZero() { return fmt.Errorf("created at must not be zero") } return nil } // ErrNotFound reports that a player-mapping lookup failed because no // matching row exists. var ErrNotFound = errors.New("player mapping not found") // ErrConflict reports that a player-mapping insert could not be applied // because a row with the same `(game_id, user_id)` primary key or with // the same `(game_id, race_name)` unique pair already exists. Adapters // surface PostgreSQL unique-violations through this sentinel so the // service layer maps it to a `conflict` REST envelope. var ErrConflict = errors.New("player mapping already exists")