Update der Dokumentation

This commit is contained in:
Tim Krampitz
2026-08-08 12:50:15 +02:00
parent bbc6ad6080
commit 118a62a804
15 changed files with 460 additions and 139 deletions

View File

@@ -2,7 +2,10 @@
## OpenWiki
This repository uses OpenWiki for recurring code documentation. Start with `openwiki/quickstart.md`, then follow its links to architecture, workflows, domain concepts, operations, integrations, testing guidance, and source maps.
This repository has a generated `openwiki/` evidence index. It is optional just-in-time context, not required startup reading.
- Treat source code and tests as authoritative. A brief's unknowns and review items are verification gaps, not automatic requirements.
- Prefer the narrowest quiet validation that proves the changed behavior. Preserve complete failure output.
The scheduled OpenWiki GitHub Actions workflow refreshes the repository wiki. Do not hand-edit generated OpenWiki pages unless explicitly asked; prefer updating source code/docs and letting OpenWiki regenerate.

View File

@@ -2,7 +2,10 @@
## OpenWiki
This repository uses OpenWiki for recurring code documentation. Start with `openwiki/quickstart.md`, then follow its links to architecture, workflows, domain concepts, operations, integrations, testing guidance, and source maps.
This repository has a generated `openwiki/` evidence index. It is optional just-in-time context, not required startup reading.
- Treat source code and tests as authoritative. A brief's unknowns and review items are verification gaps, not automatic requirements.
- Prefer the narrowest quiet validation that proves the changed behavior. Preserve complete failure output.
The scheduled OpenWiki GitHub Actions workflow refreshes the repository wiki. Do not hand-edit generated OpenWiki pages unless explicitly asked; prefer updating source code/docs and letting OpenWiki regenerate.

View File

@@ -101,7 +101,7 @@
<ItemGroup>
<PackageReference Include="Microsoft.Maui.Controls" Version="10.0.90" />
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="11.0.0-preview.6.26359.118" />
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.0" />
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.2" />
<PackageReference Include="Microsoft.AspNetCore.SignalR.Client" Version="10.0.10" />
</ItemGroup>

View File

@@ -1,6 +1,8 @@
{
"updatedAt": "2026-07-26T11:33:42.070Z",
"command": "init",
"gitHead": "070727d5cd1fff7fdd7e0db903f1c696b0045ecd",
"model": "gemini-3.6-flash"
"updatedAt": "2026-08-08T10:49:34.141Z",
"command": "update",
"gitHead": "bbc6ad60802ba4e19218d1529c927c67389d973d",
"model": "gemini-3.6-flash",
"status": "complete",
"language": "en"
}

View File

@@ -1,3 +1,3 @@
# Files
- [System Architecture & Security Overview](overview.md) - Technical architecture of SlipItIn, covering .NET Aspire orchestration, ASP.NET Core SignalR hub design, JWT authentication, and IDbContextFactory thread safety.
- [System Architecture & Security Overview](overview.md) - Technical architecture of SlipItIn, covering .NET Aspire orchestration, ASP.NET Core SignalR hub, .NET MAUI MVVM client layer, WeakReferenceMessenger event routing, and dual-layer local storage.

View File

