CharacterAdded functions not loading correctly

I often run into a problem where I have to add wait() to characteradded to wait for everything to load. Sure the wait time can fix it, but if a player lags long enough even 5 seconds isn’t enough. I’ve tried looping through to check if the character is added first, but that’s not too much of a good idea since the characteradded function fires when the character is added. Is there any other ways I can go about loading stuff w/o stuff just ‘not loading’ due to a lag spike?

2 Likes

What are you waiting for that isn’t loaded in time? Please post your code and explain what and where exactly the problem is.

The only reason stuff isn’t loading for me, is because using wait() isn’t an efficient method. I’m just trying to figure out what way would make it work completely so that I won’t need to add a wait() that I can’t depend on 100%.

Please post your code. We cannot help without knowing what you’re trying to do.

I know I have a problem with my code, but I know why and I’m asking you if you could help me figure out a better solution to it. The code is too long to paste.

1 Like

Your problem is that you’re not taking care of what has already happened or what already exists.

local function CharacterAdded(char)
	--Character added code
end

local function PlayerAdded(player)
	--Connect the event
	player.CharacterAdded:Connect(CharacterAdded)
	
	--Take care of when it already exists
	local char = player.Character
	if char then
		CharacterAdded(char)
	end
end

--Connect the event
game.Players.PlayerAdded:Connect(PlayerAdded)

--Take care of when it already exists
for i,v in pairs(game.Players:GetPlayers()) do
	PlayerAdded(v)
end

This should handle any and all situations for when a player and character get added. Notice how I check if things existed after I connected the event. The event is not going to fire for anything that already exists, so you have to run the function on it instead.

EDIT: Don’t use for i,v in next, since the performance gains from Luau are lost when using next (negligible difference in this case, but still fair to note).

23 Likes

Thanks for your help!

1 Like

Why don’t you do that both at once?

game.Players.PlayerAdded:Connect(function(plr)
   
  local char = plr.Character or plr.CharacterAdded:Wait() --waits until plr.Character exists

-- rest of code
end)
2 Likes

Typically when you’re using CharacterAdded you want to apply what you did the first time to the character every time it loads.

2 Likes

By that you would mean loading it when the player’s added and when the character’s added seperately but the same way correct?

Yeah, I guess my snippet is a one-time thing. It was just an example though. You can hook a Humanoid.Died event and add the char variable inside it.

You should not do that whatsoever.

2 Likes

Yeah, once I typed that I regretted typing it right away, Was about to delete, but :man_shrugging:

Thanks for your help too!

It’s always good practice to account for what already exists. Locard’s solution works like a charm in the way that it accounts for players already in the server as well as those entering the server - same with the character. There’s something to understand in that respect though.

Generally, you do not need to enclose your functionality in a handler function, connect it to PlayerAdded and run it for players that already exist in your server. If you find yourself needing to do this, then your code is either yielding at the beginning of its execution or it’s not something that’s implicitly available at runtime or the start of the instance.

As for the character, the same thing applies in relatively the same manner. Locard’s solution covers it again already, though. This is fairly good practice; you can connect a function to be ran every time the Character is added. This isn’t guaranteed to run though! So what you do is handle the character for the first time underneath the connection. It’s a great catch-all in practice.


I guess what I’m trying to say from all this is that somewhere in your code, there is a thread execution yield happening before these events can be connected to. Even a difference of a few seconds can make all the difference as to when the event gets connected and causes a discrepancy in behaviour, such as the one being observed in the OP. That’s what prompted the initial problem.

I gotta say though, as much as I’m aware of these issues, I don’t put them into practice well. Once upon a time ago, I had a GetAsync at the top of my script that was preventing PlayerAdded from being connected immediately. :sweat_smile: I should definitely start implementing this into my code.

1 Like

Super late reply, but I am using this function and I noticed that it only works on one player. Whenever a 2nd person loads in the game the function breaks saying that Char is nil

The code you’re replying to looks fine to me. There’s probably a problem with the way you copied over the structure instead.

Sorry for hopping in this late, but if your waiting till the player’s character is fully loaded, using game.Players.CharacterAppearanceLoaded:Connect(function() runs when every instance(also clothes) is within the players character.

1 Like

I copied and pasted it. However, I use DataStore2 and I create a few leaderstats instances that are connected to the DataStore. I’ll post my code below. (I have tried commenting out the leaderstats code too to see if that was the issue)

local function CharacterAdded(Player, Char)
    -- I also added this because the Char nil error would happen when players respawn	
    Char.Humanoid.Died:Connect(function()
		Player:Kick("ERROR") 
	end)
	
	
	local HRP = Char:WaitForChild("HumanoidRootPart")
	game:GetService("RunService").Stepped:wait()
	--	Weld Controller to players right hand
	local controllerName, controllerModel = "", game.ReplicatedStorage.Assets.Controllers.Starter
	for _, c in pairs(PlayerDataService:ReturnStat(Player, "Controllers")) do
		-- :ReturnStat() returns a table that looks like this: {{"Starter", true, 1, 1}}
		print(unpack(c))
		if c and c[2] == true then
			--	This controller is equipped
			print(Player.Name .. "'s Tool: " .. c[1])
			controllerName = c[1]
			PlayerDataService:Set(Player, "Damage", c[3])
			PlayerDataService:Set(Player, "Cooldown", c[4])
		end
	end
end

local function PlayerAdded(player)
	--	Getting the player and using a retry system
	local retries = 0
	local P = PlayerDataService.new(player)
	repeat
		P = PlayerDataService.new(player)
		retries = retries + 1
		wait()
	until P ~= nil or retries >= 5
	
	if retries >= 5 then
		player:Kick("[DataStore2 Error]: Error loading player data.\nPlease click rejoin.")
	end
	
	--	Leaderstats
	local ls = Instance.new("Folder")
	ls.Name = "leaderstats"
	ls.Parent = player
	
	local Crates = Instance.new("NumberValue")
	Crates.Name = "Crates"
	Crates.Value = P:ReturnStat(player, "Crates") or 0
	Crates.Parent = ls
		
	local Games = Instance.new("NumberValue")
	Games.Name = "Games"
	Games.Value = P:ReturnStat(player, "Games") or 0
	Games.Parent = ls
		
	local Gems = Instance.new("NumberValue")
	Gems.Name = "Gems"
	Gems.Value = P:ReturnStat(player, "Gems") or 0
	Gems.Parent = ls
	
	local Damage = Instance.new("NumberValue")
	Damage.Name = "Damage"
	Damage.Value = P:ReturnStat(player, "Damage") or 1
	Damage.Parent = ls
	
	local Target = Instance.new("ObjectValue")
	Target.Name = "Target"
	Target.Parent = player
	

	--	Connect the event
	player.CharacterAdded:Connect(CharacterAdded)
	
	--	Take care of when it already exists
	local char = player.Character
	if char then
		CharacterAdded(player, char)
	end
end

--	Connect the event
game.Players.PlayerAdded:Connect(PlayerAdded)

--	Take care of when it already exists
for i,v in next,game.Players:GetPlayers() do
	PlayerAdded(v)
end

Apply some basic debugging first: you should comment out parts of this code until it works again, and add some more print statements to better understand what’s going on, then add back pieces of code until you figure out which part is causing issues.

1 Like