Why does CharacterLoaded sometimes not fire when you first boot up the server?

player.CharacterAppearanceLoaded:Connect(function(char)
		print("charloaded")
end)

Most of the time when I go in test mode in studio “charloaded” prints, but sometimes it doesn’t print and I have no idea what causes this. When it doesn’t print “charloaded” if I reset it does then print. I’m guessing its something like the char loaded before the script and so the script didn’t catch the char being loaded? I’m really not sure though and how would I go about fixing that?

1 Like

I usually add repeat wait() until player.Character at the top of my scripts before dealing with the player character. So far it’s worked 100% of the time.

1 Like

You should never have to do that when event-based programming is staring at you in the face. This is a bad habit some developers still have of abusing loops over using what’s already given.

local character = player.Character or player.CharacterAdded:Wait()

Doesn’t seem to be what the thread is talking about though, but there’s no context. @TheCreatorBenn Could you explain your situation or Studio setup or show more code? In a similar vein like the above code I wrote, you may want to use HasAppearanceLoaded to serve as your initial state in case the character’s appearance does load ahead of CharacterAppearanceLoaded being fired.

if not player:HasAppearanceLoaded() then
    player.CharacterAppearanceLoaded:Wait()
end

print("Character appearance loaded")

Obviously very rudimentary and I’m not accounting for any potential hangs that may arise out of this (the latter having fired but the former returning false).

5 Likes
local Players = game:GetService("Players")
local playerStatsLoaded = false

Players.PlayerAdded:Connect(function(player)
	--creating leaderboard, loading datastore and creating player stat folder
	--------------------------------
	player.CharacterAppearanceLoaded:Connect(function(char)
		print("charloaded")
		repeat wait(.2) until playerStatsLoaded
		--anything that has to be loaded onto the character like teamclothing
		--as well as character size based off of the playerStat values
	end)
end)

Speaking of loopholes is what I used at “repeat wait(.2) until playerStatsLoaded” considered a loophole? The purpose of it is because I need to wait until the data is successfully loaded for it to get the data from my playerStats folder and then applied to modify the character size. If this is what you’d also consider a loophole, what would be a better way to set this up? I also noticed this problem only happening in studio or at least I haven’t seen it happen inside a real server. In studio it happens about 1/4 of the time where the first time loading in nothing inside of CharAppLoaded runs.

I’m also a little bit confused what difference is between these two
Is :HasAppearanceLoaded() faster?

Players.PlayerAdded:Connect(function(player)
  if not player:HasAppearanceLoaded() then
      player.CharacterAppearanceLoaded:Wait()
  end
  print("Character appearance loaded")
end)

vs

Players.PlayerAdded:Connect(function(player)
	player.CharacterAppearanceLoaded:Connect(function(char)
		 print("Character appearance loaded")
	end)
end)

Repeat is a loop. There are functions like WaitForChild that exist if you’re waiting for the existence of something like a folder containing player data if you’re not keeping it pure Luau.

You rarely need to use loops or you should make it so that you rarely need them to begin with. Always try to get an event-driven system as much as possible until it’s actually necessary for you to poll. Waiting for the existence of data with Roblox DataStores does not qualify as a scenario where you need to poll. A lot of cases of polling by Roblox developers just highlight bad code structuring.

Regarding the bottom, they are different and it depends on your use case which one you should use or if you want to write it a different way. The first one only runs once and it uses HasAppearanceLoaded as an initial state to see if it should wait for CharacterAppearanceLoaded to fire or not. The second one just connects a lambda to run every time CharacterAppearanceLoaded runs.

If the second scenario is what you’re looking for, then you’d use HasAppearanceLoaded as an initial state to run a function for the very first character in case it gets added before CharacterAppearanceLoaded connects and thus doesn’t run the function for the first character.

local function onAppearanceLoaded(character)
    -- Code here
end

player.CharacterAppearanceLoaded:Connect(onAppearanceLoaded)
if player.Character and player:HasAppearanceLoaded() then
    onAppearanceLoaded(player.Character)
end
1 Like

After some testing what seems to be happening was that the computer was still reading the code datastores and the character is loading during that. So then once it got to the CharacterAppearanceLoaded event it didn’t fire since the char was already loaded before the computer was able to read to that line of code. What I ended up doing was putting the data loading after CharacterAppearanceLoaded and that worked out.