@@ -1,13 +1,19 @@
---
type: Architecture
title: System Architecture & Security Overview
description: Technical architecture of SlipItIn, covering .NET Aspire orchestration, ASP.NET Core SignalR hub design, JWT authentication, and IDbContextFactory thread safety.
tags: [architecture, spire, signalr, jwt, efcore, security]
description: Technical architecture of SlipItIn, covering .NET Aspire orchestration, ASP.NET Core SignalR hub, .NET MAUI MVVM client layer, WeakReferenceMessenger event routing, and dual-layer local storage.
tags: [architecture, spire, signalr, jwt, efcore, security, maui, mvvm]
openwiki:
roles: [architecture, domain]
change_kinds: [lifecycle, public-api]
source_paths: [SlipItIn/MauiProgram.cs, SlipItIn/Services/SignalRService.cs, SlipItIn/Services/GameStateService.cs, SlipItIn.Server/Program.cs, SlipItIn.Server/Hubs/GameHub.cs]
symbols: [SignalRService, GameStateService, LocalStorageService, GameHub, GameService, IDbContextFactory]
validation_commands: ["dotnet build SlipItIn.slnx"]
---
# System Architecture & Security Overview
**SlipItIn** is designed as a distributed, real-time application using modern .NET 10 architecture. This document details the orchestration model, backend service structure, security model, and concurrency safeguards.
**SlipItIn** is designed as a distributed, real-time application using modern .NET 10 architecture. This document details the orchestration model, backend server structure, .NET MAUI MVVM client architecture, real-time connection resilience, messaging infrastructure, security model, and concurrency safeguards.
The architectural foundation [orchestrates services with](/openwiki/operations/runbook.md) .NET Aspire, while [enforcing security & privacy on](/openwiki/domain/game-mechanics.md) domain entities and [serving real-time hub endpoints for](/openwiki/workflows/slip-and-challenge.md) all active game sessions. Source code structure for all architectural components can be found in the [Source Code Map](/openwiki/source-map.md).
@@ -32,14 +38,14 @@ The solution uses **.NET Aspire** to orchestrate application resources, services
The backend is an ASP.NET Core Web API & SignalR application providing REST endpoints for user management and real-time WebSockets for game state synchronization.
### Key Components
### Key Backend Components
1. **`Program.cs`**:
- Registers `AddServiceDefaults()`, `AddNpgsqlDataSource("postgresdb")`, and `AddDbContextFactory<SlipItInDbContext>()`.
- Configures JWT Bearer authentication with custom `OnMessageReceived` token resolution for SignalR.
- Automatically executes database migrations (`db.Database.Migrate()`) at startup.
2. **`AuthController.cs`**:
- Manages user registration (`/api/auth/register`) and authentication (`/api/auth/login`).
- Manages user registration (`/api/auth/register`), authentication (`/api/auth/login`), and profile retrieval (`/api/auth/me`).
- Uses `BCrypt.Net` for secure password hashing.
- Issues JWT tokens signed with `Jwt:Key`, containing `ClaimTypes.NameIdentifier`, `ClaimTypes.Name`, and `ClaimTypes.Email`.
3. **`GameHub.cs`**:
@@ -48,6 +54,7 @@ The backend is an ASP.NET Core Web API & SignalR application providing REST endp
- Extracts and verifies user claims, delegating state mutations to `IGameService`.
4. **`GameService.cs`**:
- Implements core business logic: game creation, player joins, card dealing, slip submission, challenge creation, and resolution.
- Adds `hostPlayer` directly to `game.Players` before `context.Games.Add(game)` to ensure EF Core navigation tracking consistency.
---
@@ -101,13 +108,103 @@ sequenceDiagram
Client->>Hub: JoinLobby(lobbyCode)
Hub->>Hub: GetAuthenticatedUserId()
Hub->>Service: JoinGameAsync(lobbyCode, userId, connectionId)
Service->>DB: Create IDbContext Session & Save Player
Hub-->>Client: Broadcast "PlayerJoined" (GameStateDto)
```
*Sequence diagram showing user authentication via REST and subsequent authenticated SignalR WebSocket connection.*
---
## 4. Concurrency Safety: `IDbContextFactory`
## 4. .NET MAUI Client Architecture (`SlipItIn`)
The cross-platform client app uses the **MVVM Pattern** built on `CommunityToolkit.Mvvm` and Dependency Injection configured in `MauiProgram.cs`.
```mermaid
graph TD
subgraph UI_Layer["UI Layer (Views)"]
LoginPage["LoginPage"]
RegisterPage["RegisterPage"]
LobbyPage["LobbyPage"]
GameBoardPage["GameBoardPage"]
end
subgraph ViewModel_Layer["ViewModel Layer"]
LoginVM["LoginViewModel"]
RegisterVM["RegisterViewModel"]
LobbyVM["LobbyViewModel"]
GameBoardVM["GameBoardViewModel"]
end
subgraph Messaging_Layer["Messaging Layer"]
Messenger["WeakReferenceMessenger"]
Messages["Messages (GameStateChanged, PlayerHandChanged, ChallengeReceived)"]
end
subgraph Service_Layer["Service Layer"]
GameStateSvc["GameStateService"]
SignalRSvc["SignalRService"]
AuthSessionSvc["AuthSessionService"]
ApiSvc["ApiService"]
LocalStorageSvc["LocalStorageService"]
end
LoginPage --> LoginVM
RegisterPage --> RegisterVM
LobbyPage --> LobbyVM
GameBoardPage --> GameBoardVM
LobbyVM --> Messenger
GameBoardVM --> Messenger
GameStateSvc --> Messenger
GameStateSvc --> SignalRSvc
GameStateSvc --> LocalStorageSvc
LoginVM --> ApiSvc
LoginVM --> AuthSessionSvc
LoginVM --> SignalRSvc
```
*Layered architecture diagram of the .NET MAUI client showing Views, ViewModels, WeakReferenceMessenger, and Service implementations.*
### Dependency Injection & Service Registration (`MauiProgram.cs`)
- **Singletons**: `IAppConfigurationService` (`AppConfigurationService`), `ILocalStorageService` (`LocalStorageService`), `IAuthSessionService` (`AuthSessionService`), `IApiService` (`ApiService`), `ISignalRService` (`SignalRService`), `IGameStateService` (`GameStateService`), `LobbyViewModel`, `GameBoardViewModel`.
- **Transients**: `LoginViewModel`, `RegisterViewModel`.
- **Service Locator Helper**: `ServiceHelper.Services` captures `app.Services` at startup to allow static access where constructor DI is unavailable.
### Decoupled Messaging Infrastructure (`WeakReferenceMessenger`)
To avoid memory leaks caused by long-lived event subscriptions in ViewModels, `GameStateService` translates raw `ISignalRService` hub events into strongly-typed messages dispatched via `WeakReferenceMessenger.Default`:
- `LobbyCreatedMessage`: Dispatched when a new game lobby code is issued by the server.
- `GameStateChangedMessage`: Dispatched when public game state or player list updates.
- `GameStartedMessage`: Dispatched when the game transitions to `InProgress`.
- `PlayerHandChangedMessage`: Dispatched when a player receives or updates private phrase cards.
- `ChallengeReceivedMessage`: Dispatched when a player is directly targeted by a slip challenge.
- `ErrorOccurredMessage`: Dispatched when a hub or network error occurs.
- `ConnectionStateChangedMessage`: Dispatched when SignalR connection drops or reconnects.
ViewModels implement `IRecipient<TMessage>` and register via `WeakReferenceMessenger.Default.RegisterAll(this)` to receive automated UI updates.
### Real-Time Connection Resilience & Auto-Reconnect
`SignalRService` configures exponential backoff for WebSocket reconnection:
```csharp
_hubConnection = new HubConnectionBuilder()
.WithUrl(_configuration.HubUrl, options =>
{
options.AccessTokenProvider = () => Task.FromResult<string?>(token);
})
.WithAutomaticReconnect([TimeSpan.Zero, TimeSpan.FromSeconds(2), TimeSpan.FromSeconds(10), TimeSpan.FromSeconds(30)])
.Build();
```
When connection drops, `SignalRService` emits `ConnectionStateChanged`. Upon reconnection, `GameStateService` automatically triggers `ResyncAsync()` to fetch current state via `RequestGameStateAsync()` and flush locally queued offline actions.
### Dual-Layer Storage Security Model (`LocalStorageService`)
To satisfy security and privacy constraints:
1. **`SecureStorage` (Platform Encrypted Storage)**: Used strictly for `AuthTokenKey` (`auth_token`). Prevents plaintext access to active JWT credentials.
2. **`Preferences` (Application Key-Value Settings)**: Used for non-sensitive cached data (`auth_user`, `game_state`, `player_hand`, and `queued_actions`).
---
## 5. Concurrency Safety: `IDbContextFactory`
SignalR hubs process concurrent requests from multiple clients over persistent connections. Using a standard Scoped `DbContext` in SignalR leads to thread conflict exceptions (`InvalidOperationException: A second operation was started on this context instance before a previous operation completed`).
@@ -119,7 +216,7 @@ SlipItIn solves this by registering EF Core with `AddDbContextFactory<SlipItInDb
---
## 5. Data Privacy Isolation Model
## 6. Data Privacy Isolation Model
To prevent players from inspecting opponent cards via network trace analysis, SlipItIn strictly separates public and private data DTOs:
@@ -127,3 +224,30 @@ To prevent players from inspecting opponent cards via network trace analysis, Sl
* **`PlayerHandDto` (Private)**: Direct unicast message sent *only* to the specific player's SignalR `ConnectionId`. Contains card text (`CardId`, `PhraseId`, `Text`, `IsUsed`).
This architecture guarantees that players cannot see opponent phrase cards, even if they inspect raw WebSocket packets.
---
## 7. Guidance for Future Agents & Developers
When extending or modifying the system architecture:
- **When to Consult**: Consult this document before adding new real-time SignalR methods, introducing client ViewModels, modifying storage mechanisms, or altering server authentication logic.
- **Runtime Invariants**:
- `GameHub` methods must validate caller claims via `GetAuthenticatedUserId()` and `ValidatePlayerAccessAsync()`.
- Client ViewModels must consume `WeakReferenceMessenger` messages rather than subscribing directly to `ISignalRService` C# events.
- JWT tokens must never be persisted in `Preferences`; always use `SecureStorage`.
- **Extension Points**:
- New real-time server messages: Add method to `GameHub`, add event to `ISignalRService`, add message class in `SlipItIn/Messages/`, update `GameStateService` listener.
- New ViewModels: Register as Transient (dialogs/auth) or Singleton (main navigation screens) in `MauiProgram.cs`.
- **Primary Source Files**:
- `SlipItIn/MauiProgram.cs`
- `SlipItIn/Services/SignalRService.cs`
- `SlipItIn/Services/GameStateService.cs`
- `SlipItIn/Services/LocalStorageService.cs`
- `SlipItIn.Server/Hubs/GameHub.cs`
- `SlipItIn.Server/Services/GameService.cs`
- **Focused Checks**: Build and verify project references across client, server, and shared libraries.
- **Minimal Validation Command**:
```bash
dotnet build SlipItIn.slnx
```

View File

