Compare commits

..

7 Commits

Author SHA1 Message Date
tim
46ea7a15d7 Merge pull request 'Lobby: include HostUserId in GameStateDto and let client derive host status; update tests' (#7) from feature/host-userid-lobby-start into master
Some checks failed
Build / build (push) Has been cancelled
Reviewed-on: #7
2026-10-04 16:12:24 +00:00
Tim Krampitz
d175ef8a6c Project: include Windows TFM by default unless SkipWindowsTargetFramework=true; fix AppHost ProjectReference path
All checks were successful
Build / build (pull_request) Successful in 2m2s
2026-10-04 18:09:16 +02:00
Tim Krampitz
fd0d7f9971 CI: conditionally restore Windows TFM; build AppHost only on Windows runners to avoid missing Windows runtime packs on non-Windows CI/tooling
All checks were successful
Build / build (pull_request) Successful in 2m5s
2026-10-04 18:07:49 +02:00
Tim Krampitz
21cb57fd74 CI: avoid restoring Windows MAUI runtime packs in CI/tooling by skipping Windows TFM; make SkipWindowsTargetFramework property controllable
Some checks failed
Build / build (pull_request) Failing after 34s
2026-10-04 17:58:18 +02:00
Tim Krampitz
c4915542fe CI: build MAUI target for Android in CI and restore without Windows runtime to avoid missing Mono runtime pack when running locally with act
All checks were successful
Build / build (pull_request) Successful in 2m29s
2026-10-04 17:56:55 +02:00
Tim Krampitz
8b5dd6f45d CI: restore and build MAUI project with win-x64 runtime identifier to ensure runtime packs are restored
Some checks failed
Build / build (pull_request) Failing after 37s
2026-10-04 17:53:54 +02:00
Tim Krampitz
2d0687a083 Lobby: include HostUserId in GameStateDto and let client derive host status; update tests
Some checks failed
Build / build (pull_request) Failing after 1m0s
2026-10-03 22:29:33 +02:00
8 changed files with 45 additions and 8 deletions

View File

@@ -22,19 +22,30 @@ jobs:
- name: Install MAUI workload - name: Install MAUI workload
run: dotnet workload install maui run: dotnet workload install maui
- name: Restore - name: Restore (Windows)
if: runner.os == 'Windows'
run: dotnet restore SlipItIn.slnx run: dotnet restore SlipItIn.slnx
- name: Build Server-Stack - name: Restore (non-Windows)
if: runner.os != 'Windows'
# Skip restoring the Windows target frameworks to avoid requiring Windows runtime packs in non-Windows CI/tooling environments
run: dotnet restore SlipItIn.slnx -p:SkipWindowsTargetFramework=true
- name: Build Server-Stack (core projects)
run: | run: |
dotnet build SlipItIn.Shared/SlipItIn.Shared.csproj --no-restore --configuration Release dotnet build SlipItIn.Shared/SlipItIn.Shared.csproj --no-restore --configuration Release
dotnet build SlipItIn.ServiceDefaults/SlipItIn.ServiceDefaults.csproj --no-restore --configuration Release dotnet build SlipItIn.ServiceDefaults/SlipItIn.ServiceDefaults.csproj --no-restore --configuration Release
dotnet build SlipItIn.Server/SlipItIn.Server.csproj --no-restore --configuration Release dotnet build SlipItIn.Server/SlipItIn.Server.csproj --no-restore --configuration Release
dotnet build SlipItIn.Server.Tests/SlipItIn.Server.Tests.csproj --no-restore --configuration Release dotnet build SlipItIn.Server.Tests/SlipItIn.Server.Tests.csproj --no-restore --configuration Release
- name: Build AppHost
if: runner.os == 'Windows'
run: |
dotnet build SlipItIn.AppHost/SlipItIn.AppHost.csproj --no-restore --configuration Release dotnet build SlipItIn.AppHost/SlipItIn.AppHost.csproj --no-restore --configuration Release
- name: Build MAUI Client - name: Build MAUI Client
run: dotnet build SlipItIn/SlipItIn.csproj --no-restore --configuration Debug # Build the MAUI project for Android only in CI to avoid requiring Windows runtime packs
run: dotnet build SlipItIn/SlipItIn.csproj --no-restore --configuration Debug -f net10.0-android
- name: Run Server Tests - name: Run Server Tests
run: dotnet test SlipItIn.Server.Tests/SlipItIn.Server.Tests.csproj --no-restore --configuration Release run: dotnet test SlipItIn.Server.Tests/SlipItIn.Server.Tests.csproj --no-restore --configuration Release

View File

@@ -15,7 +15,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\SlipItIN\SlipItIn.csproj"> <ProjectReference Include="..\SlipItIn\SlipItIn.csproj">
<SetTargetFramework>TargetFramework=net10.0-windows10.0.19041.0</SetTargetFramework> <SetTargetFramework>TargetFramework=net10.0-windows10.0.19041.0</SetTargetFramework>
</ProjectReference> </ProjectReference>
<ProjectReference Include="..\SlipItIn.Server\SlipItIn.Server.csproj" /> <ProjectReference Include="..\SlipItIn.Server\SlipItIn.Server.csproj" />

View File

@@ -108,6 +108,7 @@ public class GameHubTests
{ {
GameId = game.Id, GameId = game.Id,
LobbyCode = game.LobbyCode, LobbyCode = game.LobbyCode,
HostUserId = game.HostId,
Players = [new PlayerInfoDto { Username = user.Username }] Players = [new PlayerInfoDto { Username = user.Username }]
}); });
@@ -151,7 +152,7 @@ public class GameHubTests
var game = new Game { Id = 3, LobbyCode = "JOIN42", HostId = 99 }; var game = new Game { Id = 3, LobbyCode = "JOIN42", HostId = 99 };
_gameService.Setup(s => s.JoinGameAsync("JOIN42", user.Id, ConnectionId)).ReturnsAsync(game); _gameService.Setup(s => s.JoinGameAsync("JOIN42", user.Id, ConnectionId)).ReturnsAsync(game);
_gameService.Setup(s => s.GetGameStateAsync(game.Id)).ReturnsAsync(new GameStateDto { GameId = 3 }); _gameService.Setup(s => s.GetGameStateAsync(game.Id)).ReturnsAsync(new GameStateDto { GameId = 3, HostUserId = 99 });
await _hub.JoinLobby("JOIN42"); await _hub.JoinLobby("JOIN42");
@@ -205,7 +206,7 @@ public class GameHubTests
_gameService.Setup(s => s.SetPlayerReadyAsync(game.Id, player.Id)) _gameService.Setup(s => s.SetPlayerReadyAsync(game.Id, player.Id))
.ReturnsAsync(new Game { Id = game.Id, LobbyCode = game.LobbyCode }); .ReturnsAsync(new Game { Id = game.Id, LobbyCode = game.LobbyCode });
_gameService.Setup(s => s.GetGameStateAsync(game.Id)).ReturnsAsync(new GameStateDto { GameId = game.Id }); _gameService.Setup(s => s.GetGameStateAsync(game.Id)).ReturnsAsync(new GameStateDto { GameId = game.Id, HostUserId = game.HostId });
await _hub.PlayerReady(game.Id, player.Id); await _hub.PlayerReady(game.Id, player.Id);
@@ -540,7 +541,9 @@ public class GameHubTests
SeedGameWithPlayers((user, ConnectionId)); SeedGameWithPlayers((user, ConnectionId));
var gameId = db.Games.Single().Id; var gameId = db.Games.Single().Id;
_gameService.Setup(s => s.GetGameStateAsync(gameId)).ReturnsAsync(new GameStateDto { GameId = gameId }); var hostId = db.Games.Single().HostId;
_gameService.Setup(s => s.GetGameStateAsync(gameId)).ReturnsAsync(new GameStateDto { GameId = gameId, HostUserId = hostId });
await _hub.RequestGameState(gameId); await _hub.RequestGameState(gameId);

