Promotion: fix sauvegarde des missions en zone (raid client-hosted) #4
@@ -1,12 +1,31 @@
|
|||||||
using Microsoft.AspNetCore.SignalR;
|
using Microsoft.AspNetCore.SignalR;
|
||||||
|
|
||||||
namespace Prospect.Server.Api.Hubs;
|
namespace Prospect.Server.Api.Hubs;
|
||||||
|
|
||||||
public class CycleHub : Hub
|
public class CycleHub : Hub
|
||||||
{
|
{
|
||||||
|
private readonly SignalRConnectionRegistry _registry;
|
||||||
|
|
||||||
|
public CycleHub(SignalRConnectionRegistry registry)
|
||||||
|
{
|
||||||
|
_registry = registry;
|
||||||
|
}
|
||||||
|
|
||||||
public override async Task OnConnectedAsync()
|
public override async Task OnConnectedAsync()
|
||||||
{
|
{
|
||||||
Console.WriteLine("Connected {0}", Context.ConnectionId);
|
// The client connects with the SignalR URL returned by GetSignalRConnection, which
|
||||||
|
// carries a `uid` query param — use it to map this connection to the player so
|
||||||
|
// server-initiated messages can target a single player instead of everyone.
|
||||||
|
var uid = Context.GetHttpContext()?.Request.Query["uid"].ToString();
|
||||||
|
if (!string.IsNullOrEmpty(uid))
|
||||||
|
_registry.Add(uid, Context.ConnectionId);
|
||||||
|
Console.WriteLine("Connected {0} (uid={1})", Context.ConnectionId, uid);
|
||||||
await base.OnConnectedAsync();
|
await base.OnConnectedAsync();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
public override async Task OnDisconnectedAsync(Exception? exception)
|
||||||
|
{
|
||||||
|
_registry.Remove(Context.ConnectionId);
|
||||||
|
await base.OnDisconnectedAsync(exception);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,29 @@
|
|||||||
|
using System.Collections.Concurrent;
|
||||||
|
|
||||||
|
namespace Prospect.Server.Api.Hubs;
|
||||||
|
|
||||||
|
// Maps players to their live SignalR connection so server-initiated messages (e.g. the
|
||||||
|
// matchmaking-success "travel to match" signal) can target a single player instead of being
|
||||||
|
// broadcast to everyone. The client's SignalR URL carries a `uid` query param (added by
|
||||||
|
// GetSignalRConnection); CycleHub records it on connect.
|
||||||
|
public class SignalRConnectionRegistry
|
||||||
|
{
|
||||||
|
private readonly ConcurrentDictionary<string, string> _userToConn = new();
|
||||||
|
private readonly ConcurrentDictionary<string, string> _connToUser = new();
|
||||||
|
|
||||||
|
public void Add(string userId, string connectionId)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(userId)) return;
|
||||||
|
_userToConn[userId] = connectionId;
|
||||||
|
_connToUser[connectionId] = userId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Remove(string connectionId)
|
||||||
|
{
|
||||||
|
if (_connToUser.TryRemove(connectionId, out var userId))
|
||||||
|
_userToConn.TryRemove(userId, out _);
|
||||||
|
}
|
||||||
|
|
||||||
|
public string? GetConnection(string userId)
|
||||||
|
=> _userToConn.TryGetValue(userId, out var c) ? c : null;
|
||||||
|
}
|
||||||
@@ -24,13 +24,15 @@ public class EnterMatchmakingMatchFunction : ICloudScriptFunction<FYEnterMatchAz
|
|||||||
private readonly IHubContext<CycleHub> _hubContext;
|
private readonly IHubContext<CycleHub> _hubContext;
|
||||||
private readonly UserDataService _userDataService;
|
private readonly UserDataService _userDataService;
|
||||||
private readonly TitleDataService _titleDataService;
|
private readonly TitleDataService _titleDataService;
|
||||||
|
private readonly SignalRConnectionRegistry _registry;
|
||||||
|
|
||||||
public EnterMatchmakingMatchFunction(IHubContext<CycleHub> hubContext, IHttpContextAccessor httpContextAccessor, UserDataService userDataService, TitleDataService titleDataService)
|
public EnterMatchmakingMatchFunction(IHubContext<CycleHub> hubContext, IHttpContextAccessor httpContextAccessor, UserDataService userDataService, TitleDataService titleDataService, SignalRConnectionRegistry registry)
|
||||||
{
|
{
|
||||||
_httpContextAccessor = httpContextAccessor;
|
_httpContextAccessor = httpContextAccessor;
|
||||||
_hubContext = hubContext;
|
_hubContext = hubContext;
|
||||||
_userDataService = userDataService;
|
_userDataService = userDataService;
|
||||||
_titleDataService = titleDataService;
|
_titleDataService = titleDataService;
|
||||||
|
_registry = registry;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<FYEnterMatchmakingResult> ExecuteAsync(FYEnterMatchAzureFunction request)
|
public async Task<FYEnterMatchmakingResult> ExecuteAsync(FYEnterMatchAzureFunction request)
|
||||||
@@ -94,11 +96,20 @@ public class EnterMatchmakingMatchFunction : ICloudScriptFunction<FYEnterMatchAz
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
await _hubContext.Clients.All.SendAsync("OnSquadMatchmakingSuccess", new OnSquadMatchmakingSuccessMessage {
|
// Send the "matchmaking succeeded / travel to match" signal only to the deploying
|
||||||
|
// player (via their mapped SignalR connection) — broadcasting to Clients.All made
|
||||||
|
// every connected player travel too. Fall back to broadcast only if the connection
|
||||||
|
// isn't mapped yet.
|
||||||
|
var deployMessage = new OnSquadMatchmakingSuccessMessage {
|
||||||
Success = true,
|
Success = true,
|
||||||
SessionID = request.MapName, // TODO: Need to implement TryGetCompleteSquadInfo and pass squad info
|
SessionID = request.MapName, // TODO: pass real squad session info
|
||||||
SquadID = request.SquadId,
|
SquadID = request.SquadId,
|
||||||
});
|
};
|
||||||
|
var connectionId = _registry.GetConnection(userId);
|
||||||
|
if (connectionId != null)
|
||||||
|
await _hubContext.Clients.Client(connectionId).SendAsync("OnSquadMatchmakingSuccess", deployMessage);
|
||||||
|
else
|
||||||
|
await _hubContext.Clients.All.SendAsync("OnSquadMatchmakingSuccess", deployMessage);
|
||||||
|
|
||||||
return new FYEnterMatchmakingResult
|
return new FYEnterMatchmakingResult
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ using System.Net;
|
|||||||
using System.Net.Sockets;
|
using System.Net.Sockets;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
using Prospect.Server.Api.Config;
|
using Prospect.Server.Api.Config;
|
||||||
|
using Prospect.Server.Api.Services.Auth.Extensions;
|
||||||
using Prospect.Server.Api.Services.CloudScript.Models;
|
using Prospect.Server.Api.Services.CloudScript.Models;
|
||||||
|
|
||||||
namespace Prospect.Server.Api.Services.CloudScript.Functions;
|
namespace Prospect.Server.Api.Services.CloudScript.Functions;
|
||||||
@@ -43,7 +44,13 @@ public class GetSignalRConnection : ICloudScriptFunction<FYGetSignalRConnection,
|
|||||||
catch { /* keep the configured URL on any parsing issue */ }
|
catch { /* keep the configured URL on any parsing issue */ }
|
||||||
}
|
}
|
||||||
|
|
||||||
_logger.LogInformation("GetSignalRConnection: remoteIp={Remote} lan={Lan} -> {Url}", remote, lan, url);
|
// Tag the connection with the player id so the hub can map connection -> user and
|
||||||
|
// target server messages (deploy signal) instead of broadcasting to everyone.
|
||||||
|
var userId = _httpContextAccessor.HttpContext?.User.FindAuthUserId();
|
||||||
|
if (!string.IsNullOrEmpty(userId))
|
||||||
|
url += (url.Contains('?') ? "&" : "?") + "uid=" + Uri.EscapeDataString(userId);
|
||||||
|
|
||||||
|
_logger.LogInformation("GetSignalRConnection: remoteIp={Remote} lan={Lan} uid={Uid} -> {Url}", remote, lan, userId, url);
|
||||||
|
|
||||||
return Task.FromResult(new FYGetSignalRConnectionResult
|
return Task.FromResult(new FYGetSignalRConnectionResult
|
||||||
{
|
{
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -36,6 +36,7 @@ public class Startup
|
|||||||
services.AddSingleton<DbUserDataService>();
|
services.AddSingleton<DbUserDataService>();
|
||||||
|
|
||||||
services.AddSingleton<Prospect.Server.Api.Services.Squad.SquadService>();
|
services.AddSingleton<Prospect.Server.Api.Services.Squad.SquadService>();
|
||||||
|
services.AddSingleton<Prospect.Server.Api.Hubs.SignalRConnectionRegistry>();
|
||||||
|
|
||||||
services.AddHostedService<QosService>();
|
services.AddHostedService<QosService>();
|
||||||
services.AddSingleton<QosServer>();
|
services.AddSingleton<QosServer>();
|
||||||
|
|||||||
Reference in New Issue
Block a user