Files
watrbx-game-engine/content/scripts/Modules/FriendsData.lua
T
2025-09-18 17:55:52 -04:00

287 lines
8.6 KiB
Lua

--[[
// FriendsData.lua
// Caches the current friends pagination to used by anyone in the app
// polls every POLL_DELAY and gets the latest pagination
// TODO:
Need polling to update friends. How are we going to handle all the cases
like the person you're selecting going offline, etc..
]]
local CoreGui = game:GetService("CoreGui")
local GuiRoot = CoreGui:FindFirstChild("RobloxGui")
local Modules = GuiRoot:FindFirstChild("Modules")
local Players = game:GetService('Players')
local HttpService = game:GetService('HttpService')
local PlatformService = nil
pcall(function() PlatformService = game:GetService('PlatformService') end)
local Http = require(Modules:FindFirstChild('Http'))
local Utility = require(Modules:FindFirstChild('Utility'))
local EventHub = require(Modules:FindFirstChild('EventHub'))
local UserData = require(Modules:FindFirstChild('UserData'))
local SortData = require(Modules:FindFirstChild('SortData'))
local Strings = require(Modules:FindFirstChild('LocalizedStrings'))
-- NOTE: This is just required for fixing Usernames in auto-generatd games
local GameData = require(Modules:FindFirstChild('GameData'))
local ConvertMyPlaceNameInXboxAppFlag = Utility.IsFastFlagEnabled("ConvertMyPlaceNameInXboxApp")
local FriendsData = {}
local pollDelay = Utility.GetFastVariable("XboxFriendsPolling")
if pollDelay then
pollDelay = tonumber(pollDelay)
end
local POLL_DELAY = pollDelay or 30
local STATUS = {
UNKNOWN = "Unknown";
ONLINE = "Online";
OFFLINE = "Offline";
AWAY = "Away";
}
local isOnlineFriendsPolling = false
local myCurrentFriendsData = nil
local updateEventCns = {}
local function filterXboxFriends(friendsData)
local titleId = PlatformService and tostring(PlatformService:GetTitleId()) or ''
local onlineRobloxFriendsData = {}
local onlineRobloxFriendsUserIds = {}
local onlineFriendsData = {}
-- filter into two list, those online in roblox and those online
-- for those online, also get their roblox userId
for i = 1, #friendsData do
local data = friendsData[i]
if data["status"] and data["status"] == STATUS.ONLINE then
if data["rich"] then
-- get rich presence table, last entry is most recent activity from user
local richTbl = data["rich"]
richTbl = richTbl[#richTbl]
if richTbl["titleId"] == titleId then
table.insert(onlineRobloxFriendsData, data)
table.insert(onlineRobloxFriendsUserIds, data["robloxuid"])
else
table.insert(onlineFriendsData, data)
end
end
end
end
-- now get roblox friends presence in roblox
local robloxPresence = {}
if #onlineRobloxFriendsUserIds > 0 then
local jsonTable = {}
jsonTable["userIds"] = onlineRobloxFriendsUserIds
local jsonPostBody = HttpService:JSONEncode(jsonTable)
robloxPresence = Http.GetUsersOnlinePresenceAsync(jsonPostBody)
if robloxPresence and robloxPresence["UserPresences"] then
robloxPresence = robloxPresence["UserPresences"]
end
end
-- now append roblox presence data to each users data
for i = 1, #onlineRobloxFriendsData do
if robloxPresence[i] then
local data = onlineRobloxFriendsData[i]
-- make sure we have the right person
for j = 1, #robloxPresence do
local rbxPresenceData = robloxPresence[j]
local rbxUserId = rbxPresenceData["VisitorId"]
if rbxUserId == data["robloxuid"] then
if rbxPresenceData["IsOnline"] == true then
local placeId = rbxPresenceData["PlaceId"]
local lastLocation = rbxPresenceData["LastLocation"]
-- If the lastLocation for a user is some user place with a GeneratedUsername in it
-- then replace it with the actual creator name!
if ConvertMyPlaceNameInXboxAppFlag and placeId and lastLocation and GameData:ExtractGeneratedUsername(lastLocation) then
local gameCreator = GameData:GetGameCreatorAsync(placeId)
if gameCreator then
lastLocation = GameData:GetFilteredGameName(lastLocation, gameCreator)
end
end
-- If the user is not in a featured game, then hide their presence
if placeId and not SortData:IsFeaturedGameAsync(placeId) then
data["IsPrivateSession"] = true
lastLocation = Strings:LocalizedString("PrivateSessionPhrase")
end
data["PlaceId"] = placeId
data["LastLocation"] = lastLocation
end
-- remove from list and gtfo
table.remove(robloxPresence, j)
break
end
end
end
end
-- now sort those in roblox
table.sort(onlineRobloxFriendsData, function(a, b)
if a["PlaceId"] and b["PlaceId"] then
return a["display"] < b["display"]
end
if a["PlaceId"] then
return true
end
if b["PlaceId"] then
return false
end
return a["display"] < b["display"]
end)
-- now sort all other friends
table.sort(onlineFriendsData, function(a, b)
return a["display"] < b["display"]
end)
-- now concat tables
for i = 1, #onlineFriendsData do
onlineRobloxFriendsData[#onlineRobloxFriendsData + 1] = onlineFriendsData[i]
end
return onlineRobloxFriendsData
end
--[[
// Returns table with an array with the key friends
// Keys
// xuid - number, xbox user id
// gamertage - string, users gamertag
// display - string, users display name (depends on user settings, could be same as gamertag)
// robloxuid - number, users roblox userId
// status - string (Online, Away, Offline, Unknown)
// rich - array of rich presence, might currently be empty?
// timestamp - number, UTC timestamp of record
// device - string, XboxOne, Windows8, etc.
// title - string, of game
// playing - boolean, are they playing the game
// presence - string, rich presence string from that title
]]
local function fetchXboxFriendsAsync()
local success, result = pcall(function()
if PlatformService then
return PlatformService:BeginFetchFriends(Enum.UserInputType.Gamepad1)
end
end)
if success then
return HttpService:JSONDecode(result)
else
print("fetchXboxFriends failed because", result)
end
end
local function getOnlineFriends()
if UserSettings().GameSettings:InStudioMode() then
-- Roblox Friends - leaving this in for testing purposes in studio
local result = Http.GetOnlineFriendsAsync()
if not result then
-- TODO: Error code
return nil
end
--
local myOnlineFriends = {}
for i = 1, #result do
local data = result[i]
local friend = {
Name = Players:GetNameFromUserIdAsync(data["VisitorId"]);
UserId = data["VisitorId"];
LastLocation = data["LastLocation"];
PlaceId = data["PlaceId"];
LocationType = data["LocationType"];
GameId = data["GameId"];
}
table.insert(myOnlineFriends, friend)
end
local function sortFunc(a, b)
if a.LocationType == b.LocationType then
return a.Name:lower() < b.Name:lower()
end
return a.LocationType > b.LocationType
end
table.sort(myOnlineFriends, sortFunc)
return myOnlineFriends
elseif game:GetService('UserInputService'):GetPlatform() == Enum.Platform.XBoxOne then
-- Xbox Friends
local myXboxFriends = fetchXboxFriendsAsync()
local myOnlineFriends = {}
if myXboxFriends then
myXboxFriends = myXboxFriends["friends"]
myOnlineFriends = filterXboxFriends(myXboxFriends)
end
return myOnlineFriends
end
end
--[[ Public API ]]--
FriendsData.OnFriendsDataUpdated = Utility.Signal()
local isFetchingFriends = false
function FriendsData.GetOnlineFriendsAsync()
-- can only make one call into PlatformService:BeginFetchFriends() at a time
while isFetchingFriends do
wait()
end
-- we have current data, this will be updated when polling
if myCurrentFriendsData then
return myCurrentFriendsData
end
isFetchingFriends = true
myCurrentFriendsData = getOnlineFriends()
-- spawn polling on first request
if not isOnlineFriendsPolling then
FriendsData.BeginPolling()
end
isFetchingFriends = false
return myCurrentFriendsData
end
function FriendsData.BeginPolling()
if not isOnlineFriendsPolling then
isOnlineFriendsPolling = true
local requesterId = UserData:GetRbxUserId()
spawn(function()
wait(POLL_DELAY)
while requesterId == UserData:GetRbxUserId() do
myCurrentFriendsData = getOnlineFriends()
FriendsData.OnFriendsDataUpdated:fire(myCurrentFriendsData)
wait(POLL_DELAY)
end
end)
end
end
-- we make connections through this function so we can clean them all up upon
-- clearing the friends data
function FriendsData.ConnectUpdateEvent(cbFunc)
local cn = FriendsData.OnFriendsDataUpdated:connect(cbFunc)
table.insert(updateEventCns, cn)
end
function FriendsData.Reset()
isOnlineFriendsPolling = false
myCurrentFriendsData = nil
for index,cn in pairs(updateEventCns) do
cn = Utility.DisconnectEvent(cn)
updateEventCns[index] = nil
end
print('FriendsData: Cleared last users FriendsData')
end
return FriendsData