View File

@@ -40,6 +40,18 @@ public class GameServiceTests
await Assert.ThrowsAsync<InvalidOperationException>(() => _sut.CreateGameAsync(999)); await Assert.ThrowsAsync<InvalidOperationException>(() => _sut.CreateGameAsync(999));
} }
[Fact]
public async Task GetGameStateAsync_IncludesHostUserId()
{
using var db = _factory.CreateDbContext();
var host = TestDbHelper.SeedUser(db);
var (game, _) = TestDbHelper.SeedGame(db, host);
var dto = await _sut.GetGameStateAsync(game.Id);
Assert.Equal(game.HostId, dto.HostUserId);
}
// ---------- JoinGameAsync ---------- // ---------- JoinGameAsync ----------
[Fact] [Fact]

View File

@@ -293,6 +293,7 @@ public class GameService : IGameService
LobbyCode = game.LobbyCode, LobbyCode = game.LobbyCode,
Status = game.Status.ToString(), Status = game.Status.ToString(),
Players = playerInfos, Players = playerInfos,
HostUserId = game.HostId,
CurrentRound = currentRound?.RoundNumber ?? 0, CurrentRound = currentRound?.RoundNumber ?? 0,
RoundTimeRemaining = roundTimeRemaining RoundTimeRemaining = roundTimeRemaining
}; };

