Files
galaxy-game/gamemaster/internal/domain/playermapping/model.go
T
2026-05-03 07:59:03 +02:00

72 lines
2.5 KiB
Go

// 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")