From d7821ebf1f679ea3dd1b7fdaf59d71643d07a682 Mon Sep 17 00:00:00 2001 From: neckfire Date: Thu, 16 Jul 2026 09:06:17 +0200 Subject: [PATCH] friends: richer GetFriendList payload (field aliases) + log returned JSON The friend tile rendered empty/offline because the UE client couldn't read our best-guess response shape. Expose each friend's identity/name/steam/presence under every plausible field name (camelCase + PlayFab PascalCase, flat + nested profile) under both friends/Friends keys, and log the JSON so we can pin the exact shape. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../CloudScript/Functions/GetFriendList.cs | 59 ++++++++++++++----- 1 file changed, 44 insertions(+), 15 deletions(-) diff --git a/src/Prospect.Server.Api/Services/CloudScript/Functions/GetFriendList.cs b/src/Prospect.Server.Api/Services/CloudScript/Functions/GetFriendList.cs index e5ee9ba..971f09f 100644 --- a/src/Prospect.Server.Api/Services/CloudScript/Functions/GetFriendList.cs +++ b/src/Prospect.Server.Api/Services/CloudScript/Functions/GetFriendList.cs @@ -32,9 +32,8 @@ public class GetFriendList : ICloudScriptFunction } var userId = context.User.FindAuthUserId(); - // Resolve the imported Steam friends (persisted by ClientsideFriendsImport) to the - // players that actually exist on this server. It's a private server, so only friends - // who have logged in here will show up. + // Imported Steam friends (persisted by ClientsideFriendsImport), resolved to players + // that actually exist on this private server. var steamIds = new List(); var userData = await _userDataService.FindAsync(userId, userId, new List { "ImportedSteamFriends" }); if (userData.TryGetValue("ImportedSteamFriends", out var rec) && !string.IsNullOrWhiteSpace(rec.Value)) @@ -52,8 +51,10 @@ public class GetFriendList : ICloudScriptFunction { if (player.Id == userId) continue; // never list yourself - // Presence persisted by UpdatePlayerPresenceState: online if seen in the last - // 3 minutes. EYUserState best-effort: 0 = offline, 1 = online, 2 = in match. + var steamId = player.Auth?.FirstOrDefault(a => a.Type == PlayFabUserAuthType.Steam)?.Key ?? ""; + 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 presenceData = await _userDataService.FindAsync(player.Id, player.Id, new List { "PresenceState" }); if (presenceData.TryGetValue("PresenceState", out var presenceRec) && !string.IsNullOrWhiteSpace(presenceRec.Value)) @@ -69,22 +70,50 @@ public class GetFriendList : ICloudScriptFunction 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 { - profile = new - { - playerId = player.Id, - displayName = player.DisplayName, - avatarUrl = "", - }, + // identity + friendPlayFabId = player.Id, + FriendPlayFabId = player.Id, + playerId = player.Id, + 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, + 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(), + Tags = Array.Empty(), }); } } - // NOTE: the exact response shape the client expects is not yet confirmed; this is a - // best-effort structure. The log lets us verify resolution while we validate in game. - _logger.LogInformation("GetFriendList for {User}: {Count} friend(s) resolved on server", userId, friends.Count); - return new { friends }; + // Return the list under both the camelCase and PlayFab-cased container keys. + var result = new { friends, Friends = friends, count = friends.Count }; + _logger.LogInformation("GetFriendList for {User}: {Count} friend(s); payload={Json}", userId, friends.Count, JsonSerializer.Serialize(result)); + return result; } } -- 2.54.0