Neue Skills, Referenzen & OpenWiki-Doku integriert
Umfangreiche Erweiterung der Skill-Bibliothek: Neue Skills für Humanisierung (Englisch/PT-BR), Design-Validierung, AI-SEO und Coolify-Deployment inkl. Regelwerke, Presets, Pattern-Referenzen, Testfälle und Automatisierungsskripte. Zusätzliche Skills für Revenue-Centric Design, Pier Cloud, OKF, Lebenslauf- und LinkedIn-Optimierung sowie zahlreiche Referenzdateien, Checklisten und YAML/JSON/Markdown-Templates. Einführung einer vollständigen OpenWiki-Dokumentation mit Architektur-, Domain- und Workflow-Beschreibungen, zentralem Index und automatisierten Updates. Modularer Aufbau, restriktive Lizenzen und umfassende Qualitäts- und Evaluationsmechanismen für alle neuen Inhalte.
This commit is contained in:
6
openwiki/.last-update.json
Normal file
6
openwiki/.last-update.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"updatedAt": "2026-07-26T11:33:42.070Z",
|
||||
"command": "init",
|
||||
"gitHead": "070727d5cd1fff7fdd7e0db903f1c696b0045ecd",
|
||||
"model": "gemini-3.6-flash"
|
||||
}
|
||||
1
openwiki/INSTRUCTIONS.md
Normal file
1
openwiki/INSTRUCTIONS.md
Normal file
@@ -0,0 +1 @@
|
||||
A code wiki for this local repository. Prioritize a concise quickstart, architecture overview, source map, key workflows, domain concepts, operations/runbook notes, testing guidance, and integration points. Inspect git history to understand reasoning behind code changes and the progression of the repository. Keep pages grounded in the repository structure and recent code changes. Prefer practical navigation for engineers over generic summaries.
|
||||
3
openwiki/architecture/index.md
Normal file
3
openwiki/architecture/index.md
Normal file
@@ -0,0 +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.
|
||||
129
openwiki/architecture/overview.md
Normal file
129
openwiki/architecture/overview.md
Normal file
@@ -0,0 +1,129 @@
|
||||
---
|
||||
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]
|
||||
---
|
||||
|
||||
# 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.
|
||||
|
||||
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).
|
||||
|
||||
---
|
||||
|
||||
## 1. .NET Aspire Orchestration Model
|
||||
|
||||
The solution uses **.NET Aspire** to orchestrate application resources, services, and infrastructure dependencies:
|
||||
|
||||
- **AppHost (`SlipItIn.AppHost/AppHost.cs`)**:
|
||||
- Provisions a PostgreSQL database container (`AddPostgres("pgsql").AddDatabase("postgresdb")`).
|
||||
- Registers the backend project `SlipItIn.Server` with a direct reference to the PostgreSQL resource.
|
||||
- Registers the client project `SlipItIn` with service discovery references to `SlipItIn.Server`.
|
||||
- **Service Defaults (`SlipItIn.ServiceDefaults/Extensions.cs`)**:
|
||||
- Configures **OpenTelemetry** logging, metrics (AspNetCore, HttpClient, Runtime), and tracing.
|
||||
- Exposes standardized health check endpoints (`/health` and `/alive`).
|
||||
- Enforces automatic HTTP resilience (`AddStandardResilienceHandler()`) and service discovery.
|
||||
|
||||
---
|
||||
|
||||
## 2. ASP.NET Core Backend Architecture (`SlipItIn.Server`)
|
||||
|
||||
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
|
||||
|
||||
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`).
|
||||
- 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`**:
|
||||
- Real-time SignalR hub mapped to `/hubs/game`.
|
||||
- Annotated with `[Authorize]` to reject unauthenticated WebSocket connections.
|
||||
- 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.
|
||||
|
||||
---
|
||||
|
||||
## 3. JWT Authentication & SignalR Security
|
||||
|
||||
In real-time SignalR applications, clients can attempt to spoof player identifiers. SlipItIn eliminates this vulnerability through strict claim validation and authorization checks.
|
||||
|
||||
### SignalR Token Extraction
|
||||
Since WebSockets cannot pass custom HTTP headers during connection handshakes, `Program.cs` configures JWT query parameter extraction:
|
||||
|
||||
```csharp
|
||||
options.Events = new JwtBearerEvents
|
||||
{
|
||||
OnMessageReceived = context =>
|
||||
{
|
||||
var accessToken = context.Request.Query["access_token"];
|
||||
var path = context.HttpContext.Request.Path;
|
||||
if (!string.IsNullOrEmpty(accessToken) && path.StartsWithSegments("/hubs"))
|
||||
{
|
||||
context.Token = accessToken;
|
||||
}
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### Claims Validation & Access Control in `GameHub.cs`
|
||||
Inside `GameHub.cs`, claims are extracted directly from the authenticated SignalR context:
|
||||
|
||||
* `GetAuthenticatedUserId()`: Extracts `Context.User?.FindFirst(ClaimTypes.NameIdentifier)`. Throws `UnauthorizedAccessException` if missing or invalid.
|
||||
* `ValidatePlayerAccessAsync(playerId, authUserId)`: Queries the database to verify that `Player.UserId` matches the authenticated user ID.
|
||||
* `ValidateHostAccessAsync(gameId, authUserId)`: Verifies that `Game.HostId` matches the authenticated user ID before allowing lobby configuration or game start.
|
||||
* `ValidateChallengeTargetAccessAsync(challengeId, authUserId)`: Ensures only the accused player can resolve a challenge against them.
|
||||
|
||||
### Real-Time Auth Flow Diagram
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
autonumber
|
||||
actor Client as MAUI Client
|
||||
participant Auth as AuthController
|
||||
participant Hub as GameHub
|
||||
participant Service as GameService
|
||||
participant DB as SlipItInDbContext
|
||||
|
||||
Client->>Auth: POST /api/auth/login
|
||||
Auth->>DB: Query User & Verify BCrypt Hash
|
||||
Auth-->>Client: 200 OK (JWT Access Token)
|
||||
Client->>Hub: Connect WebSocket /hubs/game?access_token=JWT
|
||||
Hub->>Hub: Validate JWT Signature & Claims
|
||||
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)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. 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`).
|
||||
|
||||
### Resolution via `IDbContextFactory`
|
||||
SlipItIn solves this by registering EF Core with `AddDbContextFactory<SlipItInDbContext>`:
|
||||
|
||||
* Every method call inside `GameHub.cs`, `AuthController.cs`, and `GameService.cs` creates a short-lived, isolated `DbContext` session using `using var context = _contextFactory.CreateDbContext()`.
|
||||
* **Benefit**: Thread-safe database operations across simultaneous challenges, card transfers, and lobby updates without race conditions.
|
||||
|
||||
---
|
||||
|
||||
## 5. Data Privacy Isolation Model
|
||||
|
||||
To prevent players from inspecting opponent cards via network trace analysis, SlipItIn strictly separates public and private data DTOs:
|
||||
|
||||
* **`GameStateDto` (Public)**: Broadcast to all players in a lobby. Contains public game information (`GameId`, `LobbyCode`, `Status`, `CurrentRound`, `RoundTimeRemaining`) and player summaries (`PlayerId`, `Username`, `Score`, `IsReady`, `CardCount`). **Crucially, no card text is included.**
|
||||
* **`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.
|
||||
171
openwiki/domain/game-mechanics.md
Normal file
171
openwiki/domain/game-mechanics.md
Normal file
@@ -0,0 +1,171 @@
|
||||
---
|
||||
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]
|
||||
---
|
||||
|
||||
# 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.
|
||||
|
||||
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/`).
|
||||
|
||||
---
|
||||
|
||||
## 1. Entity Relationship Diagram (ERD)
|
||||
|
||||
The database schema is managed via Entity Framework Core (`SlipItInDbContext`) targeting PostgreSQL.
|
||||
|
||||
```mermaid
|
||||
erDiagram
|
||||
User {
|
||||
int Id PK
|
||||
string Username
|
||||
string Email
|
||||
string PasswordHash
|
||||
bool IsActive
|
||||
}
|
||||
Game {
|
||||
int Id PK
|
||||
string LobbyCode
|
||||
int HostId FK
|
||||
GameStatus Status
|
||||
int MaxPlayers
|
||||
int RoundDurationSeconds
|
||||
}
|
||||
Player {
|
||||
int Id PK
|
||||
int UserId FK
|
||||
int GameId FK
|
||||
int Score
|
||||
bool IsReady
|
||||
string ConnectionId
|
||||
}
|
||||
Phrase {
|
||||
int Id PK
|
||||
string Text
|
||||
int CreatorId FK
|
||||
bool IsActive
|
||||
}
|
||||
GameRound {
|
||||
int Id PK
|
||||
int GameId FK
|
||||
int RoundNumber
|
||||
RoundStatus Status
|
||||
}
|
||||
PlayerCard {
|
||||
int Id PK
|
||||
int PlayerId FK
|
||||
int PhraseId FK
|
||||
int GameRoundId FK
|
||||
bool IsUsed
|
||||
}
|
||||
SlipChallenge {
|
||||
int Id PK
|
||||
int GameRoundId FK
|
||||
int ChallengingPlayerId FK
|
||||
int TargetPlayerId FK
|
||||
int TargetCardId FK
|
||||
ChallengeStatus Status
|
||||
}
|
||||
|
||||
User ||--o{ Game : "hosts"
|
||||
User ||--o{ Player : "participates as"
|
||||
User ||--o{ Phrase : "creates"
|
||||
Game ||--o{ Player : "contains"
|
||||
Game ||--o{ GameRound : "has"
|
||||
Player ||--o{ PlayerCard : "holds"
|
||||
Phrase ||--o{ PlayerCard : "used in"
|
||||
GameRound ||--o{ PlayerCard : "active in"
|
||||
GameRound ||--o{ SlipChallenge : "contains"
|
||||
Player ||--o{ SlipChallenge : "challenges"
|
||||
Player ||--o{ SlipChallenge : "target of"
|
||||
PlayerCard ||--o{ SlipChallenge : "targeted by"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. Core Domain Entities (`SlipItIn.Shared/Models`)
|
||||
|
||||
### `User`
|
||||
Represents an authenticated account.
|
||||
* **Properties**: `Id`, `Username`, `Email`, `PasswordHash` (BCrypt), `CreatedAt`, `IsActive`.
|
||||
* **Relations**: Navigation properties to created `Game` instances, player entries (`Players`), and created phrases (`Phrases`).
|
||||
|
||||
### `Game`
|
||||
Represents a multiplayer game session / lobby.
|
||||
* **Properties**: `Id`, `LobbyCode` (6-char alphanumeric), `HostId`, `Status` (`GameStatus`), `CreatedAt`, `StartedAt`, `EndedAt`, `MaxPlayers` (default 8), `RoundDurationSeconds` (30-60s).
|
||||
* **Relations**: `Host` (`User`), `Players` (`ICollection<Player>`), `Rounds` (`ICollection<GameRound>`).
|
||||
|
||||
### `Player`
|
||||
Represents a user's participation inside a specific `Game`.
|
||||
* **Properties**: `Id`, `UserId`, `GameId`, `Score`, `SuccessfulSlips`, `FailedSlips`, `JoinedAt`, `IsReady`, `ConnectionId` (active SignalR connection ID).
|
||||
* **Relations**: `User`, `Game`, `Cards` (`ICollection<PlayerCard>`).
|
||||
|
||||
### `Phrase`
|
||||
Represents a secret phrase or sentence created for the game deck.
|
||||
* **Properties**: `Id`, `Text`, `CreatorId`, `CreatedAt`, `IsActive`.
|
||||
* **Relations**: `Creator` (`User`), `PlayerCards` (`ICollection<PlayerCard>`).
|
||||
|
||||
### `GameRound`
|
||||
Tracks a distinct round within a game.
|
||||
* **Properties**: `Id`, `GameId`, `RoundNumber`, `Status` (`RoundStatus`), `StartedAt`, `EndedAt`.
|
||||
* **Relations**: `Game`, `ActiveCards` (`ICollection<PlayerCard>`), `Challenges` (`ICollection<SlipChallenge>`).
|
||||
|
||||
### `PlayerCard`
|
||||
Represents a specific phrase card dealt to a player for a round.
|
||||
* **Properties**: `Id`, `PlayerId`, `PhraseId`, `GameRoundId`, `IsUsed` (boolean flag set when player submits slip), `AssignedAt`.
|
||||
* **Relations**: `Player`, `Phrase`, `GameRound`.
|
||||
|
||||
### `SlipChallenge`
|
||||
Represents an accusation made by one player against another player's submitted slip.
|
||||
* **Properties**: `Id`, `GameRoundId`, `ChallengingPlayerId`, `TargetPlayerId`, `TargetCardId`, `Status` (`ChallengeStatus`), `CreatedAt`, `ResolvedAt`.
|
||||
* **Relations**: `GameRound`, `ChallengingPlayer`, `TargetPlayer`, `TargetCard`.
|
||||
|
||||
---
|
||||
|
||||
## 3. Enumerations & State Lifecycles
|
||||
|
||||
### Game Status (`GameStatus`)
|
||||
```mermaid
|
||||
stateDiagram-v2
|
||||
[*] --> Lobby: CreateGameAsync()
|
||||
Lobby --> InProgress: StartGameAsync() (Host only)
|
||||
InProgress --> Completed: All rounds finished
|
||||
Lobby --> Cancelled: Host cancels
|
||||
InProgress --> Cancelled: Session abort
|
||||
```
|
||||
|
||||
* **`Lobby`**: Waiting for players to join and set ready status.
|
||||
* **`InProgress`**: Active gameplay with dealt cards and active rounds.
|
||||
* **`Completed`**: Game finished, final scores tallied.
|
||||
* **`Cancelled`**: Lobby or game terminated early.
|
||||
|
||||
### Round Status (`RoundStatus`)
|
||||
```mermaid
|
||||
stateDiagram-v2
|
||||
[*] --> Waiting: StartGame / New Round
|
||||
Waiting --> Active: DealCardsAsync()
|
||||
Active --> Resolving: Challenge submitted
|
||||
Resolving --> Active: Challenge resolved
|
||||
Active --> Completed: Timer expires / All phrases used
|
||||
```
|
||||
|
||||
### Challenge Status (`ChallengeStatus`)
|
||||
* **`Pending`**: Accusation registered, awaiting response from the accused player.
|
||||
* **`Approved`**: Accusation confirmed (the phrase was indeed an invalid slip). The card remains with the accused player.
|
||||
* **`Rejected`**: Accusation rejected (the slip was legitimate). As a penalty, the card is transferred to the accuser's hand (`PlayerCard.PlayerId = ChallengingPlayerId`).
|
||||
|
||||
---
|
||||
|
||||
## 4. 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 |
|
||||
|---|---|---|
|
||||
| `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`. |
|
||||
3
openwiki/domain/index.md
Normal file
3
openwiki/domain/index.md
Normal file
@@ -0,0 +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.
|
||||
15
openwiki/index.md
Normal file
15
openwiki/index.md
Normal file
@@ -0,0 +1,15 @@
|
||||
---
|
||||
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.
|
||||
|
||||
# Directories
|
||||
|
||||
- [architecture](architecture/)
|
||||
- [domain](domain/)
|
||||
- [operations](operations/)
|
||||
- [workflows](workflows/)
|
||||
3
openwiki/operations/index.md
Normal file
3
openwiki/operations/index.md
Normal file
@@ -0,0 +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.
|
||||
104
openwiki/operations/runbook.md
Normal file
104
openwiki/operations/runbook.md
Normal file
@@ -0,0 +1,104 @@
|
||||
---
|
||||
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]
|
||||
---
|
||||
|
||||
# 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 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.
|
||||
|
||||
---
|
||||
|
||||
## 1. Local Development Environment Setup
|
||||
|
||||
### Prerequisites
|
||||
* **.NET 10 SDK** (Installed and verified via `dotnet --version`).
|
||||
* **Docker Desktop** or **Podman** (Required by .NET Aspire to run the PostgreSQL container).
|
||||
* **Workloads**: .NET Aspire workload and .NET MAUI workload (`dotnet workload install aspire maui`).
|
||||
|
||||
### Starting the Distributed Application with Aspire
|
||||
|
||||
Run the Aspire orchestrator project:
|
||||
|
||||
```bash
|
||||
dotnet run --project SlipItIn.AppHost/SlipItIn.AppHost.csproj
|
||||
```
|
||||
|
||||
**What happens during launch:**
|
||||
1. Aspire launches a PostgreSQL container (`pgsql`/`postgresdb`).
|
||||
2. Aspire builds and starts `SlipItIn.Server`.
|
||||
3. `Program.cs` automatically executes EF Core migrations (`db.Database.Migrate()`), creating database tables if they do not exist.
|
||||
4. Aspire Dashboard opens in your web browser, displaying live metrics, OpenTelemetry traces, and structured logs for all services.
|
||||
|
||||
---
|
||||
|
||||
## 2. Configuration & Secrets Management
|
||||
|
||||
Configuration settings are loaded from `appsettings.json` and environment variables.
|
||||
|
||||
### Key Configuration Keys (`SlipItIn.Server`)
|
||||
|
||||
| Key | Default Value | Description |
|
||||
|---|---|---|
|
||||
| `Jwt:Key` | `YourSuperSecretKeyThatIsAtLeast32CharactersLong!` | Secret signing key for JWT tokens (Must be >= 256 bits). |
|
||||
| `Jwt:Issuer` | `SlipItInServer` | Token issuer claim. |
|
||||
| `Jwt:Audience` | `SlipItInClient` | Token audience claim. |
|
||||
| `Jwt:ExpirationMinutes` | `1440` (24 hours) | JWT token lifespan. |
|
||||
| `ConnectionStrings:postgresdb` | Configured via Aspire | PostgreSQL connection string. |
|
||||
|
||||
### Production Configuration Security
|
||||
In production deployment, override `Jwt:Key` using environment variables or user secrets (`dotnet user-secrets`):
|
||||
|
||||
```bash
|
||||
dotnet user-secrets set "Jwt:Key" "YOUR_HIGH_ENTROPY_PRODUCTION_SECRET_KEY_HERE" --project SlipItIn.Server
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. Entity Framework Core Migrations
|
||||
|
||||
When altering models in `SlipItIn.Shared/Models/` or `SlipItInDbContext.cs`:
|
||||
|
||||
### Adding a New Migration
|
||||
Run the EF Core CLI from the repository root:
|
||||
|
||||
```bash
|
||||
dotnet ef migrations add <MigrationName> --project SlipItIn.Server --startup-project SlipItIn.Server
|
||||
```
|
||||
|
||||
### Applying Migrations Manually
|
||||
While `Program.cs` applies migrations at startup (`db.Database.Migrate()`), migrations can also be manually applied via command line:
|
||||
|
||||
```bash
|
||||
dotnet ef database update --project SlipItIn.Server --startup-project SlipItIn.Server
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. Testing Guidance & Verification Scenarios
|
||||
|
||||
When developing or extending SlipItIn features, verify the core architecture through targeted test scenarios specified in `Agents/Architecture.md`:
|
||||
|
||||
### 1. JWT Authentication & Claims Tests
|
||||
* **Test Objective**: Verify `GameHub` rejects unauthenticated WebSocket connections or missing token query parameters.
|
||||
* **Verification**: Connect to `/hubs/game` without `?access_token=...` or with an expired token. Confirm SignalR connection is terminated with 401 Unauthorized.
|
||||
|
||||
### 2. Player Access Authorization Tests
|
||||
* **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
|
||||
* **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`.
|
||||
84
openwiki/quickstart.md
Normal file
84
openwiki/quickstart.md
Normal file
@@ -0,0 +1,84 @@
|
||||
---
|
||||
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]
|
||||
---
|
||||
|
||||
# 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.
|
||||
|
||||
---
|
||||
|
||||
## 1. System Overview & Architecture Snapshot
|
||||
|
||||
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.ServiceDefaults/ # Aspire OpenTelemetry, Health Checks & Service Discovery
|
||||
└── SlipItIn/ # .NET MAUI Client App (Android, iOS, MacCatalyst, Windows)
|
||||
```
|
||||
|
||||
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).
|
||||
|
||||
```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
|
||||
AppHost -->|Provisions| DB
|
||||
Server -->|Uses Defaults| ServiceDefaults[SlipItIn.ServiceDefaults]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 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).
|
||||
|
||||
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
|
||||
|
||||
Explore specific documentation sections for technical details:
|
||||
|
||||
- **[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.
|
||||
|
||||
---
|
||||
|
||||
## 4. Key Architectural Rules for Developers & Agents
|
||||
|
||||
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)).
|
||||
|
||||
---
|
||||
|
||||
## 5. Backlog
|
||||
|
||||
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).
|
||||
69
openwiki/source-map.md
Normal file
69
openwiki/source-map.md
Normal file
@@ -0,0 +1,69 @@
|
||||
---
|
||||
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]
|
||||
---
|
||||
|
||||
# Source Code Map & Navigation Directory
|
||||
|
||||
This directory maps every major project, folder, and source file in the SlipItIn repository to its system domain and technical responsibility.
|
||||
|
||||
This navigation map [indexes backend architecture files described in](/openwiki/architecture/overview.md) the system architecture, [indexes domain model files defined in](/openwiki/domain/game-mechanics.md) the domain mechanics guide, [indexes workflow implementation files detailed in](/openwiki/workflows/slip-and-challenge.md) the workflow guide, and [indexes operational configuration files documented in](/openwiki/operations/runbook.md) the operations runbook.
|
||||
|
||||
---
|
||||
|
||||
## 1. Solution Projects (`SlipItIn.slnx`)
|
||||
|
||||
```
|
||||
SlipItIn.slnx
|
||||
├── SlipItIn.AppHost/ # Aspire distributed orchestrator
|
||||
├── SlipItIn.Server/ # Web API & SignalR real-time server
|
||||
├── SlipItIn.Shared/ # Shared models & data transfer objects
|
||||
├── SlipItIn.ServiceDefaults/ # Aspire OpenTelemetry & health checks
|
||||
├── SlipItIn/ # .NET MAUI multi-platform client
|
||||
└── Agents/ # Architecture & planning briefs
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. Directory & Source File Map
|
||||
|
||||
### `.NET Aspire Orchestration` (`SlipItIn.AppHost`)
|
||||
- **`AppHost.cs`**: Orchestrates application dependencies. Configures PostgreSQL (`AddPostgres("pgsql")`), references `SlipItIn.Server`, and links the MAUI client.
|
||||
- **`appsettings.json`**: Aspire orchestrator configuration.
|
||||
|
||||
### `Service Defaults & Telemetry` (`SlipItIn.ServiceDefaults`)
|
||||
- **`Extensions.cs`**: Implements `AddServiceDefaults()` and `ConfigureOpenTelemetry()`. Configures OpenTelemetry logging, metrics, tracing filters (excluding `/health` and `/alive`), service discovery, and HTTP resilience handlers.
|
||||
|
||||
### `Backend Web API & SignalR Server` (`SlipItIn.Server`)
|
||||
- **`Program.cs`**: Entrypoint for ASP.NET Core server. Registers EF Core `IDbContextFactory`, JWT authentication middleware, SignalR query parameter token parsing, Npgsql PostgreSQL data source, OpenAPI, CORS policies, and automatic EF migrations.
|
||||
- **`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`).
|
||||
- **`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.
|
||||
|
||||
### `Shared Domain & DTOs` (`SlipItIn.Shared`)
|
||||
- **`Models/User.cs`**: Entity storing user credentials, BCrypt password hashes, and user state.
|
||||
- **`Models/Game.cs`**: Entity storing lobby codes, status (`GameStatus`), host ID, and duration parameters.
|
||||
- **`Models/Player.cs`**: Entity tracking player scores, ready status, and SignalR connection IDs.
|
||||
- **`Models/Phrase.cs`**: Entity storing phrase text and creator metadata.
|
||||
- **`Models/GameRound.cs`**: Entity tracking round numbers and status (`RoundStatus`).
|
||||
- **`Models/PlayerCard.cs`**: Entity linking phrases to players for specific rounds (`IsUsed` flag).
|
||||
- **`Models/SlipChallenge.cs`**: Entity recording accusations, challenging/target player IDs, and challenge status (`ChallengeStatus`).
|
||||
- **`DTOs/AuthDtos.cs`**: DTOs for authentication (`RegisterRequestDto`, `LoginRequestDto`, `AuthResponseDto`).
|
||||
- **`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.
|
||||
- **`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.
|
||||
- **`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.
|
||||
3
openwiki/workflows/index.md
Normal file
3
openwiki/workflows/index.md
Normal file
@@ -0,0 +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.
|
||||
125
openwiki/workflows/slip-and-challenge.md
Normal file
125
openwiki/workflows/slip-and-challenge.md
Normal file
@@ -0,0 +1,125 @@
|
||||
---
|
||||
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]
|
||||
---
|
||||
|
||||
# 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.
|
||||
|
||||
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.
|
||||
|
||||
---
|
||||
|
||||
## 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
|
||||
```
|
||||
|
||||
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)`.
|
||||
- `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.
|
||||
- SignalR sends private `PlayerHandUpdated` notifications (`PlayerHandDto`) individually to each player's `ConnectionId`.
|
||||
|
||||
---
|
||||
|
||||
## 2. Phrase Slipping 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.
|
||||
|
||||
---
|
||||
|
||||
## 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.
|
||||
|
||||
### 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`.
|
||||
|
||||
### Step 2: Challenge Resolution
|
||||
Player A (the accused) responds by admitting or denying the false slip:
|
||||
|
||||
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
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
autonumber
|
||||
actor Slipper as Player A (Accused)
|
||||
actor Challenger as Player B (Challenger)
|
||||
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)
|
||||
Service->>DB: Set PlayerCard.IsUsed = true
|
||||
Hub-->>Challenger: Broadcast "SlipSubmitted"
|
||||
|
||||
Note over Challenger: Suspects invalid phrase
|
||||
Challenger->>Hub: ChallengeSlip(gameId, playerBId, targetCardId)
|
||||
Hub->>Service: CreateChallengeAsync()
|
||||
Service->>DB: Insert SlipChallenge (Status = Pending)
|
||||
Hub-->>Challenger: Broadcast "SlipChallenged" (SlipChallengeDto)
|
||||
Hub-->>Slipper: Unicast "ChallengeReceived" to ConnectionId
|
||||
|
||||
Note over Slipper: Accused resolves accusation
|
||||
Slipper->>Hub: ResolveChallenge(challengeId, approved)
|
||||
Hub->>Hub: ValidateChallengeTargetAccessAsync()
|
||||
Hub->>Service: ResolveChallengeAsync(challengeId, approved)
|
||||
alt approved == false (False Accusation)
|
||||
Service->>DB: Update targetCard.PlayerId = PlayerBId (Penalty)
|
||||
Service->>DB: Set Challenge.Status = Rejected
|
||||
else approved == true (Justified Catch)
|
||||
Service->>DB: Set Challenge.Status = Approved
|
||||
end
|
||||
Hub-->>Challenger: Broadcast "ChallengeResolved"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. SignalR Hub Event Reference
|
||||
|
||||
Summary of server-to-client events emitted by `GameHub.cs`:
|
||||
|
||||
| Event Name | Scope | Payload | Trigger |
|
||||
|---|---|---|---|
|
||||
| `LobbyCreated` | Caller | `{ GameId, LobbyCode }` | Host calls `CreateLobby()` |
|
||||
| `PlayerJoined` | Group | `GameStateDto` | Player calls `JoinLobby()` |
|
||||
| `GameStateUpdated` | Group | `GameStateDto` | Player calls `PlayerReady()` |
|
||||
| `GameStarted` | Group | `{ GameId }` | Host calls `StartGame()` |
|
||||
| `PlayerHandUpdated` | Unicast | `PlayerHandDto` | Hand dealt or updated |
|
||||
| `SlipSubmitted` | Group | `{ PlayerId, CardId, Success }` | Player calls `SubmitSlip()` |
|
||||
| `SlipChallenged` | Group | `SlipChallengeDto` | Opponent calls `ChallengeSlip()` |
|
||||
| `ChallengeReceived` | Unicast | `SlipChallengeDto` | Direct alert to target player |
|
||||
| `ChallengeResolved` | Group | `SlipChallengeDto` | Target calls `ResolveChallenge()` |
|
||||
| `Error` | Caller | `{ Message }` | Any authorization or engine exception |
|
||||
Reference in New Issue
Block a user