.ChildAdded Problem

The event .ChildAdded isn’t… accurate enough? Idk what to call it.

Anyway, the problem is, it doesn’t fire every time. I use it for my leaderboard, and I’d expect it to fire every time a Child is added. But no, it only fires around 80% of the time, as I tested it about 50 times. There’s also other problems such as it firing twice or too early.
This can get really annoying, as in my case, I get many complaints about the leaderboard “glitching”, even though it’s just not being loaded.

1 Like

Can you upload a simple place/file that reproduces the issue? I’ve never had problems with ChildAdded missing things or firing twice.

2 Likes
local function ChildAdded()
	print("A Child Has Been Added")
end

game.Players.ChildAdded:Connect(function()
	ChildAdded()
end)

If you put this in a localscript, start up a test server with 2+ players, it doesn’t print it. It might just be a thing on studio, not sure.

Player1 joins first - should print it when Player2 joins for Player1.

To start, you should probably use PlayerAdded, or add a check to make sure ChildAdded is a player (I assume you are checking for players). Is there anything before the script that causes it to yeild, like a WaitForChild, or Http call?

No, that’s the full script - just made it as a test.
And does .PlayerAdded work client side now? Haven’t tried it in a while, but last time I did it didn’t work.
Also I already used the workaround for this using a remote & .PlayerAdded on the server

Fixed in 2015.

Oh dear… I only started scripting 6 months ago or so. Didn’t seem to work back then:question:

Um… are you sure it works?
https://gyazo.com/6e9a425d814dd2bff969b3c509da84cb
I have a function that loads the players. Which is supposedly meant to be called when a player is added.
It is also called for the player that joins as soon as he/she joins, which is why Player2 has it all and Player1 doesn’t

Edit | Also, it was working fine with the Remote Event & .PlayerAdded on the server.
When I used .PlayerAdded on the server & fired with a remote ^
https://gyazo.com/11db7684a68956480d41d5eebfb05779

Running the code you provided earlier seems to work as expected.

Make sure you are looping through the players that are already on the server for a new player that joins, so that these are added to the leaderboard as well.

Do something similar to:

for _,Player in pairs(game.Players:GetPlayers())do
	AddPlayer(Player)
end

game.Players.PlayerAdded:Connect(function(Player)
	AddPlayer(Player)
end)
1 Like
function LoadPlayers()
	PlayerHolder:ClearAllChildren()
	local Players = game.Players:GetPlayers()
	for i = 1, #Players do
-- Only the start of the Function that loops through the players.
-- LoadPlayers is the function that loads the leaderboard.
LoadPlayers() -- Fires it for the player that joins.
LeaderboardEvent.OnClientEvent:Connect(function()
	LoadPlayers()
end)
-- LeaderboardEvent is fired from the server with a `.PlayerAdded` which works fine

Though if I instead use .PlayerAdded on the client, it won’t call the function.

That code seems to be cut in the middle.

Perhaps you could post the entire code instead. That way it will be easier to see what might be causing it.

Yeah, if your code is for a localscript, keep this in mind

doesn’t explain ChildAdded firing twice for the same instance, if that even happens

1 Like
Whole Code
local Gui = script.Parent.PlayerHolder.Frame
local PlayerHolder = Gui:WaitForChild("Full") -- Frame that holds all the players.

local UpdateLB = game.ReplicatedStorage.Remotes.UpdateLB
local GetData = game.ReplicatedStorage.Remotes.GetData
local LeaderboardEvent = game.ReplicatedStorage.Remotes.LeaderboardEvent
local data = "Mobux"
local WaitCounter = 0

function LoadPlayers()
	PlayerHolder:ClearAllChildren()
	local Players = game.Players:GetPlayers()
	for i = 1, #Players do
		local PlrName = Players[i].Name
		GetData:FireServer(data, PlrName)
		GetData.OnClientEvent:Connect(function(Mobux, ToMobux) -- To get the player's currency data, to display it on the leaderboard.
			if ToMobux == "ToMobux" then
				mobux = Mobux
			end
		end)
		wait(0.2)
		local YSize = 0.16
		local Position = ((i * YSize + 0.02) - YSize)
		local PlayerButton = script.PlayerTemplate:Clone()
		if PlayerButton:FindFirstChild(Players[i].Name) then -- This is for players that have a "Special Tag" - It's just a thing for my game.
			PlayerButton[Players[i].Name].Visible = true
			PlayerButton.PlrName.Text = ""
		else
			PlayerButton.PlrName.Text = PlrName
		end
		PlayerButton.Name = Players[i].Name
		PlayerButton.Parent = PlayerHolder
		local thePlayer = game.Players:FindFirstChild(PlayerButton.Name)
		PlayerButton.Position = UDim2.new(0, 0, Position, 0)
		if game:GetService("GamePassService"):PlayerHasPass(thePlayer, 837859114) then
			PlayerButton:WaitForChild("Mobux").Text = "Inf"
		else
			repeat
				wait()
				WaitCounter = WaitCounter + 1
			until mobux or WaitCounter == 20 -- Sorry for the mess here. Was just making extra checks n that.
			WaitCounter = 0
			if mobux then
				PlayerButton:WaitForChild("Mobux").Text = mobux
			end
		end
	end
