From bbc6ad60802ba4e19218d1529c927c67389d973d Mon Sep 17 00:00:00 2001 From: Tim Krampitz Date: Sun, 26 Jul 2026 17:31:26 +0200 Subject: [PATCH] Architektur-Redesign: SignalR-Multiplayer & MVVM MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Umfassende Umstrukturierung der SlipItIn-App für eine moderne, zustandsbasierte und SignalR-gestützte Multiplayer-Architektur. Einführung neuer Services für Auth, API, SignalR, lokalen Speicher und Game-State-Management via Dependency Injection. Komplette Neugestaltung der Views (Login, Registrierung, Lobby, Spielbrett) mit MVVM und CommunityToolkit.Mvvm. Navigation jetzt über MAUI Shell mit expliziten Routen. Game-Logik auf Events/Messaging umgestellt, UI und Logik entkoppelt, Echtzeit-Updates integriert. SignalR für alle Spielaktionen inkl. Wiederverbindung und Offline-Queueing. Lokaler Speicher für Auth, Userdaten, GameState und Aktionen. MainPage entfernt, neue Views implementiert. Projektdatei um NuGet-Pakete und Shared-Projekt erweitert. Backend-Änderungen für direkte Spieler-Zuordnung und korrekte EF Core-Relationen. App ist nun robust, testbar und unterstützt Multiplayer- sowie Offline-Szenarien. --- SlipItIn.AppHost/AppHost.cs | 3 +- SlipItIn.Server/Services/GameService.cs | 10 +- SlipItIn/App.xaml.cs | 31 ++- SlipItIn/AppShell.xaml | 22 ++- SlipItIn/AppShell.xaml.cs | 4 +- SlipItIn/Infrastructure/ServiceHelper.cs | 16 ++ SlipItIn/MainPage.xaml | 36 ---- SlipItIn/MainPage.xaml.cs | 23 --- SlipItIn/MauiProgram.cs | 20 +- SlipItIn/Messages/ChallengeReceivedMessage.cs | 11 ++ .../Messages/ConnectionStateChangedMessage.cs | 10 + SlipItIn/Messages/ErrorOccurredMessage.cs | 10 + SlipItIn/Messages/GameStartedMessage.cs | 10 + SlipItIn/Messages/GameStateChangedMessage.cs | 11 ++ SlipItIn/Messages/LobbyCreatedMessage.cs | 10 + SlipItIn/Messages/PlayerHandChangedMessage.cs | 11 ++ SlipItIn/Models/QueuedGameAction.cs | 8 + SlipItIn/Services/ApiService.cs | 84 +++++++++ SlipItIn/Services/AppConfigurationService.cs | 12 ++ SlipItIn/Services/AuthSessionService.cs | 42 +++++ SlipItIn/Services/GameStateService.cs | 119 ++++++++++++ SlipItIn/Services/Interfaces/IApiService.cs | 10 + .../Interfaces/IAppConfigurationService.cs | 7 + .../Interfaces/IAuthSessionService.cs | 14 ++ .../Services/Interfaces/IGameStateService.cs | 17 ++ .../Interfaces/ILocalStorageService.cs | 27 +++ .../Services/Interfaces/ISignalRService.cs | 31 +++ SlipItIn/Services/LocalStorageService.cs | 110 +++++++++++ SlipItIn/Services/SignalRService.cs | 178 ++++++++++++++++++ SlipItIn/SlipItIn.csproj | 21 ++- SlipItIn/ViewModels/BaseViewModel.cs | 33 ++++ SlipItIn/ViewModels/GameBoardViewModel.cs | 177 +++++++++++++++++ SlipItIn/ViewModels/LobbyViewModel.cs | 152 +++++++++++++++ SlipItIn/ViewModels/LoginViewModel.cs | 61 ++++++ SlipItIn/ViewModels/RegisterViewModel.cs | 51 +++++ .../Views/ChallengeNotificationOverlay.xaml | 17 ++ .../ChallengeNotificationOverlay.xaml.cs | 9 + SlipItIn/Views/GameBoardPage.xaml | 49 +++++ SlipItIn/Views/GameBoardPage.xaml.cs | 13 ++ SlipItIn/Views/LobbyPage.xaml | 42 +++++ SlipItIn/Views/LobbyPage.xaml.cs | 13 ++ SlipItIn/Views/LoginPage.xaml | 23 +++ SlipItIn/Views/LoginPage.xaml.cs | 25 +++ SlipItIn/Views/RegisterPage.xaml | 23 +++ SlipItIn/Views/RegisterPage.xaml.cs | 13 ++ 45 files changed, 1544 insertions(+), 75 deletions(-) create mode 100644 SlipItIn/Infrastructure/ServiceHelper.cs delete mode 100644 SlipItIn/MainPage.xaml delete mode 100644 SlipItIn/MainPage.xaml.cs create mode 100644 SlipItIn/Messages/ChallengeReceivedMessage.cs create mode 100644 SlipItIn/Messages/ConnectionStateChangedMessage.cs create mode 100644 SlipItIn/Messages/ErrorOccurredMessage.cs create mode 100644 SlipItIn/Messages/GameStartedMessage.cs create mode 100644 SlipItIn/Messages/GameStateChangedMessage.cs create mode 100644 SlipItIn/Messages/LobbyCreatedMessage.cs create mode 100644 SlipItIn/Messages/PlayerHandChangedMessage.cs create mode 100644 SlipItIn/Models/QueuedGameAction.cs create mode 100644 SlipItIn/Services/ApiService.cs create mode 100644 SlipItIn/Services/AppConfigurationService.cs create mode 100644 SlipItIn/Services/AuthSessionService.cs create mode 100644 SlipItIn/Services/GameStateService.cs create mode 100644 SlipItIn/Services/Interfaces/IApiService.cs create mode 100644 SlipItIn/Services/Interfaces/IAppConfigurationService.cs create mode 100644 SlipItIn/Services/Interfaces/IAuthSessionService.cs create mode 100644 SlipItIn/Services/Interfaces/IGameStateService.cs create mode 100644 SlipItIn/Services/Interfaces/ILocalStorageService.cs create mode 100644 SlipItIn/Services/Interfaces/ISignalRService.cs create mode 100644 SlipItIn/Services/LocalStorageService.cs create mode 100644 SlipItIn/Services/SignalRService.cs create mode 100644 SlipItIn/ViewModels/BaseViewModel.cs create mode 100644 SlipItIn/ViewModels/GameBoardViewModel.cs create mode 100644 SlipItIn/ViewModels/LobbyViewModel.cs create mode 100644 SlipItIn/ViewModels/LoginViewModel.cs create mode 100644 SlipItIn/ViewModels/RegisterViewModel.cs create mode 100644 SlipItIn/Views/ChallengeNotificationOverlay.xaml create mode 100644 SlipItIn/Views/ChallengeNotificationOverlay.xaml.cs create mode 100644 SlipItIn/Views/GameBoardPage.xaml create mode 100644 SlipItIn/Views/GameBoardPage.xaml.cs create mode 100644 SlipItIn/Views/LobbyPage.xaml create mode 100644 SlipItIn/Views/LobbyPage.xaml.cs create mode 100644 SlipItIn/Views/LoginPage.xaml create mode 100644 SlipItIn/Views/LoginPage.xaml.cs create mode 100644 SlipItIn/Views/RegisterPage.xaml create mode 100644 SlipItIn/Views/RegisterPage.xaml.cs diff --git a/SlipItIn.AppHost/AppHost.cs b/SlipItIn.AppHost/AppHost.cs index 1e99437..cdbb743 100644 --- a/SlipItIn.AppHost/AppHost.cs +++ b/SlipItIn.AppHost/AppHost.cs @@ -1,7 +1,8 @@ var builder = DistributedApplication.CreateBuilder(args); var db = builder.AddPostgres("pgsql") - .AddDatabase("postgresdb"); + .AddDatabase("postgresdb") + ; var server = builder.AddProject("server") .WithReference(db) diff --git a/SlipItIn.Server/Services/GameService.cs b/SlipItIn.Server/Services/GameService.cs index eda875c..a7f55de 100644 --- a/SlipItIn.Server/Services/GameService.cs +++ b/SlipItIn.Server/Services/GameService.cs @@ -29,11 +29,15 @@ public class GameService : IGameService Status = GameStatus.Lobby }; - context.Games.Add(game); // Host wird automatisch als erster Spieler hinzugefügt - var hostPlayer = new Player { UserId = host.Id, GameId = game.Id, ConnectionId = connectionId }; - context.Players.Add(hostPlayer); + var hostPlayer = new Player { UserId = host.Id, ConnectionId = connectionId }; + + // Spieler direkt zur Liste des Spiels hinzufügen + game.Players.Add(hostPlayer); + + // Es reicht, nur das Game hinzuzufügen — EF Core fügt den Player automatisch mit hinzu + context.Games.Add(game); await context.SaveChangesAsync(); diff --git a/SlipItIn/App.xaml.cs b/SlipItIn/App.xaml.cs index 2c5d9dd..9574392 100644 --- a/SlipItIn/App.xaml.cs +++ b/SlipItIn/App.xaml.cs @@ -1,4 +1,5 @@ -using Microsoft.Extensions.DependencyInjection; +using SlipItIn.Infrastructure; +using SlipItIn.Services.Interfaces; namespace SlipItIn; @@ -7,10 +8,34 @@ public partial class App : Application public App() { InitializeComponent(); + _ = InitializeAsync(); } protected override Window CreateWindow(IActivationState? activationState) { - return new Window(new AppShell()); + var window = new Window(new AppShell()); + window.Resumed += async (_, _) => + { + var gameStateService = ServiceHelper.GetRequiredService(); + await gameStateService.ResyncAsync(); + }; + return window; } -} \ No newline at end of file + + private static async Task InitializeAsync() + { + var authSession = ServiceHelper.GetRequiredService(); + var signalR = ServiceHelper.GetRequiredService(); + var gameStateService = ServiceHelper.GetRequiredService(); + + await authSession.InitializeAsync(); + await gameStateService.InitializeAsync(); + + if (!string.IsNullOrWhiteSpace(authSession.AccessToken)) + { + var connected = await signalR.ConnectAsync(authSession.AccessToken); + if (connected) + await gameStateService.ResyncAsync(); + } + } +} diff --git a/SlipItIn/AppShell.xaml b/SlipItIn/AppShell.xaml index 12da11a..2d8af88 100644 --- a/SlipItIn/AppShell.xaml +++ b/SlipItIn/AppShell.xaml @@ -3,12 +3,22 @@ x:Class="SlipItIn.AppShell" xmlns="http://schemas.microsoft.com/dotnet/2021/maui" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" - xmlns:local="clr-namespace:SlipItIn" - Title="Slip It In"> + xmlns:views="clr-namespace:SlipItIn.Views"> - + + + + + + + diff --git a/SlipItIn/AppShell.xaml.cs b/SlipItIn/AppShell.xaml.cs index a985cd7..5f8c199 100644 --- a/SlipItIn/AppShell.xaml.cs +++ b/SlipItIn/AppShell.xaml.cs @@ -1,4 +1,6 @@ -namespace SlipItIn; +using SlipItIn.Views; + +namespace SlipItIn; public partial class AppShell : Shell { diff --git a/SlipItIn/Infrastructure/ServiceHelper.cs b/SlipItIn/Infrastructure/ServiceHelper.cs new file mode 100644 index 0000000..942c99d --- /dev/null +++ b/SlipItIn/Infrastructure/ServiceHelper.cs @@ -0,0 +1,16 @@ +using Microsoft.Extensions.DependencyInjection; + +namespace SlipItIn.Infrastructure; + +public static class ServiceHelper +{ + public static IServiceProvider? Services { get; set; } + + public static T GetRequiredService() where T : notnull + { + if (Services is null) + throw new InvalidOperationException("Service provider is not initialized."); + + return Services.GetRequiredService(); + } +} diff --git a/SlipItIn/MainPage.xaml b/SlipItIn/MainPage.xaml deleted file mode 100644 index 9747740..0000000 --- a/SlipItIn/MainPage.xaml +++ /dev/null @@ -1,36 +0,0 @@ - - - - - - - -