Build & Deploy / build (push) Successful in 32s
EnterMatchmakingMatch broadcast OnSquadMatchmakingSuccess to Clients.All, so one player deploying pulled every connected player into the match. Map each player to their SignalR connection (GetSignalRConnection tags the URL with uid; CycleHub records it via a SignalRConnectionRegistry) and send the deploy signal only to that connection (broadcast fallback if unmapped). Also enable m_automaticImportPlatformFriends (Steam friends). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
73 lines
3.0 KiB
C#
73 lines
3.0 KiB
C#
using System.Net;
|
|
using System.Net.Sockets;
|
|
using Microsoft.Extensions.Options;
|
|
using Prospect.Server.Api.Config;
|
|
using Prospect.Server.Api.Services.Auth.Extensions;
|
|
using Prospect.Server.Api.Services.CloudScript.Models;
|
|
|
|
namespace Prospect.Server.Api.Services.CloudScript.Functions;
|
|
|
|
[CloudScriptFunction("GetSignalRConnection")]
|
|
public class GetSignalRConnection : ICloudScriptFunction<FYGetSignalRConnection, FYGetSignalRConnectionResult>
|
|
{
|
|
// LAN address of the homelab host. Clients on the local network get the SignalR URL on
|
|
// this IP (which their cert already validates via the IP/DNS SAN), while external clients
|
|
// keep the public domain from configuration. This matters because the real-time WebSocket
|
|
// (libwebsockets, incl. under Proton/Wine) can't reach the public domain from inside the
|
|
// LAN (hairpin NAT), which surfaced as "error code 5" right after the intro.
|
|
private const string LanHost = "192.168.1.136";
|
|
|
|
private readonly PlayFabSettings _settings;
|
|
private readonly IHttpContextAccessor _httpContextAccessor;
|
|
private readonly ILogger<GetSignalRConnection> _logger;
|
|
|
|
public GetSignalRConnection(IOptions<PlayFabSettings> settings, IHttpContextAccessor httpContextAccessor, ILogger<GetSignalRConnection> logger)
|
|
{
|
|
_settings = settings.Value;
|
|
_httpContextAccessor = httpContextAccessor;
|
|
_logger = logger;
|
|
}
|
|
|
|
public Task<FYGetSignalRConnectionResult> ExecuteAsync(FYGetSignalRConnection request)
|
|
{
|
|
// The game client connects to SignalR only over HTTPS.
|
|
var url = _settings.SignalRURL;
|
|
|
|
var remote = _httpContextAccessor.HttpContext?.Connection.RemoteIpAddress;
|
|
var lan = remote != null && IsLan(remote);
|
|
if (lan)
|
|
{
|
|
try
|
|
{
|
|
url = new UriBuilder(url) { Host = LanHost }.Uri.AbsoluteUri;
|
|
}
|
|
catch { /* keep the configured URL on any parsing issue */ }
|
|
}
|
|
|
|
// 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
|
|
{
|
|
Url = url,
|
|
AccessToken = _settings.SignalRAccessToken
|
|
});
|
|
}
|
|
|
|
private static bool IsLan(IPAddress ip)
|
|
{
|
|
if (IPAddress.IsLoopback(ip)) return true;
|
|
if (ip.IsIPv4MappedToIPv6) ip = ip.MapToIPv4();
|
|
if (ip.AddressFamily != AddressFamily.InterNetwork) return false;
|
|
var b = ip.GetAddressBytes();
|
|
return b[0] == 10
|
|
|| (b[0] == 172 && b[1] >= 16 && b[1] <= 31)
|
|
|| (b[0] == 192 && b[1] == 168);
|
|
}
|
|
}
|