end
LoadPlayers()
LeaderboardEvent.OnClientEvent:Connect(function()
	LoadPlayers()
end)

UpdateLB.OnClientEvent:connect(function(dataName, dataValue, playerName) -- Fired when a players' currency changes, to update it on the leaderboard.
	local player = game.Players:FindFirstChild(playerName)
	if dataName == "Mobux" and not game:GetService("GamePassService"):PlayerHasPass(player, 837859114) then
		repeat
			wait()
		until PlayerHolder:FindFirstChild(playerName)
		PlayerHolder:FindFirstChild(playerName).Mobux.Text = dataValue
	elseif game:GetService("GamePassService"):PlayerHasPass(player, 837859114) == true then
		PlayerHolder:FindFirstChild(playerName).Mobux.Text = "Inf"
	end
end)

And yeah i’m aware that it’s not exactly efficient. Haven’t been a scripter for long :confused: But at least it works, right?
Edit-Putting in comments to explain certain things

You are doing things for each player that causes the script to yield. Such as:

game:GetService("GamePassService"):PlayerHasPass(thePlayer, 837859114) 

and

repeat
	wait()
	WaitCounter = WaitCounter + 1
until mobux or WaitCounter == 20 

I’d suggest moving that to somewhere else, and not the GUI update function.

That’s not the problem - .PlayerAdded just doesn’t fire on the client…

Besides, I did the WaitCounter thing so it doesn’t get stuck in an infinite loop. So it’s definitely not the problem.

Have you confirmed this with a simple script, making sure that it isn’t something else that causes it?

Your for each player loop yields at least 0.2s for each player. If you have 10 players, your script will yield for at least 2 seconds. If anyone joins during that time, they won’t be iterated over in the loop.

1 Like

Welp, how else would I do it?

Coroutine for each player.

I’m a bit confused how your whole GetData/LeaderboardEvent is setup though.

This code has me feeling some type of way so il help you out a bit

First section:

		GetData:FireServer(data, PlrName)
		GetData.OnClientEvent:Connect(function(Mobux, ToMobux) -- To get the player's currency data, to display it on the leaderboard.
			if ToMobux == "ToMobux" then
				mobux = Mobux
				PlayerButton:WaitForChild("Mobux").Text = mobux
			end
		end)

You REALLY should be using remote functions here to return the table from the server while this can yield you can run these operations in spawn() or a co routine as they aren’t that intensive. Using the same remote event to listen to and fire data from is just wrong imo.

Second section:

wait(0.2)

There’s no reason to yield the script in this position

Third section:

		local PlayerButton = script.PlayerTemplate:Clone()
		if PlayerButton:FindFirstChild(Players[i].Name) then -- This is for players that have a "Special Tag" - It's just a thing for my game.
			PlayerButton[Players[i].Name].Visible = true
			PlayerButton.PlrName.Text = ""
		else
			PlayerButton.PlrName.Text = PlrName
		end

You should probably clone the master PlayerButton outside of the LoadPlayers function and then remove it as to prevent a duplicate master from still being parented to the gui.

Fourth section:

	if game:GetService("GamePassService"):PlayerHasPass(thePlayer, 837859114) then
		PlayerButton:WaitForChild("Mobux").Text = "Inf"
	else
		repeat
			wait()
			WaitCounter = WaitCounter + 1
		until mobux or WaitCounter == 20 -- Sorry for the mess here. Was just making extra checks n that.
		WaitCounter = 0
		if mobux then
			PlayerButton:WaitForChild("Mobux").Text = mobux
		end
	end--]]

Once again with the yielding, what I would do is run this section inside spawn() and then remove the WaitCounter as there isnt a purpose to it besides using up resources, there’s no reason for additional checks.

As @einsteinK said, yielding inside these functions is whats causing your ChildAdded event not to fire. Its not firing because the event hasn’t connected, since the function is still yielding from the loading players.

TLDR: Stop unnecessary yielding

1 Like