How should i fix the memory leak and performance of my client script?

Basically, in my script, i want the script to add a frame if a player join the game and remove a frame if the player leave the game. I want the frame to actualise if the player party stats changes to check if the player can be invited or not.

image

How do i know there is a memory leak going:

  • The client fire multiple time the remote when pressing the invite button
  • Multiple connections are made at every 1 seconds

The script:

local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")

-- Indentify remotes
local RemoteEvents = ReplicatedStorage:WaitForChild("RemoteEvents",3)
local PartyRemotes = RemoteEvents:WaitForChild("Party",3)

local CallPartyInvitation = PartyRemotes.CallPartyInvitation
local StartParty = PartyRemotes.StartParty
local SendPartyInvitation = PartyRemotes.SendPartyInvitation

local scrollingframe = script.Parent.ScrollingHolder.ScrollingFrame

function ColorButton(template,player)

	if not template then
		print("no template")
		return
	end

	if player and player:FindFirstChild("PlayerStats") and player.PlayerStats:FindFirstChild("PartyStats") then

		if player.PlayerStats.PartyStats.Party.Value == Players.LocalPlayer.UserId then
			template.Invite.Text.Text = "Kick"
			template.Invite.BackgroundColor3 = Color3.new(1, 0, 0)

			template.Invite.TextButton.MouseButton1Click:Connect(function()
				CallPartyInvitation:FireServer(player,"Kick")
			end)

			return
		end

		if player.UserId == Players.LocalPlayer.UserId then
			template.Invite.Text.Text = "You"
			template.Invite.BackgroundColor3 = Color3.new(0.666667, 0.666667, 0.666667)

			return
		end

		if player.PlayerStats.PartyStats.PartyInviteId.Value == Players.LocalPlayer.UserId then
			template.Invite.Text.Text = "Invited"
			template.Invite.BackgroundColor3 = Color3.new(0.666667, 0.666667, 0.666667)

			return
		end

		if player.PlayerStats.PartyStats.PartyInviteId.Value == 0 and player.PlayerStats.PartyStats.Party.Value == 0 and player.UserId ~= Players.LocalPlayer.UserId then
			template.Invite.Text.Text = "Invite"
			template.Invite.BackgroundColor3 = Color3.new(0, 0.666667, 0)

			template.Invite.TextButton.MouseButton1Click:Connect(function()
				CallPartyInvitation:FireServer(player,"Invite")
			end)

			return
		end
	end
end

script.Parent.Start.TextButton.MouseButton1Click:Connect(function()
	StartParty:FireServer()
end)

local PlayersInYourParty = {}

while wait(1) do
	PlayersInYourParty = {}
	
	for _,frame in scrollingframe:GetChildren() do
		if frame:IsA("Frame") and frame.Name ~= "Template" and not Players:FindFirstChild(frame.Name) then
			frame:Destroy()
		end
	end

	for i, player in Players:GetPlayers() do
		if not scrollingframe:FindFirstChild(player.Name) then
			print("Added frame")
			
			local template = scrollingframe.Template:Clone()

			template.Parent = scrollingframe
			template.Visible = true
			template.Name = player.Name
			template.TextLabel.Text = player.Name

			local success, errorm = pcall(function()
				template.FrameForImage.PlayerImage.Image = Players:GetUserThumbnailAsync(player.UserId,Enum.ThumbnailType.HeadShot,Enum.ThumbnailSize.Size60x60)
			end)

			if not success then
				print(errorm)
			end
			
			ColorButton(template,player)
		end

		-- Color the button depending if user is free
		for _,frame in scrollingframe:GetChildren() do
			if frame.Name == player.Name then
				template = frame
				
				ColorButton(template,player)
			end
		end
		
		if player.PlayerStats.PartyStats.Party.Value == Players.LocalPlayer.UserId then
			table.insert(PlayersInYourParty,player)
		end
	end
	
	script.Parent.ActivePlayers.Text = #PlayersInYourParty.."/50"
	if #PlayersInYourParty > 0 then
		script.Parent.Start.BackgroundColor3 = Color3.new(0.333333, 1, 0)
	else
		script.Parent.Start.BackgroundColor3 = Color3.new(0.298039, 0.298039, 0.298039)
	end
