feat(squad): in-memory squad/lobby state + wire squad functions
Build & Deploy / build (push) Successful in 31s
Build & Deploy / build (push) Successful in 31s
Adds SquadService (singleton, process-local) tracking squads by the squadId the client shares across party members, plus a user->squad index so TryGetCompleteSquadInfo (no squadId in request) can resolve the caller's squad. Members are lazily registered on any squad call. - TryGetCompleteSquadInfo: returns the caller's real squad. - SquadMemberReadyForMatch: joins the squad, records readiness/map, returns the squad + whether everyone is ready. - SquadMemberSelectedMap: records the member's selected map. - DbUserService.FindByIdAsync: resolve display name for squad members. Groundwork for the station lobby; assumes the client drives squads via these calls with a shared squadId (to confirm with a 2-client session). Real-time SignalR push (unknown event names) deferred; the squad is carried in responses + TryGetCompleteSquadInfo for now. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+23
-18
@@ -1,6 +1,9 @@
|
||||
using System.Text.Json.Serialization;
|
||||
using Prospect.Server.Api.Services.CloudScript;
|
||||
using Prospect.Server.Api.Services.Auth.Extensions;
|
||||
using Prospect.Server.Api.Services.Database;
|
||||
using Prospect.Server.Api.Services.Database.Models;
|
||||
using Prospect.Server.Api.Services.Squad;
|
||||
using Microsoft.AspNetCore.SignalR;
|
||||
using Prospect.Server.Api.Hubs;
|
||||
|
||||
@@ -28,12 +31,16 @@ public class SquadMemberReadyForMatchFunction : ICloudScriptFunction<SquadMember
|
||||
private readonly ILogger<SquadMemberReadyForMatchFunction> _logger;
|
||||
private readonly IHttpContextAccessor _httpContextAccessor;
|
||||
private readonly IHubContext<CycleHub> _hubContext;
|
||||
private readonly SquadService _squadService;
|
||||
private readonly DbUserService _userService;
|
||||
|
||||
public SquadMemberReadyForMatchFunction(ILogger<SquadMemberReadyForMatchFunction> logger, IHttpContextAccessor httpContextAccessor, IHubContext<CycleHub> hubContext)
|
||||
public SquadMemberReadyForMatchFunction(ILogger<SquadMemberReadyForMatchFunction> logger, IHttpContextAccessor httpContextAccessor, IHubContext<CycleHub> hubContext, SquadService squadService, DbUserService userService)
|
||||
{
|
||||
_httpContextAccessor = httpContextAccessor;
|
||||
_logger = logger;
|
||||
_hubContext = hubContext;
|
||||
_squadService = squadService;
|
||||
_userService = userService;
|
||||
}
|
||||
|
||||
public async Task<SquadMemberReadyForMatchResponse> ExecuteAsync(SquadMemberReadyForMatchRequest request)
|
||||
@@ -44,27 +51,25 @@ public class SquadMemberReadyForMatchFunction : ICloudScriptFunction<SquadMember
|
||||
throw new CloudScriptException("CloudScript was not called within a http request");
|
||||
}
|
||||
var userId = context.User.FindAuthUserId();
|
||||
var squadId = string.IsNullOrEmpty(request.SquadID) ? userId : request.SquadID;
|
||||
|
||||
// Register the caller into the squad (party is formed client-side; each member's client
|
||||
// calls this with the shared squadId) and record their readiness / selected map.
|
||||
var displayName = (await _userService.FindByIdAsync(userId))?.DisplayName ?? "";
|
||||
_squadService.JoinOrCreate(squadId, userId, displayName);
|
||||
var ready = request.matchmakingSettings?.isReadyForMatch ?? false;
|
||||
_squadService.SetReady(squadId, userId, ready, request.matchmakingSettings?.selectedMapName);
|
||||
|
||||
var squad = _squadService.Get(squadId)!;
|
||||
var isSquadReady = _squadService.IsSquadReady(squadId);
|
||||
_logger.LogInformation("SquadMemberReadyForMatch: {User} ready={Ready} in squad {Squad} ({Count} member(s), squadReady={SquadReady})",
|
||||
userId, ready, squadId, squad.Members.Count, isSquadReady);
|
||||
|
||||
return new SquadMemberReadyForMatchResponse
|
||||
{
|
||||
Result = 0, // EYSquadActionResult::OK
|
||||
Squad = new FYPlayFabSquad {
|
||||
SquadID = "100", // TODO
|
||||
// Members = [
|
||||
// new FYPlayFabSquadMember {
|
||||
// Profile = new FYPlayFabPlayerProfile {
|
||||
// PlayerId = userId,
|
||||
// },
|
||||
// onlineState = 0, // EYUserState::IN_STATION
|
||||
// matchmakingSettings = new FYUserMatchmakingSettings {
|
||||
// isReadyForMatch = true,
|
||||
// isSecretLeader = true,
|
||||
// selectedMapName = "Map01",
|
||||
// }
|
||||
// }
|
||||
// ],
|
||||
},
|
||||
IsSquadReadyForMatch = true,
|
||||
Squad = _squadService.ToClientSquad(squad),
|
||||
IsSquadReadyForMatch = isSquadReady,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
using System.Text.Json.Serialization;
|
||||
using System.Text.Json.Serialization;
|
||||
using Prospect.Server.Api.Services.Auth.Extensions;
|
||||
using Prospect.Server.Api.Services.CloudScript;
|
||||
using Prospect.Server.Api.Services.Squad;
|
||||
|
||||
public class SquadMemberSelectedMapRequest
|
||||
{
|
||||
[JsonPropertyName("squadId")]
|
||||
@@ -17,15 +20,26 @@ public class SquadMemberSelectedMapResponse
|
||||
public class SquadMemberSelectedMapFunction : ICloudScriptFunction<SquadMemberSelectedMapRequest, SquadMemberSelectedMapResponse>
|
||||
{
|
||||
private readonly ILogger<SquadMemberSelectedMapFunction> _logger;
|
||||
private readonly IHttpContextAccessor _httpContextAccessor;
|
||||
private readonly SquadService _squadService;
|
||||
|
||||
public SquadMemberSelectedMapFunction(ILogger<SquadMemberSelectedMapFunction> logger)
|
||||
public SquadMemberSelectedMapFunction(ILogger<SquadMemberSelectedMapFunction> logger, IHttpContextAccessor httpContextAccessor, SquadService squadService)
|
||||
{
|
||||
_logger = logger;
|
||||
_httpContextAccessor = httpContextAccessor;
|
||||
_squadService = squadService;
|
||||
}
|
||||
|
||||
public async Task<SquadMemberSelectedMapResponse> ExecuteAsync(SquadMemberSelectedMapRequest request)
|
||||
public Task<SquadMemberSelectedMapResponse> ExecuteAsync(SquadMemberSelectedMapRequest request)
|
||||
{
|
||||
return new SquadMemberSelectedMapResponse
|
||||
{};
|
||||
var context = _httpContextAccessor.HttpContext;
|
||||
if (context != null && !string.IsNullOrEmpty(request.SquadID))
|
||||
{
|
||||
var userId = context.User.FindAuthUserId();
|
||||
_squadService.JoinOrCreate(request.SquadID, userId, "");
|
||||
_squadService.SetMap(request.SquadID, userId, request.SelectedMapName ?? "");
|
||||
_logger.LogInformation("SquadMemberSelectedMap: {User} -> {Map} (squad {Squad})", userId, request.SelectedMapName, request.SquadID);
|
||||
}
|
||||
return Task.FromResult(new SquadMemberSelectedMapResponse { });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
using System.Text.Json.Serialization;
|
||||
using Prospect.Server.Api.Services.Auth.Extensions;
|
||||
using Prospect.Server.Api.Services.CloudScript;
|
||||
using Prospect.Server.Api.Services.Database;
|
||||
using Prospect.Server.Api.Services.Squad;
|
||||
|
||||
public class TryGetCompleteSquadInfoRequest
|
||||
{
|
||||
@@ -51,26 +54,30 @@ public class TryGetCompleteSquadInfoFunction : ICloudScriptFunction<TryGetComple
|
||||
{
|
||||
private readonly ILogger<TryGetCompleteSquadInfoFunction> _logger;
|
||||
private readonly IHttpContextAccessor _httpContextAccessor;
|
||||
private readonly SquadService _squadService;
|
||||
|
||||
public TryGetCompleteSquadInfoFunction(ILogger<TryGetCompleteSquadInfoFunction> logger, IHttpContextAccessor httpContextAccessor)
|
||||
public TryGetCompleteSquadInfoFunction(ILogger<TryGetCompleteSquadInfoFunction> logger, IHttpContextAccessor httpContextAccessor, SquadService squadService)
|
||||
{
|
||||
_httpContextAccessor = httpContextAccessor;
|
||||
_logger = logger;
|
||||
_squadService = squadService;
|
||||
}
|
||||
|
||||
public async Task<FYPlayFabSquad> ExecuteAsync(TryGetCompleteSquadInfoRequest request)
|
||||
public Task<FYPlayFabSquad> ExecuteAsync(TryGetCompleteSquadInfoRequest request)
|
||||
{
|
||||
var context = _httpContextAccessor.HttpContext;
|
||||
if (context == null)
|
||||
{
|
||||
throw new CloudScriptException("CloudScript was not called within a http request");
|
||||
}
|
||||
// var userId = context.User.FindAuthUserId();
|
||||
|
||||
return new FYPlayFabSquad
|
||||
var userId = context.User.FindAuthUserId();
|
||||
var squad = _squadService.GetByUser(userId);
|
||||
if (squad == null)
|
||||
{
|
||||
// SquadID = "100",
|
||||
// Members = [],
|
||||
};
|
||||
// Not in a squad yet — return an empty squad (same as the previous stub).
|
||||
return Task.FromResult(new FYPlayFabSquad());
|
||||
}
|
||||
_logger.LogInformation("TryGetCompleteSquadInfo: user {User} in squad {Squad} ({Count} member(s))", userId, squad.SquadId, squad.Members.Count);
|
||||
return Task.FromResult(_squadService.ToClientSquad(squad));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user