Compare commits
16
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
042f806697 | ||
|
|
a4c8cf5955 | ||
|
|
d1f3fd1125 | ||
|
|
b05782d4e9 | ||
|
|
d7c89327a7 | ||
|
|
03048b793d | ||
|
|
1d9cf795dd | ||
|
|
7dbab1aab8 | ||
|
|
078d2f80be | ||
|
|
c986a98471 | ||
|
|
57a45d91df | ||
|
|
c6b2330e1d | ||
|
|
9737316dbc | ||
|
|
a4e006f84f | ||
|
|
3cdf8bc7e2 | ||
|
|
67f3095896 |
@@ -9,14 +9,6 @@ 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;
|
||||||
@@ -40,27 +32,6 @@ 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,8 +32,9 @@ public class GetFriendList : ICloudScriptFunction<FYBaseSocialRequest, object?>
|
|||||||
}
|
}
|
||||||
var userId = context.User.FindAuthUserId();
|
var userId = context.User.FindAuthUserId();
|
||||||
|
|
||||||
// Imported Steam friends (persisted by ClientsideFriendsImport), resolved to players
|
// Resolve the imported Steam friends (persisted by ClientsideFriendsImport) to the
|
||||||
// that actually exist on this private server.
|
// players that actually exist on this server. It's a private server, so only friends
|
||||||
|
// 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))
|
||||||
@@ -51,10 +52,8 @@ public class GetFriendList : ICloudScriptFunction<FYBaseSocialRequest, object?>
|
|||||||
{
|
{
|
||||||
if (player.Id == userId) continue; // never list yourself
|
if (player.Id == userId) continue; // never list yourself
|
||||||
|
|
||||||
var steamId = player.Auth?.FirstOrDefault(a => a.Type == PlayFabUserAuthType.Steam)?.Key ?? "";
|
// Presence persisted by UpdatePlayerPresenceState: online if seen in the last
|
||||||
var displayName = string.IsNullOrWhiteSpace(player.DisplayName) ? "Prospector" : player.DisplayName;
|
// 3 minutes. EYUserState best-effort: 0 = offline, 1 = online, 2 = in match.
|
||||||
|
|
||||||
// 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))
|
||||||
@@ -70,50 +69,22 @@ 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
|
||||||
{
|
{
|
||||||
// identity
|
profile = new
|
||||||
friendPlayFabId = player.Id,
|
{
|
||||||
FriendPlayFabId = player.Id,
|
|
||||||
playerId = player.Id,
|
playerId = player.Id,
|
||||||
PlayerId = player.Id,
|
displayName = player.DisplayName,
|
||||||
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,
|
|
||||||
isOnline = onlineState > 0,
|
|
||||||
inMatch = onlineState == 2,
|
|
||||||
presence = new { onlineState, state = onlineState },
|
|
||||||
avatarUrl = "",
|
avatarUrl = "",
|
||||||
// nested profiles
|
},
|
||||||
profile = new { playerId = player.Id, displayName, avatarUrl = "" },
|
onlineState,
|
||||||
Profile = new { PlayerId = player.Id, DisplayName = displayName, PlayerProfileModel = new { DisplayName = displayName } },
|
|
||||||
tags = Array.Empty<string>(),
|
|
||||||
Tags = Array.Empty<string>(),
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return the list under both the camelCase and PlayFab-cased container keys.
|
// NOTE: the exact response shape the client expects is not yet confirmed; this is a
|
||||||
var result = new { friends, Friends = friends, count = friends.Count };
|
// best-effort structure. The log lets us verify resolution while we validate in game.
|
||||||
_logger.LogInformation("GetFriendList for {User}: {Count} friend(s); payload={Json}", userId, friends.Count, JsonSerializer.Serialize(result));
|
_logger.LogInformation("GetFriendList for {User}: {Count} friend(s) resolved on server", userId, friends.Count);
|
||||||
return result;
|
return new { friends };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user