end

Does anyone have solution for that script or a similar system that could work and not ruin the player memory?

4 Likes

Did you ask an Ai already? If there’s a really obvious source of memory leak they detect those pretty easily (something like forgetting to destroy an object or disconnect events).

I personally can’t help you though because I’m not good at scripting yet.

basically my only solution is to remake the script?

2 Likes

Well I feel like the best thing you could do for now is just ask an AI if there are any mistakes and then wait for further responses on this post.

Because maybe you just need a really simple change and that’s it. Who knows? I don’t xd

Yeah there is a leak in ColorButton() where you’re connecting MouseButton1Click everytime this is called.
Since this function is being called in a loop it creates multiple connections every second.

i will task.spawn a function then so it might give me the ability to give connection variable to disconnect

I think you could use a ChildAdded event for the scrollingframe to then call the
ColorButton function which looks more like a setup function for the button to me.

From there it’d be easier to actually handle the button logic, using loops might keep causing headaches like this in the future :derp:

1 Like

is this good handling connections in HandleFrameButtons function?
image

Yea, looks pretty good!

Don’t forget to remove the player from the table after they leave though.
memory leaks are so 2024 :coefficients:

By default a event connection serves as a task.spawn, i recommend using :once inside your events connections as they disconnect when they are done.

They should not disconnect especially when the player party value change, its the most important part of the system. The frame should allow to invite and kick somewone from the party.

Well is the server or the client handling the connection of clicking the button? As clients are preferred and 100% more efficient on handling user input stuff for gui

i would go for once for MouseButton1Clicked and connection for Changed stats

I fixed the script and eleminated 80% of the memory leak capacity

local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")

-- Indentify remotes
local RemoteEvents = ReplicatedStorage:WaitForChild("RemoteEvents",3)
local PartyRemotes = RemoteEvents:WaitForChild("Party",3)

local CallPartyInvitation = PartyRemotes.CallPartyInvitation
local StartParty = PartyRemotes.StartParty
local SendPartyInvitation = PartyRemotes.SendPartyInvitation

local scrollingframe = script.Parent.ScrollingHolder.ScrollingFrame

-- Set Player
local LocalPlayer = Players.LocalPlayer

function ColorButton(template,player)

	if not template then
		print("no template")
		return
	end
	
	if not player then
		print("No player found")
		return
	end

	if player and player:FindFirstChild("PlayerStats") and player.PlayerStats:FindFirstChild("PartyStats") then

		if player.PlayerStats.PartyStats.Party.Value == Players.LocalPlayer.UserId then
			template.Invite.Text.Text = "Kick"
			template.Invite.BackgroundColor3 = Color3.new(1, 0, 0)

			return
		end

		if player.UserId == Players.LocalPlayer.UserId then
			template.Invite.Text.Text = "You"
			template.Invite.BackgroundColor3 = Color3.new(0.666667, 0.666667, 0.666667)

			return
		end

		if player.PlayerStats.PartyStats.PartyInviteId.Value == Players.LocalPlayer.UserId then
			template.Invite.Text.Text = "Invited"
			template.Invite.BackgroundColor3 = Color3.new(0.666667, 0.666667, 0.666667)

			return
		end
		
		if player.PlayerStats.PartyStats.PartyInviteDebounce.Value then
			template.Invite.Text.Text = "Busy"
			template.Invite.BackgroundColor3 = Color3.new(0.666667, 0.666667, 0.666667)
			
			return
		end

		if player.PlayerStats.PartyStats.PartyInviteId.Value == 0 and player.PlayerStats.PartyStats.Party.Value == 0 and player.UserId ~= Players.LocalPlayer.UserId then
			template.Invite.Text.Text = "Invite"
			template.Invite.BackgroundColor3 = Color3.new(0, 0.666667, 0)

			return
		end
	end
end