@@ -1,21 +1,27 @@
---
type: Domain Model
title: Game Domain & State Models
description: Detailed domain model and state lifecycles for SlipItIn games, rounds, phrase decks, players, and challenges.
tags: [domain, models, state-machine, entity-framework, privacy]
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, and data transfer objects (DTOs) that form the foundation of the SlipItIn game engine.
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/` and `SlipItIn.Shared/DTOs/`).
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 database schema is managed via Entity Framework Core (`SlipItInDbContext`) targeting PostgreSQL.
The backend database schema is managed via Entity Framework Core (`SlipItInDbContext`) targeting PostgreSQL.
```mermaid
erDiagram
@@ -70,19 +76,20 @@ erDiagram
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"
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.*
---
@@ -125,7 +132,19 @@ Represents an accusation made by one player against another player's submitted s
---
## 3. Enumerations & State Lifecycles
## 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
@@ -136,6 +155,7 @@ stateDiagram-v2
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.
@@ -151,6 +171,7 @@ stateDiagram-v2
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.
@@ -159,13 +180,33 @@ stateDiagram-v2
---
## 4. Data Transfer Objects (DTOs) & Data Privacy
## 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
```

View File

@@ -1,3 +1,3 @@
# Files
- [Game Domain & State Models](game-mechanics.md) - Detailed domain model and state lifecycles for SlipItIn games, rounds, phrase decks, players, and challenges.
- [Game Domain & State Models](game-mechanics.md) - Detailed domain model and state lifecycles for SlipItIn games, rounds, phrase decks, players, challenges, and client offline action queue.

View File

@@ -4,8 +4,8 @@ okf_version: "0.1"
# Files
- [SlipItIn Code Wiki Quickstart](quickstart.md) - Entrypoint for SlipItIn - a real-time multiplayer party game built with .NET 10, MAUI, ASP.NET Core SignalR, EF Core PostgreSQL, and .NET Aspire.
- [Source Code Map & Navigation Directory](source-map.md) - Practical navigation guide mapping source files across projects to system domains and responsibilities.
- [SlipItIn Code Wiki Quickstart](quickstart.md) - Entrypoint for SlipItIn - a real-time multiplayer party game built with .NET 10, MAUI MVVM, ASP.NET Core SignalR, EF Core PostgreSQL, and .NET Aspire.
- [Source Code Map & Navigation Directory](source-map.md) - Practical navigation guide mapping source files across projects to system domains, viewmodels, services, and responsibilities.
# Directories

View File

@@ -1,3 +1,3 @@
# Files
- [Operations, Environment Setup & Testing Guidance](runbook.md) - Operational guide for launching SlipItIn with .NET Aspire, running EF Core PostgreSQL migrations, configuring JWT secrets, and executing tests.
- [Operations, Environment Setup & Testing Guidance](runbook.md) - Operational guide for launching SlipItIn with .NET Aspire, running EF Core PostgreSQL migrations, configuring secrets, and executing client/server verification tests.

View File

@@ -1,15 +1,21 @@
---
type: Runbook
title: Operations, Environment Setup & Testing Guidance
description: Operational guide for launching SlipItIn with .NET Aspire, running EF Core PostgreSQL migrations, configuring JWT secrets, and executing tests.
tags: [operations, runbook, spire, postgresql, migrations, testing]
description: Operational guide for launching SlipItIn with .NET Aspire, running EF Core PostgreSQL migrations, configuring secrets, and executing client/server verification tests.
tags: [operations, runbook, spire, postgresql, migrations, testing, mvvm, offline-queue]
openwiki:
roles: [operations, testing]
change_kinds: [lifecycle, public-api]
source_paths: [SlipItIn.AppHost/AppHost.cs, SlipItIn.Server/Program.cs, SlipItIn/MauiProgram.cs]
symbols: [AppHost, Program, MauiProgram]
validation_commands: ["dotnet build SlipItIn.slnx"]
---
# Operations, Environment Setup & Testing Guidance
This runbook provides actionable instructions for local development setup, starting services via .NET Aspire, executing Entity Framework Core migrations, configuring environment keys, and running tests.
This runbook provides actionable instructions for local development setup, starting services via .NET Aspire, executing Entity Framework Core migrations, configuring environment keys, running client/server applications, and executing targeted test scenarios.
This guide [configures environment parameters for](/openwiki/architecture/overview.md) the backend architecture, [manages database migrations for entities in](/openwiki/domain/game-mechanics.md) the domain model, [verifies real-time event flows defined in](/openwiki/workflows/slip-and-challenge.md) the workflow guide, and [references source entrypoints cataloged in](/openwiki/source-map.md) the source map.
This guide [configures environment parameters for](/openwiki/architecture/overview.md) the backend and client architecture, [manages database migrations for entities in](/openwiki/domain/game-mechanics.md) the domain model, [verifies real-time event flows defined in](/openwiki/workflows/slip-and-challenge.md) the workflow guide, and [references source entrypoints cataloged in](/openwiki/source-map.md) the source map.
---
@@ -81,7 +87,7 @@ dotnet ef database update --project SlipItIn.Server --startup-project SlipItIn.S
## 4. Testing Guidance & Verification Scenarios
When developing or extending SlipItIn features, verify the core architecture through targeted test scenarios specified in `Agents/Architecture.md`:
When developing or extending SlipItIn features, verify the core architecture through these targeted test scenarios:
### 1. JWT Authentication & Claims Tests
* **Test Objective**: Verify `GameHub` rejects unauthenticated WebSocket connections or missing token query parameters.
@@ -91,14 +97,34 @@ When developing or extending SlipItIn features, verify the core architecture thr
* **Test Objective**: Verify Player A cannot manipulate Player B's cards or state.
* **Verification**: Authenticate as User A and attempt to call `GameHub.SubmitSlip(gameId, playerBId, cardId)`. Verify that `ValidatePlayerAccessAsync` throws `UnauthorizedAccessException` and returns an error response.
### 3. Concurrency & Race Condition Tests
### 3. Client MVVM Decoupled Messaging Tests
* **Test Objective**: Confirm `GameStateService` translates SignalR events into `WeakReferenceMessenger` messages without memory leak risk.
* **Verification**: Trigger a `GameStateUpdated` event on `SignalRService`. Confirm that `LobbyViewModel` and `GameBoardViewModel` receive `GameStateChangedMessage` and update their observable collections cleanly.
### 4. Offline Action Queueing & Resync Verification
* **Test Objective**: Verify actions taken while disconnected are stored locally and replayed upon reconnection.
* **Verification**:
1. Disconnect network or stop `SignalRService`.
2. Invoke `GameBoardViewModel.SubmitSlipAsync(card)`. Confirm `QueuedGameAction` is written to `Preferences`.
3. Re-establish connection. Confirm `GameStateService.ResyncAsync()` calls `RequestGameStateAsync()` and replays the queued action via `SendQueuedActionAsync()`.
### 5. Dual-Layer Local Storage Verification
* **Test Objective**: Confirm sensitive tokens are isolated in `SecureStorage` while non-sensitive state stays in `Preferences`.
* **Verification**: Inspect local device storage after login. Verify `auth_token` is stored via platform `SecureStorage` and `game_state` / `queued_actions` are saved in `Preferences`.
### 6. Concurrency & Race Condition Tests
* **Test Objective**: Confirm `IDbContextFactory` handles simultaneous WebSocket calls without thread collision.
* **Verification**: Simulate 5 parallel calls to `GameHub.ChallengeSlip()` or `SubmitSlip()` across multiple clients. Verify that all calls complete cleanly without `InvalidOperationException` from DbContext.
### 4. Data Privacy Isolation Verification
* **Test Objective**: Confirm card text is never broadcast in public group messages.
* **Verification**: Capture SignalR `PlayerJoined` and `GameStateUpdated` payloads. Inspect JSON content to confirm only `CardCount` is present and no phrase card `Text` is leaked.
---
### 5. False Accusation Penalty Verification
* **Test Objective**: Verify penalty card transfer when a challenge is rejected.
* **Verification**: Submit a challenge against a valid slip, then call `ResolveChallenge(challengeId, approved: false)`. Verify in the database that `PlayerCard.PlayerId` is reassigned to the challenger's `PlayerId`.
## 5. Guidance for Future Agents & Developers
- **When to Consult**: Refer to this runbook for local environment setup, Aspire startup commands, EF Core migration commands, and test verification procedures.
- **Invariants**:
- Always run migrations using `SlipItIn.Server` as both project and startup-project.
- Test offline queue flushing before submitting client-side real-time changes.
- **Minimal Validation Command**:
```bash
dotnet build SlipItIn.slnx
```

View File

@@ -1,15 +1,21 @@
---
type: Overview
title: SlipItIn Code Wiki Quickstart
description: Entrypoint for SlipItIn - a real-time multiplayer party game built with .NET 10, MAUI, ASP.NET Core SignalR, EF Core PostgreSQL, and .NET Aspire.
tags: [quickstart, overview, slipitin, dotnet10, spire]
description: Entrypoint for SlipItIn - a real-time multiplayer party game built with .NET 10, MAUI MVVM, ASP.NET Core SignalR, EF Core PostgreSQL, and .NET Aspire.
tags: [quickstart, overview, slipitin, dotnet10, spire, maui, mvvm]
openwiki:
roles: [architecture, domain, workflow]
change_kinds: [public-api, lifecycle]
source_paths: [SlipItIn/MauiProgram.cs, SlipItIn.Server/Program.cs, SlipItIn.AppHost/AppHost.cs]
symbols: [MauiProgram, SignalRService, GameStateService, GameHub, GameService]
validation_commands: ["dotnet build SlipItIn.slnx"]
---
# SlipItIn Code Wiki Quickstart
Welcome to the **SlipItIn** repository wiki. SlipItIn is a real-time multiplayer party game where players receive secret phrase cards and attempt to "slip" those phrases into everyday conversations or text chats without getting caught by other players.
The solution is built using **.NET 10**, leveraging **ASP.NET Core Web API & SignalR** for the backend engine, **Entity Framework Core (Npgsql / PostgreSQL)** for data persistence, **.NET MAUI** for the cross-platform client app, and **.NET Aspire** for distributed cloud-native orchestration and telemetry.
The solution is built using **.NET 10**, leveraging **ASP.NET Core Web API & SignalR** for the backend engine, **Entity Framework Core (Npgsql / PostgreSQL)** for data persistence, **.NET MAUI (MVVM Pattern)** for the cross-platform client app, and **.NET Aspire** for distributed cloud-native orchestration and telemetry.
---
@@ -21,47 +27,62 @@ The repository is organized as a multi-project .NET solution (`SlipItIn.slnx`):
SlipItIN/
├── SlipItIn.AppHost/ # .NET Aspire AppHost orchestrator (pgsql + server + client)
├── SlipItIn.Server/ # ASP.NET Core Web API + SignalR Hub + Game Engine
├── SlipItIn.Shared/ # Shared Class Library (Models & DTOs)
├── SlipItIn.Shared/ # Shared Class Library (Models, DTOs & Enums)
├── SlipItIn.ServiceDefaults/ # Aspire OpenTelemetry, Health Checks & Service Discovery
└── SlipItIn/ # .NET MAUI Client App (Android, iOS, MacCatalyst, Windows)
└── SlipItIn/ # .NET MAUI Client App (MVVM: Views, ViewModels, Services, Messages)
```
The system architecture [orchestrates services with](/openwiki/operations/runbook.md) .NET Aspire and [enforces security and privacy on](/openwiki/domain/game-mechanics.md) all domain entities. For a deep dive into the backend design, JWT security, and concurrency safety, see the [System Architecture & Security Overview](/openwiki/architecture/overview.md).
The system architecture [orchestrates services with](/openwiki/operations/runbook.md) .NET Aspire and [enforces security and privacy on](/openwiki/domain/game-mechanics.md) all domain entities. For a deep dive into backend security, SignalR real-time client integration, MVVM architecture, and offline queueing, see the [System Architecture & Security Overview](/openwiki/architecture/overview.md).
```mermaid
graph TD
MAUI[SlipItIn .NET MAUI Client] -->|REST / API| Server[SlipItIn.Server ASP.NET Core]
MAUI -->|SignalR WebSockets| Hub[GameHub /hubs/game]
Server -->|IDbContextFactory| DB[(PostgreSQL Database)]
AppHost[.NET Aspire AppHost] -->|Orchestrates| Server
subgraph MAUI_Client["SlipItIn .NET MAUI Client"]
Views["Views (LoginPage, LobbyPage, GameBoardPage)"] --> ViewModels["ViewModels (LoginVM, LobbyVM, GameBoardVM)"]
ViewModels --> GameState["GameStateService"]
GameState --> SignalR["SignalRService"]
GameState --> LocalStorage["LocalStorageService (SecureStorage & Preferences)"]
ViewModels --> ApiService["ApiService (REST Auth)"]
end
ApiService -->|REST /api/auth| AuthCtrl["AuthController"]
SignalR -->|WebSockets /hubs/game| Hub["GameHub"]
Hub --> Service["GameService"]
AuthCtrl --> DB[("PostgreSQL Database")]
Service -->|IDbContextFactory| DB
AppHost[".NET Aspire AppHost"] -->|Orchestrates| Server["SlipItIn.Server"]
AppHost -->|Provisions| DB
Server -->|Uses Defaults| ServiceDefaults[SlipItIn.ServiceDefaults]
```
*System architecture diagram illustrating .NET MAUI MVVM client layer, backend REST/SignalR APIs, and .NET Aspire PostgreSQL orchestration.*
---
## 2. Core Game Loop & Mechanics
1. **Lobby Creation**: Host creates a game lobby with a 6-character code. Players join via code.
2. **Card Dealing**: Upon game start, each player receives a private hand of 5 secret phrase cards (`PlayerHandDto`).
3. **Phrase Slipping**: During normal conversation, a player speaks or types one of their phrases and clicks **Submit Slip**.
4. **Slip Challenge**: Opponents suspecting a fake phrase can issue a **Slip Challenge**.
- **Justified Accusation (Approved)**: Target phrase was an invalid slip.
- **False Accusation (Rejected)**: Target phrase was legitimate. The accuser receives the card as a penalty (expanding their hand size beyond 5).
1. **Authentication & Session**: Player registers or logs in via `LoginPage` / `RegisterPage`. `AuthSessionService` stores the JWT token in `SecureStorage`.
2. **Lobby Creation**: Host creates a lobby on `LobbyPage`, generating a 6-character code. Players join using the code.
3. **Card Dealing**: Upon game start, each player receives a private hand of 5 secret phrase cards (`PlayerHandDto`) via SignalR unicast.
4. **Phrase Slipping**: During normal conversation, a player speaks or types one of their phrases and clicks **Submit Slip** on `GameBoardPage`. If offline, actions queue locally in `QueuedGameAction` and flush automatically upon reconnection.
5. **Slip Challenge**: Opponents suspecting a fake phrase can issue a **Slip Challenge**.
- **Justified Accusation (Approved)**: Target phrase was an invalid slip. Card remains with accused player.
- **False Accusation (Rejected)**: Target phrase was legitimate. The accuser receives the phrase card as a penalty.
To learn how game events flow across SignalR in real time, inspect the [Slip & Challenge Workflows](/openwiki/workflows/slip-and-challenge.md).
---
## 3. Wiki Navigation Map
## 3. Task Routing & Navigation Map
Explore specific documentation sections for technical details:
The following task routing table directs future engineers and automated agents to relevant wiki documentation, source entry points, key symbols, and verification commands for common modification scenarios:
- **[System Architecture & Security Overview](/openwiki/architecture/overview.md)**: Explains .NET Aspire orchestration, JWT bearer authentication, claims validation, `IDbContextFactory` thread safety, and public vs. private data isolation.
- **[Game Domain & State Models](/openwiki/domain/game-mechanics.md)**: Details domain entities (`User`, `Game`, `Player`, `Phrase`, `PlayerCard`, `GameRound`, `SlipChallenge`) and their state lifecycles.
- **[Slip & Challenge Workflows](/openwiki/workflows/slip-and-challenge.md)**: Details step-by-step game loop execution, real-time SignalR notifications, and penalty rules.
- **[Source Code Map](/openwiki/source-map.md)**: Directory and file navigation index mapping repository paths to technical domains.
- **[Operations & Runbook](/openwiki/operations/runbook.md)**: Instructions for running the app with Aspire, executing PostgreSQL EF Core migrations, configuration keys, and testing strategies.
| Intent / Change Area | Relevant Wiki Page | Source Entry Points | Key Symbols / Types | Focused Checks | Minimal Validation Command |
|---|---|---|---|---|---|
| **MAUI Client ViewModels & Pages** | [Architecture Overview](/openwiki/architecture/overview.md) | `SlipItIn/ViewModels/`, `SlipItIn/Views/` | `LobbyViewModel`, `GameBoardViewModel`, `LoginViewModel` | Inspect MVVM command handlers and `WeakReferenceMessenger` bindings | `dotnet build SlipItIn/SlipItIn.csproj` |
| **Client SignalR & Messaging** | [Slip & Challenge Workflows](/openwiki/workflows/slip-and-challenge.md) | `SlipItIn/Services/SignalRService.cs`, `SlipItIn/Services/GameStateService.cs` | `ISignalRService`, `IGameStateService`, `WeakReferenceMessenger` | Verify auto-reconnect backoff and message dispatching | `dotnet build SlipItIn/SlipItIn.csproj` |
| **Offline Action Queue & Resync** | [Slip & Challenge Workflows](/openwiki/workflows/slip-and-challenge.md) | `SlipItIn/Services/GameStateService.cs`, `SlipItIn/Models/QueuedGameAction.cs` | `QueuedGameAction`, `EnqueueActionAsync`, `FlushQueuedActionsAsync` | Verify offline queueing in `Preferences` and flush on reconnect | `dotnet build SlipItIn/SlipItIn.csproj` |
| **Local Storage & Auth Session** | [Architecture Overview](/openwiki/architecture/overview.md) | `SlipItIn/Services/LocalStorageService.cs`, `SlipItIn/Services/AuthSessionService.cs` | `ILocalStorageService`, `IAuthSessionService`, `SecureStorage` | Confirm JWT stored in `SecureStorage` and state in `Preferences` | `dotnet build SlipItIn/SlipItIn.csproj` |
| **Backend Game Engine & Hub** | [Architecture Overview](/openwiki/architecture/overview.md) | `SlipItIn.Server/Hubs/GameHub.cs`, `SlipItIn.Server/Services/GameService.cs` | `GameHub`, `GameService`, `IDbContextFactory` | Test JWT claim extraction and EF Core DbContext session isolation | `dotnet build SlipItIn.Server/SlipItIn.Server.csproj` |
| **Domain Models & DTOs** | [Game Domain & State Models](/openwiki/domain/game-mechanics.md) | `SlipItIn.Shared/Models/`, `SlipItIn.Shared/DTOs/` | `GameStateDto`, `PlayerHandDto`, `SlipChallengeDto`, `QueuedGameAction` | Confirm card privacy isolation between public state and private hand | `dotnet build SlipItIn.Shared/SlipItIn.Shared.csproj` |
| **Aspire & Infrastructure Setup** | [Operations & Runbook](/openwiki/operations/runbook.md) | `SlipItIn.AppHost/AppHost.cs`, `SlipItIn.ServiceDefaults/` | `AppHost`, `AddServiceDefaults`, `AddPostgres` | Validate Aspire service references and PostgreSQL container creation | `dotnet run --project SlipItIn.AppHost/SlipItIn.AppHost.csproj` |
---
@@ -72,6 +93,8 @@ When modifying this repository, strictly adhere to these core rules:
1. **IDbContextFactory Thread Safety**: Never inject a scoped `SlipItInDbContext` into SignalR hubs or singleton services. Always use `IDbContextFactory<SlipItInDbContext>.CreateDbContext()` to prevent DbContext concurrency exceptions during concurrent WebSocket calls ([Architecture Overview](/openwiki/architecture/overview.md)).
2. **Data Privacy Isolation**: Do not leak card text into public DTOs. Public game state must be broadcast using `GameStateDto` (card counts only), while private cards are dispatched strictly via `PlayerHandDto` to individual client connections ([Domain Mechanics](/openwiki/domain/game-mechanics.md)).
3. **Server-Side Validation**: SignalR client calls represent intent ("I want to challenge X"). The server MUST re-verify JWT claims, player game membership, card ownership, and round status inside `GameHub.cs` and `GameService.cs` before mutating state ([Slip & Challenge Workflows](/openwiki/workflows/slip-and-challenge.md)).
4. **Decoupled Client Messaging**: MAUI ViewModels MUST NOT consume `ISignalRService` events directly for UI updates. Always route events through `IGameStateService` via `WeakReferenceMessenger` messages (`GameStateChangedMessage`, `PlayerHandChangedMessage`, `ChallengeReceivedMessage`) to prevent memory leaks and dangling subscriptions ([Architecture Overview](/openwiki/architecture/overview.md)).
5. **Dual-Layer Local Storage**: Always store sensitive credentials (JWT access tokens) in platform `SecureStorage`. Non-sensitive UI states, cached game data, and offline action queues MUST be persisted in `Preferences` ([Architecture Overview](/openwiki/architecture/overview.md)).
---
@@ -79,6 +102,4 @@ When modifying this repository, strictly adhere to these core rules:
The following features and components are specified in specification documents (`Agents/ProjectPlan.md` and `Agents/Architecture.md`) and backlogged for upcoming development iterations:
- **MAUI Client Services & ViewModels**: Implement `ApiService`, `SignalRService`, `GameStateService`, `LobbyViewModel`, and `GameBoardViewModel` under `SlipItIn/` using `CommunityToolkit.Mvvm` and `WeakReferenceMessenger`. (Anchor: `SlipItIn/`, pending client phase 3 completion).
- **Offline Action Queue & Auto-Resync**: Implement exponential backoff reconnect logic (0s, 2s, 10s, 30s) and queued action execution upon app resume in MAUI client. (Anchor: `SlipItIn/Services/`, deferred until MAUI service layer setup).
- **Timer Engine for Slip Rounds**: Background timer service on server enforcing round duration limits (30-60s) with automated round completion notifications. (Anchor: `SlipItIn.Server/Services/`, pending phase 2b refinement).

