mod planning
This commit is contained in:
parent
d2ee1c5193
commit
fab82fcde8
|
|
@ -850,7 +850,8 @@ Spiele laufen im Speicher auf der DayDetails-Seite ohne Browser-Reload.
|
||||||
| Spiel-Ende | Auto-Ende + manuelle "Beenden" Option |
|
| Spiel-Ende | Auto-Ende + manuelle "Beenden" Option |
|
||||||
| Strafen-Schnellwahl | Nur im Details-Tab, nicht in Eingabe/Tafel |
|
| Strafen-Schnellwahl | Nur im Details-Tab, nicht in Eingabe/Tafel |
|
||||||
| Multi-Game | Sequentiell - mehrere Spiele pro Tag, Liste sichtbar |
|
| Multi-Game | Sequentiell - mehrere Spiele pro Tag, Liste sichtbar |
|
||||||
| Persistenz | JSON in `Game.GameData` nach Spielende speichern |
|
| Persistenz | **Nach jedem Wurf** in DB speichern (Game.GameData) |
|
||||||
|
| Multi-User | Spielfortschritt in anderen Sessions via **SignalR** sichtbar |
|
||||||
| Scheiss-Spiel Ende | Erster mit 0 Punkten **gewinnt**, andere bekommen Strafen nach Restpunkten |
|
| Scheiss-Spiel Ende | Erster mit 0 Punkten **gewinnt**, andere bekommen Strafen nach Restpunkten |
|
||||||
| Strafen-Berechnung | Fixbetrag pro Restpunkt (z.B. 0.10€ × 30 Punkte = 3.00€) |
|
| Strafen-Berechnung | Fixbetrag pro Restpunkt (z.B. 0.10€ × 30 Punkte = 3.00€) |
|
||||||
|
|
||||||
|
|
@ -862,22 +863,55 @@ Spiele laufen im Speicher auf der DayDetails-Seite ohne Browser-Reload.
|
||||||
| View-Wechsel | ViewMode-Enum + MudTabs (kein URL-Routing) |
|
| View-Wechsel | ViewMode-Enum + MudTabs (kein URL-Routing) |
|
||||||
| Spieltyp-System | IGameDefinition in Domain, Registry in Application |
|
| Spieltyp-System | IGameDefinition in Domain, Registry in Application |
|
||||||
| Pin-Komponenten | Port aus KoogleApp mit neuem Namespace |
|
| Pin-Komponenten | Port aus KoogleApp mit neuem Namespace |
|
||||||
| Persistenz | In-Memory während Spiel → DB (Game.GameData) bei Ende |
|
| Persistenz | **Nach jedem Wurf** → DB (Game.GameData JSON) |
|
||||||
|
| Recovery | Bei Page-Load: aktives Spiel aus DB laden |
|
||||||
|
| Multi-User Sync | **SignalR** für Echtzeit-Updates (Live-Tafel) |
|
||||||
|
|
||||||
## Existierende Entity
|
## Game Entity (erweitern)
|
||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
// Koogle.Domain/Entities/Game.cs - bereits vorhanden
|
// Koogle.Domain/Entities/Game.cs - erweitern
|
||||||
public class Game : BaseEntity
|
public class Game : BaseEntity
|
||||||
{
|
{
|
||||||
public string GameData { get; set; } = "{}"; // JSON für Spielergebnis
|
public string GameData { get; set; } = "{}"; // JSON für kompletten Spielzustand
|
||||||
public Guid DayId { get; set; }
|
public Guid DayId { get; set; }
|
||||||
public Day Day { get; set; }
|
public Day Day { get; set; }
|
||||||
public ICollection<GamePerson> GamePersons { get; set; }
|
public ICollection<GamePerson> GamePersons { get; set; }
|
||||||
public Guid ClubId { get; set; }
|
public Guid ClubId { get; set; }
|
||||||
public Club Club { get; set; }
|
public Club Club { get; set; }
|
||||||
|
|
||||||
|
// NEU:
|
||||||
|
public string GameType { get; set; } = ""; // "Training", "Shit"
|
||||||
|
public GameStatus Status { get; set; } // Active, Completed, Aborted
|
||||||
|
public DateTime? StartedAt { get; set; }
|
||||||
|
public DateTime? CompletedAt { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum GameStatus { Active, Completed, Aborted }
|
||||||
|
```
|
||||||
|
|
||||||
|
## GameData JSON Struktur (Beispiel Training)
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"gameType": "Training",
|
||||||
|
"throwPanelState": {
|
||||||
|
"throwsPerRound": 3,
|
||||||
|
"throwMode": "Reposition",
|
||||||
|
"totalThrowCounter": 42
|
||||||
|
},
|
||||||
|
"participants": {
|
||||||
|
"playerIds": ["guid1", "guid2"],
|
||||||
|
"currentPlayerIndex": 1
|
||||||
|
},
|
||||||
|
"gameModel": {
|
||||||
|
"playerStatistics": {
|
||||||
|
"guid1": { "throwCount": 21, "pinCount": 145, "circleCount": 2 },
|
||||||
|
"guid2": { "throwCount": 21, "pinCount": 138, "circleCount": 1 }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"undoStack": [ ... ]
|
||||||
}
|
}
|
||||||
// → GameType Property hinzufügen (string oder Enum)
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Architektur
|
## Architektur
|
||||||
|
|
@ -900,10 +934,11 @@ Web: Store/GameState/, Components/Game/
|
||||||
| ☐ | H6 | UI | Game Setup Dialog | 4 |
|
| ☐ | H6 | UI | Game Setup Dialog | 4 |
|
||||||
| ☐ | H7 | Integration | DayDetails Integration | 3 |
|
| ☐ | H7 | Integration | DayDetails Integration | 3 |
|
||||||
| ☐ | H8 | Features | Undo/Redo | 3 |
|
| ☐ | H8 | Features | Undo/Redo | 3 |
|
||||||
| ☐ | H9 | Features | Session Persistence | 2 |
|
| ☐ | H9 | Persistenz | DB Persistence & Recovery | 4 |
|
||||||
|
| ☐ | H9b | Sync | SignalR Live-Updates | 4 |
|
||||||
| ☐ | H10 | Testing | Game Tests | 3 |
|
| ☐ | H10 | Testing | Game Tests | 3 |
|
||||||
|
|
||||||
**Geschätzte Dateien: ~42 neue/modifizierte**
|
**Geschätzte Dateien: ~48 neue/modifizierte**
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
@ -1166,16 +1201,66 @@ public record ShitGameModel
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### **Phase H9: Session Persistence**
|
### **Phase H9: DB Persistence & Recovery**
|
||||||
|
|
||||||
**Dateien:**
|
**Dateien:**
|
||||||
1. `src/Koogle.Web/wwwroot/js/gameSession.js`
|
1. `src/Koogle.Application/Interfaces/IGamePersistenceService.cs`
|
||||||
2. `src/Koogle.Web/Store/GameState/GameEffects.cs` (erweitern)
|
2. `src/Koogle.Application/Services/GamePersistenceService.cs`
|
||||||
|
3. `src/Koogle.Web/Store/GameState/GameEffects.cs` (erweitern)
|
||||||
|
4. `src/Koogle.Application/DTOs/GameStateDto.cs` (JSON-Serialisierung)
|
||||||
|
|
||||||
**Mechanismus:**
|
**Methoden:**
|
||||||
- Bei State-Änderung: SessionStorage speichern
|
- `SaveGameStateAsync(Guid gameId, GameState state)` - Nach jedem Wurf
|
||||||
- Bei Page-Load: SessionStorage prüfen
|
- `LoadActiveGameAsync(Guid dayId)` - Bei Page-Load
|
||||||
- Wenn Session für aktuellen DayId existiert: Restore anbieten
|
- `GetCompletedGamesAsync(Guid dayId)` - Für Spiele-Liste
|
||||||
|
|
||||||
|
**Persistenz-Flow:**
|
||||||
|
```
|
||||||
|
1. Spiel starten → Game Entity mit Status=Active erstellen
|
||||||
|
2. Nach jedem Wurf:
|
||||||
|
- GameState → JSON serialisieren
|
||||||
|
- Game.GameData updaten (Debounced, max 1x/500ms)
|
||||||
|
3. Page Reload / Andere Session:
|
||||||
|
- LoadActiveGameAsync(dayId)
|
||||||
|
- Wenn Active Game existiert → GameState wiederherstellen
|
||||||
|
4. Spiel beenden:
|
||||||
|
- Status = Completed, CompletedAt = now
|
||||||
|
- Finales GameData speichern
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### **Phase H9b: SignalR Live-Updates**
|
||||||
|
|
||||||
|
**Dateien:**
|
||||||
|
1. `src/Koogle.Web/Hubs/GameHub.cs`
|
||||||
|
2. `src/Koogle.Web/Hubs/IGameHubClient.cs`
|
||||||
|
3. `src/Koogle.Web/Store/GameState/GameEffects.cs` (erweitern für Broadcast)
|
||||||
|
4. `src/Koogle.Web/Program.cs` (AddSignalR, MapHub)
|
||||||
|
|
||||||
|
**SignalR Hub:**
|
||||||
|
```csharp
|
||||||
|
public class GameHub : Hub<IGameHubClient>
|
||||||
|
{
|
||||||
|
public async Task JoinGame(Guid gameId)
|
||||||
|
=> await Groups.AddToGroupAsync(Context.ConnectionId, gameId.ToString());
|
||||||
|
|
||||||
|
public async Task LeaveGame(Guid gameId)
|
||||||
|
=> await Groups.RemoveFromGroupAsync(Context.ConnectionId, gameId.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface IGameHubClient
|
||||||
|
{
|
||||||
|
Task GameStateUpdated(GameStateDto state);
|
||||||
|
Task GameEnded(GameResultDto result);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Flow:**
|
||||||
|
1. Tafel-View öffnen → `JoinGame(gameId)`
|
||||||
|
2. Nach jedem Wurf: DB Save → `Clients.Group(gameId).GameStateUpdated(state)`
|
||||||
|
3. Andere Sessions empfangen Update → Reducer aktualisiert State
|
||||||
|
4. Tafel-View schließen → `LeaveGame(gameId)`
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
@ -1224,8 +1309,8 @@ public record ShitGameModel
|
||||||
## Migration
|
## Migration
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# GameType Property zu Game Entity hinzufügen
|
# Game Entity erweitern: GameType, Status, StartedAt, CompletedAt
|
||||||
dotnet ef migrations add AddGameType --project src/Koogle.Infrastructure --startup-project src/Koogle.Web --context AppDbContext --output-dir Data/Migrations
|
dotnet ef migrations add ExtendGameEntity --project src/Koogle.Infrastructure --startup-project src/Koogle.Web --context AppDbContext --output-dir Data/Migrations
|
||||||
|
|
||||||
dotnet ef database update -p src/Koogle.Infrastructure -s src/Koogle.Web --context AppDbContext
|
dotnet ef database update -p src/Koogle.Infrastructure -s src/Koogle.Web --context AppDbContext
|
||||||
```
|
```
|
||||||
|
|
@ -1242,6 +1327,13 @@ dotnet ef database update -p src/Koogle.Infrastructure -s src/Koogle.Web --conte
|
||||||
|
|
||||||
## Zusammenfassung Phase 2
|
## Zusammenfassung Phase 2
|
||||||
|
|
||||||
**10 Phasen (H1-H10)** → **~42 Dateien** → **Detaillierte Spielverwaltung**
|
**11 Phasen (H1-H10 + H9b)** → **~48 Dateien** → **Detaillierte Spielverwaltung**
|
||||||
|
|
||||||
|
**Kernfeatures:**
|
||||||
|
- Spiele mit Wurf-für-Wurf Eingabe
|
||||||
|
- DB-Persistenz nach jedem Wurf
|
||||||
|
- SignalR Live-Updates für Multi-User
|
||||||
|
- Recovery bei Page-Reload
|
||||||
|
- Undo/Redo für Wurf-Korrekturen
|
||||||
|
|
||||||
Bereit für Implementierung Phase H1
|
Bereit für Implementierung Phase H1
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue