Update der Dokumentation
This commit is contained in:
@@ -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.
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
```
|
||||
|
||||
@@ -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
|
||||
```
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
```
|
||||
|
||||
@@ -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).
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
[Client App] LoginVM.LoginAsync() ──> REST Auth ──> Save Token in SecureStorage ──> SignalR.ConnectAsync()
|
||||
│
|
||||
[Player] JoinLobby() ──> Verify MaxPlayers & Status ───────────┤
|
||||
[Host] LobbyVM.CreateLobbyAsync() ──> SignalR.CreateLobbyAsync() ──> Server Generates Code ──┤
|
||||
│
|
||||
[Player] PlayerReady() ──> Set IsReady = true ─────────────────┼──> Broadcast GameStateUpdated
|
||||
[Player] LobbyVM.JoinLobbyAsync() ──> SignalR.JoinLobbyAsync(code) ──────────────────────────┤
|
||||
│
|
||||
[Host] StartGame() ──> Deal 5 Random Cards Per Player ─────────┴──> Unicast PlayerHandUpdated
|
||||
[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
|
||||
```
|
||||
|
||||
Reference in New Issue
Block a user