View File

@@ -6,6 +6,8 @@ public class GameStateDto
public string LobbyCode { get; set; } = string.Empty; public string LobbyCode { get; set; } = string.Empty;
public string Status { get; set; } = string.Empty; public string Status { get; set; } = string.Empty;
public List<PlayerInfoDto> Players { get; set; } = []; public List<PlayerInfoDto> Players { get; set; } = [];
// Host user id to let clients determine whether the current user is the host
public int HostUserId { get; set; }
public int CurrentRound { get; set; } public int CurrentRound { get; set; }
public int RoundTimeRemaining { get; set; } public int RoundTimeRemaining { get; set; }
} }

View File

@@ -3,7 +3,9 @@
<PropertyGroup> <PropertyGroup>
<TargetFrameworks>net10.0-android</TargetFrameworks> <TargetFrameworks>net10.0-android</TargetFrameworks>
<TargetFrameworks Condition="!$([MSBuild]::IsOSPlatform('linux'))">$(TargetFrameworks);</TargetFrameworks> <TargetFrameworks Condition="!$([MSBuild]::IsOSPlatform('linux'))">$(TargetFrameworks);</TargetFrameworks>
<TargetFrameworks Condition="$([MSBuild]::IsOSPlatform('windows'))">$(TargetFrameworks);net10.0-windows10.0.19041.0</TargetFrameworks> <!-- Windows target framework can be skipped by setting SkipWindowsTargetFramework=true during restore/build (useful for CI or tooling without Windows runtime packs).
Include the Windows TFM by default so restores which evaluate OS differently still generate assets for the Windows TFM unless explicitly skipped. -->
<TargetFrameworks Condition="'$(SkipWindowsTargetFramework)' != 'true'">$(TargetFrameworks);net10.0-windows10.0.19041.0</TargetFrameworks>
<LangVersion>preview</LangVersion> <LangVersion>preview</LangVersion>
<!-- Note for MacCatalyst: <!-- Note for MacCatalyst:

View File

@@ -144,8 +144,14 @@ public partial class LobbyViewModel : BaseViewModel,
Players.Add(player); Players.Add(player);
var currentUsername = _authSession.CurrentUser?.Username; var currentUsername = _authSession.CurrentUser?.Username;
var currentUserId = _authSession.CurrentUser?.UserId ?? 0;
IsCurrentPlayerReady = message.Value.Players.Any(p => IsCurrentPlayerReady = message.Value.Players.Any(p =>
p.IsReady && string.Equals(p.Username, currentUsername, StringComparison.OrdinalIgnoreCase)); p.IsReady && string.Equals(p.Username, currentUsername, StringComparison.OrdinalIgnoreCase));
// Determine whether the current user is the host based on server-provided HostUserId
IsHost = currentUserId > 0 && message.Value.HostUserId > 0 && currentUserId == message.Value.HostUserId;
CanStartGame = message.Value.Players.Count >= 2 && message.Value.Players.All(p => p.IsReady); CanStartGame = message.Value.Players.Count >= 2 && message.Value.Players.All(p => p.IsReady);
}); });
} }