Skip to content

Commit fbd0045

Browse files
feat: Integrate live status data for game servers across various controllers and views for enhanced visibility
1 parent 0ad0e3f commit fbd0045

20 files changed

Lines changed: 132 additions & 72 deletions

src/XtremeIdiots.Portal.Web/Controllers/DashboardController.cs

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using Microsoft.AspNetCore.Mvc;
44

55
using XtremeIdiots.Portal.Repository.Abstractions.Constants.V1;
6+
using XtremeIdiots.Portal.Repository.Abstractions.Models.V1.LiveStatus;
67
using XtremeIdiots.Portal.Repository.Api.Client.V1;
78
using XtremeIdiots.Portal.Web.Auth.Constants;
89
using XtremeIdiots.Portal.Web.Extensions;
@@ -75,35 +76,42 @@ public async Task<IActionResult> Index(CancellationToken cancellationToken = def
7576
var gameServersTask = repositoryApiClient.GameServers.V1.GetGameServers(
7677
null, null, GameServerFilter.AgentEnabled, 0, 100,
7778
GameServerOrder.ServerListPosition, cancellationToken);
79+
var liveStatusTask = repositoryApiClient.LiveStatus.V1.GetAllGameServerLiveStatuses(cancellationToken);
7880

79-
await Task.WhenAll(telemetryTask, gameServersTask).ConfigureAwait(false);
81+
await Task.WhenAll(telemetryTask, gameServersTask, liveStatusTask).ConfigureAwait(false);
8082

8183
var telemetry = await telemetryTask.ConfigureAwait(false);
8284
var gameServersResponse = await gameServersTask.ConfigureAwait(false);
85+
var liveStatusResponse = await liveStatusTask.ConfigureAwait(false);
8386

8487
var serverLookup = gameServersResponse.IsSuccess && gameServersResponse.Result?.Data?.Items is not null
8588
? gameServersResponse.Result.Data.Items
8689
.GroupBy(gs => gs.GameServerId)
8790
.ToDictionary(g => g.Key, g => g.First())
8891
: [];
8992

93+
var liveStatusLookup = liveStatusResponse.IsSuccess && liveStatusResponse.Result?.Data?.Items is not null
94+
? liveStatusResponse.Result.Data.Items.ToDictionary(ls => ls.ServerId)
95+
: [];
96+
9097
// Enrich telemetry with server names from the repository API
9198
var telemetryByServer = telemetry
9299
.GroupBy(t => t.ServerId)
93100
.ToDictionary(g => g.Key, g => g.First());
94101
viewModel.AgentStatuses = serverLookup.Values.Select(gs =>
95102
{
96103
telemetryByServer.TryGetValue(gs.GameServerId, out var summary);
104+
liveStatusLookup.TryGetValue(gs.GameServerId, out var liveStatus);
97105

98106
return new AgentServerSummary
99107
{
100108
ServerId = gs.GameServerId,
101-
ServerTitle = string.IsNullOrWhiteSpace(gs.LiveTitle) ? gs.Title : gs.LiveTitle,
109+
ServerTitle = string.IsNullOrWhiteSpace(liveStatus?.Title) ? gs.Title : liveStatus.Title,
102110
GameType = gs.GameType.ToString(),
103111
LastEventReceived = summary?.LastEventReceived,
104112
EventsLastHour = summary?.EventsLastHour ?? 0,
105113
PlayerCount = summary?.PlayerCount ?? 0,
106-
CurrentMap = summary?.CurrentMap ?? gs.LiveMap,
114+
CurrentMap = summary?.CurrentMap ?? liveStatus?.Map,
107115
IsAgentActive = summary?.IsAgentActive ?? false,
108116
ActivityStatus = summary?.ActivityStatus ?? AgentActivityStatus.Offline
109117
};

src/XtremeIdiots.Portal.Web/Controllers/MapManagerController.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,9 @@ public async Task<IActionResult> Manage(Guid id, CancellationToken cancellationT
109109
Rotations = rotations
110110
};
111111

112+
var liveStatusResponse = await repositoryApiClient.LiveStatus.V1.GetGameServerLiveStatus(gameServerData.GameServerId, cancellationToken).ConfigureAwait(false);
113+
viewModel.LiveStatus = liveStatusResponse.IsSuccess ? liveStatusResponse.Result?.Data : null;
114+
112115
return View(viewModel);
113116
}, nameof(Manage)).ConfigureAwait(false);
114117
}

src/XtremeIdiots.Portal.Web/Controllers/ServerAdminController.cs

