Lobby: include HostUserId in GameStateDto and let client derive host status; update tests #7
17
.github/workflows/build.yml
vendored
17
.github/workflows/build.yml
vendored
@@ -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
|
||||||
|
|||||||
@@ -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" />
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|
||||||
|
|||||||
@@ -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]
|
||||||
|
|||||||
@@ -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
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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; }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
@@ -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);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user