function HandleFrameButtons(template,player)
	local PlayerLeftConnection
	local PartyStatChangedConnection
	local PartyInviteIdStatChangedConnection
	local PartyInviteDebounceConnection
	local ButtonConnection

	-- Find the stats
	local PlayerStats = player:WaitForChild("PlayerStats",3)
	local PartyStats = PlayerStats:WaitForChild("PartyStats",3)

	-- The stats
	local Party = PartyStats:WaitForChild("Party",3)
	local PartyInviteId = PartyStats:WaitForChild("PartyInviteId",3)
	local PartyInviteDebounce = PartyStats:WaitForChild("PartyInviteDebounce",3)

	-- Connect if the player leave
	PlayerLeftConnection = Players.PlayerRemoving:Connect(function(leavingplayer)
		if leavingplayer == player then

			-- Disconnect the stats
			PartyStatChangedConnection:Disconnect()
			PlayerLeftConnection:Disconnect()
			PartyInviteDebounceConnection:Disconnect()

			-- Disconnect the button
			ButtonConnection:Disconnect()

			-- Delete the template
			template:Destroy()
			player = nil
			template = nil
		end
	end)

	-- Connect when inviteid stat change
	PartyInviteIdStatChangedConnection = PartyInviteId:GetPropertyChangedSignal("Value"):Connect(function()
		ColorButton(template,player)
	end)

	-- Connect when party stat change
	PartyStatChangedConnection = Party:GetPropertyChangedSignal("Value"):Connect(function()
		ColorButton(template,player)
	end)
	
	-- Connect if player debounce is changing
	PartyInviteDebounceConnection = PartyInviteDebounce:GetPropertyChangedSignal("Value"):Connect(function()
		ColorButton(template,player)
	end)

	-- Connect when the button is clicked
	ButtonConnection = template.Invite.InviteButton.MouseButton1Click:Connect(function()
		if template.Invite.BackgroundColor3 == Color3.new(0, 0.666667, 0) then
			if template.Invite.Text.Text == "Invite" then
				game.ReplicatedStorage.RemoteEvents.Party.CallPartyInvitation:FireServer(player,"Invite")
			else 
				return
			end 
			
		elseif template.Invite.BackgroundColor3 == Color3.new(1, 0, 0) then
			if template.Invite.Text.Text == "Kick" then
				game.ReplicatedStorage.RemoteEvents.Party.CallPartyInvitation:FireServer(player,"Kick")
			else
				return
			end
		
		else
			return
		end
	end)
end

function CreateFrame(player)
	local success,err = pcall(function()
		local template = scrollingframe.Template:Clone()

		template.Parent = scrollingframe
		template.Visible = true
		template.Name = player.Name
		template.TextLabel.Text = player.Name

		local successi, errorm = pcall(function()
			template.FrameForImage.PlayerImage.Image = Players:GetUserThumbnailAsync(player.UserId,Enum.ThumbnailType.HeadShot,Enum.ThumbnailSize.Size60x60)
		end)

		if not successi then
			print(errorm)
		end

		ColorButton(template,player)

		-- Handle Buttons
		task.spawn(HandleFrameButtons,template,player)
	end)

	if not success then
		warn("FAILED CREATING PARTY FRAME : "..err)
	end
end

-- Get all players in server when joigning
for _, player in Players:GetPlayers() do
	CreateFrame(player)
end

-- Connect to player addeds
Players.PlayerAdded:Connect(function(player)
	task.wait(0.05)
	CreateFrame(player)
end)

script.Parent.Start.TextButton.MouseButton1Click:Connect(function()
	StartParty:FireServer()
end)

local PlayersInYourParty = {}

while wait(1) do
	PlayersInYourParty = {}

	for _, player in Players:GetPlayers() do
		if player.PlayerStats.PartyStats.Party.Value == LocalPlayer.UserId then
			table.insert(PlayersInYourParty,player)
		end
	end

	script.Parent.ActivePlayers.Text = #PlayersInYourParty.."/50"
	if #PlayersInYourParty > 0 then
		script.Parent.Start.BackgroundColor3 = Color3.new(0.333333, 1, 0)
	else
		script.Parent.Start.BackgroundColor3 = Color3.new(0.298039, 0.298039, 0.298039)
	end
end

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.