Lines changed: 34 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
using XtremeIdiots.Portal.Repository.Abstractions.Constants.V1;
1414
using XtremeIdiots.Portal.Repository.Abstractions.Models.V1.AdminActions;
1515
using XtremeIdiots.Portal.Repository.Abstractions.Models.V1.GameServers;
16+
using XtremeIdiots.Portal.Repository.Abstractions.Models.V1.LiveStatus;
1617
using XtremeIdiots.Portal.Repository.Abstractions.Models.V1.Maps;
1718
using XtremeIdiots.Portal.Repository.Abstractions.Models.V1.Players;
1819
using XtremeIdiots.Portal.Repository.Api.Client.V1;
@@ -63,11 +64,21 @@ public async Task<IActionResult> Index(CancellationToken cancellationToken = def
6364
return RedirectToAction("Display", "Errors", new { id = 500 });
6465
}
6566

66-
var results = gameServersApiResponse.Result.Data.Items.Select(gs => new ServerAdminGameServerViewModel
67+
var liveStatusResponse = await repositoryApiClient.LiveStatus.V1.GetAllGameServerLiveStatuses(cancellationToken).ConfigureAwait(false);
68+
var liveStatusLookup = liveStatusResponse.IsSuccess && liveStatusResponse.Result?.Data?.Items is not null
69+
? liveStatusResponse.Result.Data.Items.ToDictionary(ls => ls.ServerId)
70+
: [];
71+
72+
var results = gameServersApiResponse.Result.Data.Items.Select(gs =>
6773
{
68-
GameServer = gs,
69-
GameServerQueryStatus = new ServerQueryStatusResponseDto(),
70-
GameServerRconStatus = new ServerRconStatusResponseDto()
74+
liveStatusLookup.TryGetValue(gs.GameServerId, out var liveStatus);
75+
return new ServerAdminGameServerViewModel
76+
{
77+
GameServer = gs,
78+
LiveStatus = liveStatus,
79+
GameServerQueryStatus = new ServerQueryStatusResponseDto(),
80+
GameServerRconStatus = new ServerRconStatusResponseDto()
81+
};
7182
}).ToList();
7283

7384
Logger.LogInformation("Successfully loaded {Count} game servers for user {UserId} server admin dashboard",
@@ -132,6 +143,10 @@ public async Task<IActionResult> ServerDetail(Guid id, CancellationToken cancell
132143

133144
var gs = gameServerApiResponse.Result.Data;
134145

146+
// Fetch live status for this server
147+
var liveStatusResponse = await repositoryApiClient.LiveStatus.V1.GetGameServerLiveStatus(gs.GameServerId, cancellationToken).ConfigureAwait(false);
148+
var liveStatus = liveStatusResponse.IsSuccess ? liveStatusResponse.Result?.Data : null;
149+
135150
// Check per-tab permissions in parallel
136151
var rconAuth = authorizationService.AuthorizeAsync(User, gs.GameType, AuthPolicies.ViewLiveRcon);
137152
var chatAuth = authorizationService.AuthorizeAsync(User, gs.GameType, AuthPolicies.ViewServerChatLog);
@@ -144,6 +159,7 @@ public async Task<IActionResult> ServerDetail(Guid id, CancellationToken cancell
144159
var viewModel = new ServerDetailViewModel
145160
{
146161
GameServer = gs,
162+
LiveStatus = liveStatus,
147163
CanViewRcon = (await rconAuth.ConfigureAwait(false)).Succeeded,
148164
CanViewChatLog = (await chatAuth.ConfigureAwait(false)).Succeeded,
149165
CanViewMapRotation = (await mapRotAuth.ConfigureAwait(false)).Succeeded,
@@ -1239,12 +1255,22 @@ public async Task<IActionResult> GetGameServers(CancellationToken cancellationTo
12391255
return Json(Array.Empty<object>());
12401256
}
12411257

1258+
var liveStatusResponse = await repositoryApiClient.LiveStatus.V1.GetAllGameServerLiveStatuses(cancellationToken).ConfigureAwait(false);
1259+
var liveStatusLookup = liveStatusResponse.IsSuccess && liveStatusResponse.Result?.Data?.Items is not null
1260+
? liveStatusResponse.Result.Data.Items.ToDictionary(ls => ls.ServerId)
1261+
: [];
1262+
12421263
var results = gameServersApiResponse.Result.Data.Items
1243-
.Select(gs => new
1264+
.Select(gs =>
12441265
{
1245-
id = gs.GameServerId,
1246-
title = string.IsNullOrWhiteSpace(gs.LiveTitle) ? gs.Title : gs.LiveTitle,
1247-
gameType = gs.GameType.ToString()
1266+
liveStatusLookup.TryGetValue(gs.GameServerId, out var liveStatus);
1267+
var title = string.IsNullOrWhiteSpace(liveStatus?.Title) ? gs.Title : liveStatus.Title;
1268+
return new
1269+
{
1270+
id = gs.GameServerId,
1271+
title,
1272+
gameType = gs.GameType.ToString()
1273+
};
12481274
})
12491275
.OrderBy(r => r.gameType)
12501276
.ThenBy(r => r.title)

src/XtremeIdiots.Portal.Web/Controllers/ServersController.cs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using Microsoft.AspNetCore.Mvc;
44

55
using XtremeIdiots.Portal.Repository.Abstractions.Constants.V1;
6+
using XtremeIdiots.Portal.Repository.Abstractions.Models.V1.LiveStatus;
67
using XtremeIdiots.Portal.Repository.Api.Client.V1;
78
using XtremeIdiots.Portal.Web.Auth.Constants;
89
using XtremeIdiots.Portal.Web.Extensions;
@@ -50,8 +51,17 @@ public async Task<IActionResult> Index(CancellationToken cancellationToken = def
5051
return RedirectToAction(nameof(ErrorsController.Display), nameof(ErrorsController).Replace("Controller", ""), new { id = 500 });
5152
}
5253

54+
var liveStatusResponse = await repositoryApiClient.LiveStatus.V1.GetAllGameServerLiveStatuses(cancellationToken).ConfigureAwait(false);
55+
var liveStatusLookup = liveStatusResponse.IsSuccess && liveStatusResponse.Result?.Data?.Items is not null
56+
? liveStatusResponse.Result.Data.Items.ToDictionary(ls => ls.ServerId)
57+
: [];
58+
5359
var result = gameServersApiResponse.Result.Data.Items
54-
.Select(gs => new ServersGameServerViewModel(gs))
60+
.Select(gs =>
61+
{
62+
liveStatusLookup.TryGetValue(gs.GameServerId, out var liveStatus);
63+
return new ServersGameServerViewModel(gs, liveStatus);
64+
})
5565
.ToList();
5666

5767
Logger.LogInformation("User {UserId} successfully retrieved {ServerCount} servers",

src/XtremeIdiots.Portal.Web/Controllers/StatusController.cs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
using XtremeIdiots.Portal.Repository.Abstractions.Constants.V1;
66
using XtremeIdiots.Portal.Repository.Abstractions.Models.V1.GameServers;
7+
using XtremeIdiots.Portal.Repository.Abstractions.Models.V1.LiveStatus;
78
using XtremeIdiots.Portal.Repository.Api.Client.V1;
89
using XtremeIdiots.Portal.Web.Auth.Constants;
910
using XtremeIdiots.Portal.Web.Extensions;
@@ -119,6 +120,11 @@ public async Task<IActionResult> AgentStatus(CancellationToken cancellationToken
119120
? [.. gameServersApiResponse.Result.Data.Items]
120121
: new List<GameServerDto>();
121122

123+
var liveStatusResponse = await repositoryApiClient.LiveStatus.V1.GetAllGameServerLiveStatuses(cancellationToken).ConfigureAwait(false);
124+
var liveStatusLookup = liveStatusResponse.IsSuccess && liveStatusResponse.Result?.Data?.Items is not null
125+
? liveStatusResponse.Result.Data.Items.ToDictionary(ls => ls.ServerId)
126+
: [];
127+
122128
IReadOnlyList<AgentServerSummary> telemetry = Array.Empty<AgentServerSummary>();
123129
try
124130
{
@@ -134,16 +140,17 @@ public async Task<IActionResult> AgentStatus(CancellationToken cancellationToken
134140
var models = servers.Select(gs =>
135141
{
136142
telemetryByServer.TryGetValue(gs.GameServerId, out var summary);
143+
liveStatusLookup.TryGetValue(gs.GameServerId, out var liveStatus);
137144

138145
return new AgentServerSummary
139146
{
140147
ServerId = gs.GameServerId,
141-
ServerTitle = string.IsNullOrWhiteSpace(gs.LiveTitle) ? gs.Title : gs.LiveTitle,
148+
ServerTitle = string.IsNullOrWhiteSpace(liveStatus?.Title) ? gs.Title : liveStatus.Title,
142149
GameType = gs.GameType.ToString(),
143150
LastEventReceived = summary?.LastEventReceived,
144151
EventsLastHour = summary?.EventsLastHour ?? 0,
145152
PlayerCount = summary?.PlayerCount ?? 0,
146-
CurrentMap = summary?.CurrentMap ?? gs.LiveMap,
153+
CurrentMap = summary?.CurrentMap ?? liveStatus?.Map,
147154
IsAgentActive = summary?.IsAgentActive ?? false,
148155
ActivityStatus = summary?.ActivityStatus ?? AgentActivityStatus.Offline
149156
};

src/XtremeIdiots.Portal.Web/ViewModels/ManageMapsViewModel.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using XtremeIdiots.Portal.Integrations.Servers.Abstractions.Models.V1.Rcon;
33
using XtremeIdiots.Portal.Repository.Abstractions.Constants.V1;
44
using XtremeIdiots.Portal.Repository.Abstractions.Models.V1.GameServers;
5+
using XtremeIdiots.Portal.Repository.Abstractions.Models.V1.LiveStatus;
56
using XtremeIdiots.Portal.Repository.Abstractions.Models.V1.MapRotations;
67
using XtremeIdiots.Portal.Repository.Abstractions.Models.V1.Maps;
78

@@ -10,6 +11,7 @@ namespace XtremeIdiots.Portal.Web.ViewModels;
1011
public class ManageMapsViewModel(GameServerDto gameServer)
1112
{
1213
public GameServerDto GameServer { get; private set; } = gameServer;
14+
public GameServerLiveStatusDto? LiveStatus { get; set; }
1315
public List<MapDto> Maps { get; set; } = [];
1416
public List<ServerMapDto> ServerMaps { get; set; } = [];
1517
public List<RconMapDto> RconMaps { get; set; } = [];

src/XtremeIdiots.Portal.Web/ViewModels/ServerAdminGameServerViewModel.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using XtremeIdiots.Portal.Integrations.Servers.Abstractions.Models.V1;
22
using XtremeIdiots.Portal.Repository.Abstractions.Models.V1.GameServers;
3+
using XtremeIdiots.Portal.Repository.Abstractions.Models.V1.LiveStatus;
34

45
namespace XtremeIdiots.Portal.Web.ViewModels;
56

@@ -13,6 +14,11 @@ public class ServerAdminGameServerViewModel
1314
/// </summary>
1415
public required GameServerDto GameServer { get; set; }
1516

17+
/// <summary>
18+
/// Gets or sets the live status data for the server
19+
/// </summary>
20+
public GameServerLiveStatusDto? LiveStatus { get; set; }
21+
1622
/// <summary>
1723
/// Gets or sets the current query status of the game server
1824
/// </summary>

src/XtremeIdiots.Portal.Web/ViewModels/ServerDetailViewModel.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using XtremeIdiots.Portal.Repository.Abstractions.Models.V1.BanFileMonitors;
22
using XtremeIdiots.Portal.Repository.Abstractions.Models.V1.GameServers;
3+
using XtremeIdiots.Portal.Repository.Abstractions.Models.V1.LiveStatus;
34
using XtremeIdiots.Portal.Web.Models;
45
using XtremeIdiots.Portal.Web.Services;
56

@@ -11,6 +12,7 @@ namespace XtremeIdiots.Portal.Web.ViewModels;
1112
public class ServerDetailViewModel
1213
{
1314
public required GameServerDto GameServer { get; set; }
15+
public GameServerLiveStatusDto? LiveStatus { get; set; }
1416

1517
// Tab permission flags — determines which tabs are rendered
1618
public bool CanViewRcon { get; set; }
Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,22 @@
11
using XtremeIdiots.Portal.Repository.Abstractions.Models.V1.GameServers;
2+
using XtremeIdiots.Portal.Repository.Abstractions.Models.V1.LiveStatus;
23

34
namespace XtremeIdiots.Portal.Web.ViewModels;
45

56
/// <summary>
67
/// View model for displaying game server information in the public servers list
78
/// </summary>
89
/// <param name="gameServer">The game server data transfer object</param>
9-
public class ServersGameServerViewModel(GameServerDto gameServer)
10+
/// <param name="liveStatus">The live status data for this server</param>
11+
public class ServersGameServerViewModel(GameServerDto gameServer, GameServerLiveStatusDto? liveStatus = null)
1012
{
1113
/// <summary>
1214
/// Gets the game server data
1315
/// </summary>
1416
public GameServerDto GameServer { get; private set; } = gameServer;
17+
18+
/// <summary>
19+
/// Gets the live status data for the server
20+
/// </summary>
21+
public GameServerLiveStatusDto? LiveStatus { get; private set; } = liveStatus;
1522
}

src/XtremeIdiots.Portal.Web/Views/BanFileMonitors/Delete.cshtml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@
5959
<dt class="detail-label">Game Server</dt>
6060
<dd class="detail-value" data-testid="ban-file-monitor-delete-gameserver">
6161
<server-name title="@Model.GameServer.Title"
62-
live-title="@Model.GameServer.LiveTitle"></server-name>
62+
live-title="@Model.GameServer.Title"></server-name>
6363
</dd>
6464
</div>
6565
<div class="detail-field col-sm-6">

0 commit comments

Comments
 (0)