View File

@@ -1,8 +1,14 @@
---
type: Reference
title: Source Code Map & Navigation Directory
description: Practical navigation guide mapping source files across projects to system domains and responsibilities.
tags: [source-map, navigation, directory, projects]
description: Practical navigation guide mapping source files across projects to system domains, viewmodels, services, and responsibilities.
tags: [source-map, navigation, directory, projects, maui, mvvm]
openwiki:
roles: [repository]
change_kinds: [public-api]
source_paths: [SlipItIn/MauiProgram.cs, SlipItIn.Server/Program.cs, SlipItIn.AppHost/AppHost.cs]
symbols: [MauiProgram, Program, AppHost]
validation_commands: ["dotnet build SlipItIn.slnx"]
---
# Source Code Map & Navigation Directory
@@ -19,9 +25,9 @@ This navigation map [indexes backend architecture files described in](/openwiki/
SlipItIn.slnx
├── SlipItIn.AppHost/ # Aspire distributed orchestrator
├── SlipItIn.Server/ # Web API & SignalR real-time server
├── SlipItIn.Shared/ # Shared models & data transfer objects
├── SlipItIn.Shared/ # Shared models, DTOs & enums
├── SlipItIn.ServiceDefaults/ # Aspire OpenTelemetry & health checks
├── SlipItIn/ # .NET MAUI multi-platform client
├── SlipItIn/ # .NET MAUI multi-platform client (MVVM)
└── Agents/ # Architecture & planning briefs
```
@@ -41,7 +47,7 @@ SlipItIn.slnx
- **`Controllers/AuthController.cs`**: REST API controller providing `/api/auth/register`, `/api/auth/login`, and `/api/auth/me`. Handles BCrypt password hashing and JWT token generation.
- **`Hubs/GameHub.cs`**: SignalR hub mapped to `/hubs/game`. Performs JWT claim extraction (`GetAuthenticatedUserId`), player/host authorization checks (`ValidatePlayerAccessAsync`, `ValidateHostAccessAsync`), connection ID tracking, and real-time event broadcasting.
- **`Services/IGameService.cs`**: Contract interface defining backend game operations.
- **`Services/GameService.cs`**: Core engine implementation. Handles game creation, player joining, card dealing from active phrases, slip submission, challenge creation, penalty resolution, and state serialization (`GetGameStateAsync`, `GetPlayerHandAsync`).
- **`Services/GameService.cs`**: Core engine implementation. Handles game creation, player joining, card dealing from active phrases, slip submission, challenge creation, penalty resolution, and state serialization (`GetGameStateAsync`, `GetPlayerHandAsync`). Adds `hostPlayer` directly to `game.Players` before `context.Games.Add(game)` for EF Core navigation safety.
- **`Data/SlipItInDbContext.cs`**: Entity Framework Core DbContext mapping `Users`, `Games`, `Players`, `Phrases`, `PlayerCards`, `GameRounds`, and `SlipChallenges` to PostgreSQL tables.
- **`Migrations/`**: Auto-generated EF Core migration snapshots (`20260723193505_InitialCreate.cs`).
- **`appsettings.json` & `appsettings.Development.json`**: JWT secret keys, issuer/audience defaults, and database connection strings.
@@ -58,12 +64,35 @@ SlipItIn.slnx
- **`DTOs/GameStateDto.cs`**: DTOs for public state (`GameStateDto`, `PlayerInfoDto`), private hand state (`PlayerHandDto`, `PlayerCardDto`), and challenge alerts (`SlipChallengeDto`).
### `Cross-Platform MAUI Client` (`SlipItIn`)
- **`MauiProgram.cs`**: Client builder configuring MAUI app shell, fonts, and logging debug extensions.
- **`MauiProgram.cs`**: Client app builder. Configures dependency injection for ViewModels and Services, fonts, debug logging, and sets up `ServiceHelper.Services`.
- **`App.xaml` & `App.xaml.cs`**: Root MAUI application class.
- **`AppShell.xaml` & `AppShell.xaml.cs`**: AppShell routing container.
- **`MainPage.xaml` & `MainPage.xaml.cs`**: Initial entry view.
- **`AppShell.xaml` & `AppShell.xaml.cs`**: Shell navigation container routing between `LoginPage`, `RegisterPage`, `LobbyPage`, and `GameBoardPage`.
- **`Infrastructure/ServiceHelper.cs`**: Static service locator bridge for runtime DI resolution.
- **`Services/`**:
- `SignalRService.cs` (`ISignalRService`): WebSocket SignalR client, hub method invoker, and auto-reconnect engine.
- `GameStateService.cs` (`IGameStateService`): Central game state holder, messenger bridge (`WeakReferenceMessenger`), and offline action queue manager.
- `LocalStorageService.cs` (`ILocalStorageService`): Dual-layer local persistence (`SecureStorage` for tokens, `Preferences` for game state/offline queue).
- `AuthSessionService.cs` (`IAuthSessionService`): Authenticated user session state manager.
- `ApiService.cs` (`IApiService`): REST HTTP client for authentication endpoints.
- `AppConfigurationService.cs` (`IAppConfigurationService`): Configuration provider for API base URL and SignalR Hub URL.
- **`ViewModels/`**:
- `BaseViewModel.cs`: Abstract base ViewModel providing `IsBusy`, `StatusMessage`, and error handling helper `RunSafeAsync`.
- `LoginViewModel.cs`: Handles email/password authentication and navigation to `LobbyPage`.
- `RegisterViewModel.cs`: Handles user registration.
- `LobbyViewModel.cs`: Manages lobby creation, joining, player readiness, and game start. Implements `IRecipient` interfaces for real-time messages.
- `GameBoardViewModel.cs`: Manages game board state, player cards, slip submission, challenging, challenge resolution, and offline queueing.
- **`Views/`**:
- `LoginPage.xaml` / `.cs`: XAML login UI view.
- `RegisterPage.xaml` / `.cs`: XAML registration UI view.
- `LobbyPage.xaml` / `.cs`: XAML lobby UI view displaying lobby code and player list.
- `GameBoardPage.xaml` / `.cs`: XAML game board UI view displaying cards and player list.
- `ChallengeNotificationOverlay.xaml` / `.cs`: Reusable overlay view for challenge alerts.
- **`Messages/`**:
- `LobbyCreatedMessage.cs`, `GameStateChangedMessage.cs`, `GameStartedMessage.cs`, `PlayerHandChangedMessage.cs`, `ChallengeReceivedMessage.cs`, `ErrorOccurredMessage.cs`, `ConnectionStateChangedMessage.cs`: Strongly-typed `CommunityToolkit.Mvvm.Messaging` payloads.
- **`Models/`**:
- `QueuedGameAction.cs`: Data model for offline game action queueing.
- **`Platforms/`**: Platform-specific entry points for Android, iOS, MacCatalyst, and Windows.
### `Documentation & Specifications` (`Agents/`)
- **`Agents/Architecture.md`**: Specification document defining Phase 2b backend security (JWT validation, `IDbContextFactory`, privacy DTOs) and Phase 3b MAUI stability patterns (`WeakReferenceMessenger`, auto-reconnect, dual-layer storage).
- **`Agents/ProjectPlan.md`**: Project plan breakdown covering phases 1 through 4.
- **`Agents/Architecture.md`**: Specification document defining backend security and client MVVM/stability patterns.
- **`Agents/ProjectPlan.md`**: Project plan breakdown covering development phases.

View File

@@ -1,3 +1,3 @@
# Files
- [Slip & Challenge Workflows](slip-and-challenge.md) - Real-time game loops covering lobby creation, card dealing, phrase slipping, slip challenging, and challenge resolution.
- [Slip & Challenge Workflows](slip-and-challenge.md) - Real-time game loops covering authentication, lobby creation, card dealing, phrase slipping, slip challenging, challenge resolution, and offline action queuing.

View File

@@ -1,121 +1,175 @@
---
type: Workflow
title: Slip & Challenge Workflows
description: Real-time game loops covering lobby creation, card dealing, phrase slipping, slip challenging, and challenge resolution.
tags: [workflow, game-loop, signalr, slip-mechanic, real-time]
description: Real-time game loops covering authentication, lobby creation, card dealing, phrase slipping, slip challenging, challenge resolution, and offline action queuing.
tags: [workflow, game-loop, signalr, slip-mechanic, real-time, offline-resync]
openwiki:
roles: [workflow]
change_kinds: [public-api, lifecycle]
source_paths: [SlipItIn/ViewModels/GameBoardViewModel.cs, SlipItIn/Services/GameStateService.cs, SlipItIn/Services/SignalRService.cs, SlipItIn.Server/Hubs/GameHub.cs]
symbols: [LobbyViewModel, GameBoardViewModel, SignalRService, GameStateService, GameHub]
validation_commands: ["dotnet build SlipItIn.slnx"]
---
# Slip & Challenge Workflows
This document outlines the core real-time game workflows in SlipItIn, detailing how SignalR events, backend service operations, database updates, and client state notifications interact.
This document outlines the core real-time game workflows in SlipItIn, detailing how client MVVM ViewModels, `WeakReferenceMessenger`, backend SignalR hubs, database services, and offline action queues interact.
These workflows [execute state transitions on](/openwiki/domain/game-mechanics.md) domain entities, [invoke real-time methods in](/openwiki/architecture/overview.md) the ASP.NET Core `GameHub`, [are implemented across backend services mapped in](/openwiki/source-map.md) the source map, and [are verified using tests described in](/openwiki/operations/runbook.md) the operations runbook.
These workflows [execute state transitions on](/openwiki/domain/game-mechanics.md) domain entities, [invoke real-time methods in](/openwiki/architecture/overview.md) the ASP.NET Core `GameHub` and client `SignalRService`, [are implemented across backend and client services mapped in](/openwiki/source-map.md) the source map, and [are verified using tests described in](/openwiki/operations/runbook.md) the operations runbook.
---
## 1. Game Setup & Lobby Workflow
```
[Host] CreateLobby() ──> Generate 6-Char LobbyCode ──> Add to SignalR Group
[Player] JoinLobby() ──> Verify MaxPlayers & Status ───────────┤
[Player] PlayerReady() ──> Set IsReady = true ───────────────────> Broadcast GameStateUpdated
[Host] StartGame() ──> Deal 5 Random Cards Per Player ─────────┴──> Unicast PlayerHandUpdated
[Client App] LoginVM.LoginAsync() ──> REST Auth ──> Save Token in SecureStorage ──> SignalR.ConnectAsync()
[Host] LobbyVM.CreateLobbyAsync() ──> SignalR.CreateLobbyAsync() ──> Server Generates Code ──┤
[Player] LobbyVM.JoinLobbyAsync() ──> SignalR.JoinLobbyAsync(code) ──────────────────────────┤
[Player] LobbyVM.SetReadyAsync() ──> SignalR.PlayerReadyAsync() ─────────────────────────────┼──> Broadcast GameStateUpdated
[Host] LobbyVM.StartGameAsync() ──> SignalR.StartGameAsync() ──> Deal 5 Cards / Player ──────┴──> Unicast PlayerHandUpdated
```
1. **Lobby Creation**: Host calls `GameHub.CreateLobby()`. `GameService.CreateGameAsync()` creates a `Game` record (`Status = Lobby`), generates a random 6-character `LobbyCode`, adds the host as the first `Player`, and registers the host's WebSocket `ConnectionId`. Caller receives `LobbyCreated`.
2. **Joining Lobby**: Opponents invoke `GameHub.JoinLobby(lobbyCode)`. `GameService.JoinGameAsync()` validates that the game exists, has space (`Players.Count < MaxPlayers`), and is in `Lobby` status. The player is assigned a `Player` entry, added to the SignalR group `lobbyCode`, and `PlayerJoined` (`GameStateDto`) is broadcast to all participants.
3. **Player Ready**: Players invoke `GameHub.PlayerReady(gameId, playerId)`. `GameHub` checks `ValidatePlayerAccessAsync` and broadcasts `GameStateUpdated`.
4. **Game Start & Card Dealing**: The host invokes `GameHub.StartGame(gameId)`. `GameHub` verifies host authorization via `ValidateHostAccessAsync(gameId, authUserId)`.
1. **Authentication & Session Setup**: Player logs in via `LoginPage` (`LoginViewModel.LoginAsync()`). `ApiService` posts credentials to `/api/auth/login`. `AuthSessionService` persists the token to `SecureStorage`. `SignalRService.ConnectAsync(token)` opens a WebSocket connection to `/hubs/game`.
2. **Lobby Creation**: Host invokes `LobbyViewModel.CreateLobbyAsync()`. `SignalRService.CreateLobbyAsync()` triggers `GameHub.CreateLobby()`. `GameService.CreateGameAsync()` creates a `Game` record (`Status = Lobby`), generates a random 6-character `LobbyCode`, adds host to `game.Players`, and registers host's `ConnectionId`. Caller receives `LobbyCreated` message via `WeakReferenceMessenger`.
3. **Joining Lobby**: Opponents call `LobbyViewModel.JoinLobbyAsync()`. `SignalRService.JoinLobbyAsync(code)` validates game status and capacity. `GameService.JoinGameAsync()` assigns player to game, registers `ConnectionId`, adds player to SignalR group `lobbyCode`, and broadcasts `PlayerJoined` (`GameStateDto`) to all participants.
4. **Player Ready**: Players invoke `LobbyViewModel.SetReadyAsync()`. `GameHub.PlayerReady()` verifies access and broadcasts `GameStateUpdated`.
5. **Game Start & Card Dealing**: The host invokes `LobbyViewModel.StartGameAsync()`.
- `GameHub.StartGame()` verifies host identity (`ValidateHostAccessAsync`).
- `GameService.StartGameAsync()` transitions `GameStatus` to `InProgress` and creates `GameRound` 1 (`RoundStatus.Active`).
- `GameService.DealCardsAsync()` fetches active phrases, randomly selects 5 phrases per player, creates `PlayerCard` entries, and saves them to PostgreSQL.
- SignalR broadcasts `GameStarted` to the group.
- `GameService.DealCardsAsync()` selects 5 random phrases per player, creates `PlayerCard` records, and saves them to PostgreSQL.
- SignalR broadcasts `GameStarted` to the group (triggering client navigation to `GameBoardPage`).
- SignalR sends private `PlayerHandUpdated` notifications (`PlayerHandDto`) individually to each player's `ConnectionId`.
---
## 2. Phrase Slipping Workflow
## 2. Phrase Slipping & Offline Queueing Workflow
During real-life conversation or chat, a player speaks or types one of their secret phrases and marks it as used in the app:
1. **Submission**: Player A calls `GameHub.SubmitSlip(gameId, playerId, cardId)`.
2. **Access Control**: `GameHub` executes `ValidatePlayerAccessAsync(playerId, userId)` to verify that Player A owns the specified `Player` account.
3. **Engine Execution**: `GameService.SubmitSlipAsync()` loads the `PlayerCard`, validates card ownership (`card.PlayerId == playerId`), and sets `card.IsUsed = true`.
4. **Notification**: `GameHub` broadcasts `SlipSubmitted` (`{ PlayerId, CardId, Success }`) to the SignalR lobby group.
1. **User Action**: Player clicks **Submit Slip** on `GameBoardPage` (`GameBoardViewModel.SubmitSlipAsync(card)`).
2. **Online / Offline Branching**:
- **If Connected (`_signalR.IsConnected`)**: Calls `SignalRService.SubmitSlipAsync(gameId, playerId, cardId)` directly. `GameHub.SubmitSlip()` validates ownership and updates `PlayerCard.IsUsed = true`. `GameHub` broadcasts `SlipSubmitted` to the lobby group.
- **If Offline (`!_signalR.IsConnected`)**: Calls `GameStateService.EnqueueActionAsync("SubmitSlip", gameId, playerId, cardId)`. Action is serialized into `QueuedGameAction` and saved in `Preferences`. UI updates status to `"Offline: Aktion wurde zwischengespeichert."`
---
## 3. Slip Challenge & Resolution Workflow
If another player suspects that a submitted phrase or recent conversation statement was an invalid slip, they can challenge the slipper.
If an opponent suspects that a submitted phrase or recent conversation statement was an invalid slip, they can challenge the slipper.
### Step 1: Challenge Initiation
1. Player B (Challenger) calls `GameHub.ChallengeSlip(gameId, challengingPlayerId, targetCardId)`.
2. `GameHub` verifies Player B's identity (`ValidatePlayerAccessAsync`).
3. `GameService.CreateChallengeAsync()` creates a `SlipChallenge` entity with `ChallengeStatus.Pending`.
4. `GameHub` broadcasts `SlipChallenged` (`SlipChallengeDto`) to the lobby group.
5. `GameHub` sends a targeted `ChallengeReceived` message directly to Player A's `ConnectionId`.
1. Challenger clicks **Challenge** on `GameBoardPage` (`GameBoardViewModel.ChallengeCardAsync(card)`).
2. If online, calls `SignalRService.ChallengeSlipAsync(gameId, playerId, cardId)`.
3. `GameHub.ChallengeSlip()` verifies challenger identity (`ValidatePlayerAccessAsync`).
4. `GameService.CreateChallengeAsync()` creates a `SlipChallenge` entity with `ChallengeStatus.Pending`.
5. `GameHub` broadcasts `SlipChallenged` (`SlipChallengeDto`) to the group and unicasts `ChallengeReceived` directly to target player's `ConnectionId`.
6. Client target player sees `ChallengeNotificationOverlay` on `GameBoardPage`.
### Step 2: Challenge Resolution
Player A (the accused) responds by admitting or denying the false slip:
The accused player responds via `ChallengeNotificationOverlay`:
1. Player A calls `GameHub.ResolveChallenge(challengeId, approved)`.
2. `GameHub` executes `ValidateChallengeTargetAccessAsync(challengeId, authUserId)` to guarantee that *only* the accused player can resolve the challenge.
3. `GameService.ResolveChallengeAsync()` executes penalty logic:
- **`approved = true` (Justified Accusation / Legitimate Catch)**: The accused admits the phrase was a fake slip. `SlipChallenge.Status` is set to `Approved`. The card remains with the accused player.
- **`approved = false` (False Accusation / Wrong Penalty)**: The accused denies the charge (the slip was valid). `SlipChallenge.Status` is set to `Rejected`. As a penalty for making a false accusation, the phrase card is reassigned to the challenger: `targetCard.PlayerId = challenge.ChallengingPlayerId`. The challenger now holds >5 cards in their hand.
4. `GameHub` broadcasts `ChallengeResolved` to the lobby group.
### Real-time Sequence Diagram
1. Accused player calls `GameBoardViewModel.ResolveChallengeAsync(challengeId, approved)`.
2. `SignalRService.ResolveChallengeAsync(challengeId, approved)` calls `GameHub.ResolveChallenge()`.
3. `GameHub` executes `ValidateChallengeTargetAccessAsync(challengeId, authUserId)` to guarantee *only* the accused player can resolve the challenge.
4. `GameService.ResolveChallengeAsync()` executes penalty logic:
- **`approved = true` (Justified Accusation / Legitimate Catch)**: The accused admits the phrase was a fake slip. `SlipChallenge.Status` is set to `Approved`. The card remains with accused player.
- **`approved = false` (False Accusation / Wrong Penalty)**: The accused denies the charge. `SlipChallenge.Status` is set to `Rejected`. As a penalty for making a false accusation, the phrase card is reassigned to the challenger: `targetCard.PlayerId = challenge.ChallengingPlayerId`.
5. `GameHub` broadcasts `ChallengeResolved` to the group.
```mermaid
sequenceDiagram
autonumber
actor Slipper as Player A (Accused)
actor Challenger as Player B (Challenger)
actor Slipper as Accused Player A
actor Challenger as Challenger Player B
participant GameBoardVM as GameBoardViewModel
participant Hub as GameHub
participant Service as GameService
participant DB as SlipItInDbContext
Note over Slipper, Challenger: Active Gameplay
Slipper->>Hub: SubmitSlip(gameId, playerAId, cardId)
Hub->>Service: SubmitSlipAsync(gameId, playerAId, cardId)
Slipper->>GameBoardVM: SubmitSlipAsync(card)
GameBoardVM->>Hub: SubmitSlip(gameId, playerAId, cardId)
Hub->>Service: SubmitSlipAsync()
Service->>DB: Set PlayerCard.IsUsed = true
Hub-->>Challenger: Broadcast "SlipSubmitted"
Note over Challenger: Suspects invalid phrase
Challenger->>Hub: ChallengeSlip(gameId, playerBId, targetCardId)
Challenger->>GameBoardVM: ChallengeCardAsync(card)
GameBoardVM->>Hub: ChallengeSlip(gameId, playerBId, cardId)
Hub->>Service: CreateChallengeAsync()
Service->>DB: Insert SlipChallenge (Status = Pending)
Hub-->>Challenger: Broadcast "SlipChallenged" (SlipChallengeDto)
Hub-->>Slipper: Unicast "ChallengeReceived" to ConnectionId
Hub-->>Challenger: Broadcast "SlipChallenged"
Hub-->>Slipper: Unicast "ChallengeReceived"
Note over Slipper: Accused resolves accusation
Slipper->>Hub: ResolveChallenge(challengeId, approved)
Note over Slipper: Notification Overlay Displays
Slipper->>GameBoardVM: ResolveChallengeAsync(challengeId, approved)
GameBoardVM->>Hub: ResolveChallenge(challengeId, approved)
Hub->>Hub: ValidateChallengeTargetAccessAsync()
Hub->>Service: ResolveChallengeAsync(challengeId, approved)
Hub->>Service: ResolveChallengeAsync()
alt approved == false (False Accusation)
Service->>DB: Update targetCard.PlayerId = PlayerBId (Penalty)
Service->>DB: Reassign card.PlayerId = PlayerBId
Service->>DB: Set Challenge.Status = Rejected
else approved == true (Justified Catch)
Service->>DB: Set Challenge.Status = Approved
end
Hub-->>Challenger: Broadcast "ChallengeResolved"
```
*Sequence diagram showing phrase submission, challenge initiation, target notification, and penalty resolution.*
---
## 4. SignalR Hub Event Reference
## 4. Offline Action Queueing & Reconnection Resync Workflow
Summary of server-to-client events emitted by `GameHub.cs`:
When network connection is temporarily interrupted during gameplay, SlipItIn ensures action durability and smooth state synchronization:
```mermaid
sequenceDiagram
autonumber
actor User as Player
participant GameBoardVM as GameBoardViewModel
participant GameStateSvc as GameStateService
participant LocalStorage as LocalStorageService
participant SignalRSvc as SignalRService
participant Hub as GameHub
Note over User, SignalRSvc: Network Disconnected (Offline)
User->>GameBoardVM: SubmitSlipAsync(card)
GameBoardVM->>GameStateSvc: EnqueueActionAsync("SubmitSlip", args)
GameStateSvc->>LocalStorage: SaveQueuedActionsAsync(queuedList)
LocalStorage-->>GameBoardVM: Saved to Preferences
Note over SignalRSvc: Network Restored
SignalRSvc->>Hub: Automatic Reconnect (Exponential Backoff)
SignalRSvc->>GameStateSvc: ConnectionStateChanged(true)
GameStateSvc->>GameStateSvc: ResyncAsync()
GameStateSvc->>SignalRSvc: RequestGameStateAsync(gameId)
SignalRSvc->>Hub: RequestGameState(gameId)
Hub-->>GameStateSvc: GameStateUpdated (Fresh DTO)
GameStateSvc->>GameStateSvc: FlushQueuedActionsAsync()
loop For each QueuedGameAction
GameStateSvc->>SignalRSvc: SendQueuedActionAsync(action)
SignalRSvc->>Hub: InvokeCoreAsync(action.Method, action.Args)
end
GameStateSvc->>LocalStorage: ClearQueuedActionsAsync()
```
*Sequence diagram showing offline action queueing in local preferences, automatic reconnection, state request, and queued action replay.*
---
## 5. SignalR Hub Event Reference
Summary of server-to-client events emitted by `GameHub.cs` and handled by `SignalRService`:
| Event Name | Scope | Payload | Trigger |
|---|---|---|---|
| `LobbyCreated` | Caller | `{ GameId, LobbyCode }` | Host calls `CreateLobby()` |
| `PlayerJoined` | Group | `GameStateDto` | Player calls `JoinLobby()` |
| `GameStateUpdated` | Group | `GameStateDto` | Player calls `PlayerReady()` |
| `GameStateUpdated` | Group | `GameStateDto` | Player calls `PlayerReady()` or `RequestGameState()` |
| `GameStarted` | Group | `{ GameId }` | Host calls `StartGame()` |
| `PlayerHandUpdated` | Unicast | `PlayerHandDto` | Hand dealt or updated |
| `SlipSubmitted` | Group | `{ PlayerId, CardId, Success }` | Player calls `SubmitSlip()` |
@@ -123,3 +177,21 @@ Summary of server-to-client events emitted by `GameHub.cs`:
| `ChallengeReceived` | Unicast | `SlipChallengeDto` | Direct alert to target player |
| `ChallengeResolved` | Group | `SlipChallengeDto` | Target calls `ResolveChallenge()` |
| `Error` | Caller | `{ Message }` | Any authorization or engine exception |
---
## 6. Guidance for Future Agents & Developers
- **When to Consult**: Refer to this document when adding new game interactions, modifying UI challenge flows, changing reconnection strategies, or replaying offline actions.
- **Invariants**:
- `FlushQueuedActionsAsync()` MUST execute in strictly sequential order based on `CreatedAtUtc`.
- `ResolveChallenge` MUST throw `UnauthorizedAccessException` if invoked by anyone other than the target player.
- **Primary Source Files**:
- `SlipItIn/ViewModels/GameBoardViewModel.cs`
- `SlipItIn/Services/GameStateService.cs`
- `SlipItIn/Services/SignalRService.cs`
- `SlipItIn.Server/Hubs/GameHub.cs`
- **Minimal Validation Command**:
```bash
dotnet build SlipItIn.slnx
```