Compare commits
6
Commits
042f806697
..
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
73178ed8c3 | ||
|
|
3243b70555 | ||
|
|
91bdde3797 | ||
|
|
d7821ebf1f | ||
|
|
e5ab911dfd | ||
|
|
81092b077e |
@@ -9,6 +9,14 @@ public class RequestLoggerMiddleware
|
|||||||
private readonly ILogger<RequestLoggerMiddleware> _logger;
|
private readonly ILogger<RequestLoggerMiddleware> _logger;
|
||||||
private readonly RequestDelegate _next;
|
private readonly RequestDelegate _next;
|
||||||
|
|
||||||
|
// Keywords used to surface social/squad/invite traffic while reverse-engineering the
|
||||||
|
// squad-invite flow (see SQUAD-EMULATION.md). Matched against the path AND the body
|
||||||
|
// (the CloudScript function name lives in the body of /Client/ExecuteFunction).
|
||||||
|
private static readonly string[] SocialKeywords =
|
||||||
|
{
|
||||||
|
"group", "party", "lobby", "squad", "invite", "friend", "social", "matchmak",
|
||||||
|
};
|
||||||
|
|
||||||
public RequestLoggerMiddleware(ILogger<RequestLoggerMiddleware> logger, RequestDelegate next)
|
public RequestLoggerMiddleware(ILogger<RequestLoggerMiddleware> logger, RequestDelegate next)
|
||||||
{
|
{
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
@@ -32,6 +40,27 @@ public class RequestLoggerMiddleware
|
|||||||
}
|
}
|
||||||
|
|
||||||
await _next(context);
|
await _next(context);
|
||||||
|
|
||||||
|
// ── Capture pass (squad-invite RE, see SQUAD-EMULATION.md) ──────────────
|
||||||
|
// Runs AFTER the pipeline so we know whether the request was actually routed.
|
||||||
|
if (context.Request.Method == "POST")
|
||||||
|
{
|
||||||
|
var path = context.Request.Path.Value ?? "";
|
||||||
|
var haystack = (path + " " + body).ToLowerInvariant();
|
||||||
|
|
||||||
|
// Any POST that fell through to a 404 = an endpoint the emulator does NOT implement
|
||||||
|
// (e.g. a native PlayFab /Group/CreateGroup or /Lobby/* the client tried to call).
|
||||||
|
if (context.Response.StatusCode == 404)
|
||||||
|
{
|
||||||
|
_logger.LogWarning("[CAPTURE UNROUTED] {Method} {Url} -> 404 | Body {Body}",
|
||||||
|
context.Request.Method, context.Request.GetDisplayUrl(), body);
|
||||||
|
}
|
||||||
|
else if (SocialKeywords.Any(k => haystack.Contains(k)))
|
||||||
|
{
|
||||||
|
_logger.LogInformation("[CAPTURE SOCIAL] {Url} ({Status}) | Body {Body}",
|
||||||
|
context.Request.GetDisplayUrl(), context.Response.StatusCode, body);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async Task<string> RequestAsync(HttpRequest request)
|
private static async Task<string> RequestAsync(HttpRequest request)
|
||||||
|
|||||||
@@ -32,9 +32,8 @@ public class GetFriendList : ICloudScriptFunction<FYBaseSocialRequest, object?>
|
|||||||
}
|
}
|
||||||
var userId = context.User.FindAuthUserId();
|
var userId = context.User.FindAuthUserId();
|
||||||
|
|
||||||
// Resolve the imported Steam friends (persisted by ClientsideFriendsImport) to the
|
// Imported Steam friends (persisted by ClientsideFriendsImport), resolved to players
|
||||||
// players that actually exist on this server. It's a private server, so only friends
|
// that actually exist on this private server.
|
||||||
// who have logged in here will show up.
|
|
||||||
var steamIds = new List<string>();
|
var steamIds = new List<string>();
|
||||||
var userData = await _userDataService.FindAsync(userId, userId, new List<string> { "ImportedSteamFriends" });
|
var userData = await _userDataService.FindAsync(userId, userId, new List<string> { "ImportedSteamFriends" });
|
||||||
if (userData.TryGetValue("ImportedSteamFriends", out var rec) && !string.IsNullOrWhiteSpace(rec.Value))
|
if (userData.TryGetValue("ImportedSteamFriends", out var rec) && !string.IsNullOrWhiteSpace(rec.Value))
|
||||||
@@ -52,8 +51,10 @@ public class GetFriendList : ICloudScriptFunction<FYBaseSocialRequest, object?>
|
|||||||
{
|
{
|
||||||
if (player.Id == userId) continue; // never list yourself
|
if (player.Id == userId) continue; // never list yourself
|
||||||
|
|
||||||
// Presence persisted by UpdatePlayerPresenceState: online if seen in the last
|
var steamId = player.Auth?.FirstOrDefault(a => a.Type == PlayFabUserAuthType.Steam)?.Key ?? "";
|
||||||
// 3 minutes. EYUserState best-effort: 0 = offline, 1 = online, 2 = in match.
|
var displayName = string.IsNullOrWhiteSpace(player.DisplayName) ? "Prospector" : player.DisplayName;
|
||||||
|
|
||||||
|
// 0 = offline, 1 = online, 2 = in match. Online if seen in the last 3 minutes.
|
||||||
var onlineState = 0;
|
var onlineState = 0;
|
||||||
var presenceData = await _userDataService.FindAsync(player.Id, player.Id, new List<string> { "PresenceState" });
|
var presenceData = await _userDataService.FindAsync(player.Id, player.Id, new List<string> { "PresenceState" });
|
||||||
if (presenceData.TryGetValue("PresenceState", out var presenceRec) && !string.IsNullOrWhiteSpace(presenceRec.Value))
|
if (presenceData.TryGetValue("PresenceState", out var presenceRec) && !string.IsNullOrWhiteSpace(presenceRec.Value))
|
||||||
@@ -69,22 +70,50 @@ public class GetFriendList : ICloudScriptFunction<FYBaseSocialRequest, object?>
|
|||||||
catch { /* ignore malformed presence */ }
|
catch { /* ignore malformed presence */ }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The exact model the UE client binds is not documented, so expose the identity
|
||||||
|
// under every plausible field name (camelCase + PlayFab PascalCase) and both a
|
||||||
|
// flat and nested profile — whichever the client reads, the tile gets populated.
|
||||||
friends.Add(new
|
friends.Add(new
|
||||||
{
|
{
|
||||||
profile = new
|
// identity
|
||||||
{
|
friendPlayFabId = player.Id,
|
||||||
playerId = player.Id,
|
FriendPlayFabId = player.Id,
|
||||||
displayName = player.DisplayName,
|
playerId = player.Id,
|
||||||
avatarUrl = "",
|
PlayerId = player.Id,
|
||||||
},
|
playFabId = player.Id,
|
||||||
|
PlayFabId = player.Id,
|
||||||
|
// name
|
||||||
|
displayName,
|
||||||
|
DisplayName = displayName,
|
||||||
|
titleDisplayName = displayName,
|
||||||
|
TitleDisplayName = displayName,
|
||||||
|
name = displayName,
|
||||||
|
username = displayName,
|
||||||
|
Username = displayName,
|
||||||
|
// steam
|
||||||
|
steamId,
|
||||||
|
SteamId = steamId,
|
||||||
|
steamInfo = new { steamId, SteamId = steamId },
|
||||||
|
SteamInfo = new { SteamId = steamId, steamId },
|
||||||
|
// presence
|
||||||
onlineState,
|
onlineState,
|
||||||
|
OnlineState = onlineState,
|
||||||
|
isOnline = onlineState > 0,
|
||||||
|
inMatch = onlineState == 2,
|
||||||
|
presence = new { onlineState, state = onlineState },
|
||||||
|
avatarUrl = "",
|
||||||
|
// nested profiles
|
||||||
|
profile = new { playerId = player.Id, displayName, avatarUrl = "" },
|
||||||
|
Profile = new { PlayerId = player.Id, DisplayName = displayName, PlayerProfileModel = new { DisplayName = displayName } },
|
||||||
|
tags = Array.Empty<string>(),
|
||||||
|
Tags = Array.Empty<string>(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE: the exact response shape the client expects is not yet confirmed; this is a
|
// Return the list under both the camelCase and PlayFab-cased container keys.
|
||||||
// best-effort structure. The log lets us verify resolution while we validate in game.
|
var result = new { friends, Friends = friends, count = friends.Count };
|
||||||
_logger.LogInformation("GetFriendList for {User}: {Count} friend(s) resolved on server", userId, friends.Count);
|
_logger.LogInformation("GetFriendList for {User}: {Count} friend(s); payload={Json}", userId, friends.Count, JsonSerializer.Serialize(result));
|
||||||
return new { friends };
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user