--- type: Domain Model title: Game Domain & State Models description: Detailed domain model and state lifecycles for SlipItIn games, rounds, phrase decks, players, challenges, and client offline action queue. tags: [domain, models, state-machine, entity-framework, privacy, offline-queue] openwiki: roles: [domain] change_kinds: [public-api, lifecycle] source_paths: [SlipItIn.Shared/Models/Game.cs, SlipItIn.Shared/Models/Player.cs, SlipItIn/Models/QueuedGameAction.cs, SlipItIn.Shared/DTOs/GameStateDto.cs] symbols: [Game, Player, PlayerCard, SlipChallenge, QueuedGameAction, GameStateDto, PlayerHandDto] validation_commands: ["dotnet build SlipItIn.Shared/SlipItIn.Shared.csproj"] --- # Game Domain & State Models This page describes the core domain entities, relational schema, state machines, offline action queue models, and data transfer objects (DTOs) that form the foundation of the SlipItIn game engine. The domain model [is secured by data privacy models defined in](/openwiki/architecture/overview.md) the system architecture, [defines state lifecycles executed by](/openwiki/workflows/slip-and-challenge.md) real-time game workflows, and [maps domain classes in C# project files indexed in](/openwiki/source-map.md) the source map (`SlipItIn.Shared/Models/`, `SlipItIn.Shared/DTOs/`, and `SlipItIn/Models/`). --- ## 1. Entity Relationship Diagram (ERD) The backend database schema is managed via Entity Framework Core (`SlipItInDbContext`) targeting PostgreSQL. ```mermaid erDiagram User { int Id PK string Username string Email string PasswordHash bool IsActive } Game { int Id PK string LobbyCode int HostId FK GameStatus Status int MaxPlayers int RoundDurationSeconds } Player { int Id PK int UserId FK int GameId FK int Score bool IsReady string ConnectionId } Phrase { int Id PK string Text int CreatorId FK bool IsActive } GameRound { int Id PK int GameId FK int RoundNumber RoundStatus Status } PlayerCard { int Id PK int PlayerId FK int PhraseId FK int GameRoundId FK bool IsUsed } SlipChallenge { int Id PK int GameRoundId FK int ChallengingPlayerId FK int TargetPlayerId FK int TargetCardId FK ChallengeStatus Status } User ||--o{ Game : hosts User ||--o{ Player : participates_as User ||--o{ Phrase : creates Game ||--o{ Player : contains Game ||--o{ GameRound : has Player ||--o{ PlayerCard : holds Phrase ||--o{ PlayerCard : used_in GameRound ||--o{ PlayerCard : active_in GameRound ||--o{ SlipChallenge : contains Player ||--o{ SlipChallenge : challenges Player ||--o{ SlipChallenge : target_of PlayerCard ||--o{ SlipChallenge : targeted_by ``` *Entity Relationship Diagram showing relational database schema for users, games, players, phrases, cards, rounds, and challenges.* --- ## 2. Core Domain Entities (`SlipItIn.Shared/Models`) ### `User` Represents an authenticated account. * **Properties**: `Id`, `Username`, `Email`, `PasswordHash` (BCrypt), `CreatedAt`, `IsActive`. * **Relations**: Navigation properties to created `Game` instances, player entries (`Players`), and created phrases (`Phrases`). ### `Game` Represents a multiplayer game session / lobby. * **Properties**: `Id`, `LobbyCode` (6-char alphanumeric), `HostId`, `Status` (`GameStatus`), `CreatedAt`, `StartedAt`, `EndedAt`, `MaxPlayers` (default 8), `RoundDurationSeconds` (30-60s). * **Relations**: `Host` (`User`), `Players` (`ICollection`), `Rounds` (`ICollection`). ### `Player` Represents a user's participation inside a specific `Game`. * **Properties**: `Id`, `UserId`, `GameId`, `Score`, `SuccessfulSlips`, `FailedSlips`, `JoinedAt`, `IsReady`, `ConnectionId` (active SignalR connection ID). * **Relations**: `User`, `Game`, `Cards` (`ICollection`). ### `Phrase` Represents a secret phrase or sentence created for the game deck. * **Properties**: `Id`, `Text`, `CreatorId`, `CreatedAt`, `IsActive`. * **Relations**: `Creator` (`User`), `PlayerCards` (`ICollection`). ### `GameRound` Tracks a distinct round within a game. * **Properties**: `Id`, `GameId`, `RoundNumber`, `Status` (`RoundStatus`), `StartedAt`, `EndedAt`. * **Relations**: `Game`, `ActiveCards` (`ICollection`), `Challenges` (`ICollection`). ### `PlayerCard` Represents a specific phrase card dealt to a player for a round. * **Properties**: `Id`, `PlayerId`, `PhraseId`, `GameRoundId`, `IsUsed` (boolean flag set when player submits slip), `AssignedAt`. * **Relations**: `Player`, `Phrase`, `GameRound`. ### `SlipChallenge` Represents an accusation made by one player against another player's submitted slip. * **Properties**: `Id`, `GameRoundId`, `ChallengingPlayerId`, `TargetPlayerId`, `TargetCardId`, `Status` (`ChallengeStatus`), `CreatedAt`, `ResolvedAt`. * **Relations**: `GameRound`, `ChallengingPlayer`, `TargetPlayer`, `TargetCard`. --- ## 3. Client State & Offline Action Queue Models (`SlipItIn/Models`) ### `QueuedGameAction` Represents a game action (such as `SubmitSlip` or `ChallengeSlip`) initiated on the client while offline (`!SignalRService.IsConnected`). * **Properties**: * `Method`: Target SignalR hub method name (e.g. `"SubmitSlip"`, `"ChallengeSlip"`). * `ArgumentsJson`: JSON serialized array of method arguments. * `CreatedAtUtc`: Timestamp recording when the action was taken. * **Lifecycle**: Enqueued into `Preferences` via `GameStateService.EnqueueActionAsync()`, replayed sequentially upon reconnection via `GameStateService.FlushQueuedActionsAsync()`, and removed from storage. --- ## 4. Enumerations & State Lifecycles ### Game Status (`GameStatus`) ```mermaid stateDiagram-v2 [*] --> Lobby: CreateGameAsync() Lobby --> InProgress: StartGameAsync() (Host only) InProgress --> Completed: All rounds finished Lobby --> Cancelled: Host cancels InProgress --> Cancelled: Session abort ``` *State diagram illustrating lifecycle transitions of a game session from lobby creation to completion or cancellation.* * **`Lobby`**: Waiting for players to join and set ready status. * **`InProgress`**: Active gameplay with dealt cards and active rounds. * **`Completed`**: Game finished, final scores tallied. * **`Cancelled`**: Lobby or game terminated early. ### Round Status (`RoundStatus`) ```mermaid stateDiagram-v2 [*] --> Waiting: StartGame / New Round Waiting --> Active: DealCardsAsync() Active --> Resolving: Challenge submitted Resolving --> Active: Challenge resolved Active --> Completed: Timer expires / All phrases used ``` *State diagram illustrating round lifecycles from card dealing to challenge resolution.* ### Challenge Status (`ChallengeStatus`) * **`Pending`**: Accusation registered, awaiting response from the accused player. * **`Approved`**: Accusation confirmed (the phrase was indeed an invalid slip). The card remains with the accused player. * **`Rejected`**: Accusation rejected (the slip was legitimate). As a penalty, the card is transferred to the accuser's hand (`PlayerCard.PlayerId = ChallengingPlayerId`). --- ## 5. Data Transfer Objects (DTOs) & Data Privacy To enforce security and data privacy ([Architecture Overview](/openwiki/architecture/overview.md)), the backend exposes decoupled DTOs in `SlipItIn.Shared/DTOs`: | DTO | Visibility | Purpose & Content | |---|---|---| | `LoginRequestDto` | Direct REST | Credentials for authentication: `Email`, `Password`. | | `RegisterRequestDto` | Direct REST | Account creation details: `Username`, `Email`, `Password`. | | `AuthResponseDto` | Direct REST | JWT Access Token, Expiration, Username, Email. | | `GameStateDto` | Broadcast (Group) | Public lobby/game status: `GameId`, `LobbyCode`, `Status`, `CurrentRound`, `RoundTimeRemaining`, `Players` (`PlayerInfoDto` array containing `PlayerId`, `Username`, `Score`, `IsReady`, and `CardCount`). **No card text.** | | `PlayerHandDto` | Unicast (Client) | Private hand data sent strictly to the card owner: `PlayerId`, `Cards` (`PlayerCardDto` array containing `CardId`, `PhraseId`, `Text`, `IsUsed`). | | `SlipChallengeDto` | Unicast / Group | Challenge notification: `ChallengeId`, `ChallengingPlayerId`, `TargetPlayerId`, `TargetCardId`, `Status`, `CreatedAt`. | --- ## 6. Guidance for Future Agents & Developers - **When to Consult**: Consult this page when adding domain attributes, extending DTOs, adding new game state statuses, or modifying offline action serialization. - **Invariants**: - `PlayerCard.IsUsed` transitions from `false` to `true` upon `SubmitSlip` and cannot be reverted without server intervention. - `QueuedGameAction.ArgumentsJson` must store arguments in the exact order and type expected by `GameHub` methods. - **Primary Source Files**: - `SlipItIn.Shared/Models/Game.cs` - `SlipItIn.Shared/Models/PlayerCard.cs` - `SlipItIn.Shared/DTOs/GameStateDto.cs` - `SlipItIn/Models/QueuedGameAction.cs` - **Minimal Validation Command**: ```bash dotnet build SlipItIn.Shared/SlipItIn.Shared.csproj ```