Friend Joiner Tab - Broken but essential for my game

Description: I’m developing a game that empowers users to create and share their own unique experiences. The “Friend Join Tab” feature is a crucial element of this vision, allowing players to effortlessly connect with friends and explore each other’s creations. However, the presence system, which tracks friends’ online status and session availability, is malfunctioning.

Why is this Feature Necessary? The Friend Join Tab is integral to the social and creative aspects of the game. By enabling users to join friends’ sessions, the feature fosters collaboration, creativity, and community-building within the game. Without it, the game’s core philosophy—bringing people together to share and enjoy their creations—falls short.

Steps to Reproduce:

  1. Launch the Friend Join Tab within the game.
  2. Attempt to check friends’ presence or join their sessions.
  3. Observe that the presence system is not functioning correctly.

What is the Issue? Presence data does not update in real-time or accurately reflect friends’ availability, making it impossible to join sessions.

Solutions Tried So Far:

  1. Debugged presence logic for tracking online status and session availability.
  2. Verified server communication and API integration for presence updates.
  3. Reviewed documentation but the problem persists.

Additional Details:

  • This issue may stem from delayed presence updates or misconfigured event handling.

My code

local Players = game:GetService("Players")
local HttpService = game:GetService("HttpService")
local TeleportService = game:GetService("TeleportService")
local DataStoreService = game:GetService("DataStoreService")
local gameModeDataStore = DataStoreService:GetDataStore("GameModeDataStore")

local screenGui = script.Parent
local template = screenGui.explorer.main.friends.scroll.template
local scroll = screenGui.explorer.main.friends.scroll
local refreshBtn = screenGui.explorer.refresh

local function getGameModeByUserId(userId: number)
	local success, placeId, jobId = pcall(function()
		return TeleportService:GetPlayerPlaceInstanceAsync(userId)
	end)

	if success and placeId and jobId then
		local gameMode
		success, gameMode = pcall(function()
			return gameModeDataStore:GetAsync(jobId)
		end)

		if success and gameMode then
			return gameMode, jobId
		else
			return nil, jobId
		end
	end

	return nil, nil
end

local function teleportToServer(player: Player, targetUserId: number)
	local gameMode, jobId = getGameModeByUserId(targetUserId)

	if gameMode and jobId then
		print(player.Name, "is teleporting to server hosting:", gameMode)
		local success, err = pcall(function()
			TeleportService:TeleportToPlaceInstance(game.PlaceId, jobId, player)
		end)

		if not success then
			warn("Teleport failed:", err)
		end
	end
end

local function getFriends(player: Player)
	local friendPages = Players:GetFriendsAsync(player.UserId)
	local friendIds = {}

	local function iteratePages(pages)
		return coroutine.wrap(function()
			repeat
				for _, item in ipairs(pages:GetCurrentPage()) do
					coroutine.yield(item)
				end
				pages:AdvanceToNextPageAsync()
			until pages.IsFinished
		end)
	end

	for friend in iteratePages(friendPages) do
		table.insert(friendIds, friend.UserId)
	end

	return friendIds
end

local function getPlayersPlaying(player: Player)
	local function splitChunks(array, size)
		local chunks = {}
		for i = 1, #array, size do
			local chunk = {}
			for j = i, math.min(i + size - 1, #array) do
				table.insert(chunk, array[j])
			end
			table.insert(chunks, chunk)
		end
		return chunks
	end

	local function checkPresence(userIds)
		local apiEndpoint = "https://presence.roproxy.com/v1/presence/users"
		local results = {}
		local batches = splitChunks(userIds, 100)

		for _, batch in ipairs(batches) do
			local body = HttpService:JSONEncode({ userIds = batch })
			local success, response = pcall(function()
				return HttpService:PostAsync(apiEndpoint, body, Enum.HttpContentType.ApplicationJson)
			end)

			if success then
				local decoded = HttpService:JSONDecode(response)
				table.insert(results, decoded)
			else
				warn("Presence check failed:", response)
			end
		end

		return results
	end

	local friendIds = getFriends(player)
	local presenceResults = checkPresence(friendIds)
	local currentUniverseId = 130901771589509 -- YOUR universe ID here
	local onlineFriends = {}

	for _, batch in ipairs(presenceResults) do
		for _, presence in ipairs(batch.userPresences or {}) do
			if presence.universeId == currentUniverseId then
				table.insert(onlineFriends, presence.userId)
			end
		end
	end

	return onlineFriends
end

local function setupProfile(clone: Frame, userId: number, localPlayer: Player)
	local successName, username = pcall(function()
		return Players:GetNameFromUserIdAsync(userId)
	end)
	if not successName then username = "Unknown" end

	local successImg, thumb = pcall(function()
		return Players:GetUserThumbnailAsync(userId, Enum.ThumbnailType.AvatarThumbnail, Enum.ThumbnailSize.Size420x420)
	end)
	if not successImg then thumb = "rbxasset://textures/ui/GuiImagePlaceholder.png" end

	local gameMode, _ = getGameModeByUserId(userId)

	clone.mugshot.Image = thumb
	clone.person.Text = username
	clone.game.Text = gameMode or "Unknown"

	clone.MouseButton1Click:Connect(function()
		teleportToServer(localPlayer, userId)
	end)

	clone.Visible = true
	clone.Parent = scroll
end

local function setupProfiles(player: Player)
	for _, child in ipairs(scroll:GetChildren()) do
		if child:IsA("GuiObject") and child.Name ~= "template" then
			child:Destroy()
		end
	end

	for _, userId in ipairs(getPlayersPlaying(player)) do
		local clone = template:Clone()
		setupProfile(clone, userId, player)
	end
end

-- ✅ Use LocalScript-compatible player reference
local CurrentPlayer = screenGui.Parent.Parent
if not CurrentPlayer then
	warn("Couldn't resolve current player!")
else
	setupProfiles(CurrentPlayer)

	refreshBtn.MouseButton1Click:Connect(function()
		setupProfiles(CurrentPlayer)
	end)
end

Added way to many console prints for debugging

2 Likes

Wasn’t the presence api disabled a little while ago?

1 Like

I have gotten the presence api to work. The main issue right now is checking the game the friends are playing.

1 Like