Issue loading player data with ProfileService

I’ve been spending a lot of time trying to figure out how to implement ProfileService into my game with character customization. I’m attempting to save/load the money and level that the player has, as well as their character’s hair, face, and skin color. I save the money, level, hair, and face as integer values (for hair and face it is the asset ID) as well as each individual RGB value (Color3.R, Color3.G, Color3.B) for the skin inside a table in a module script.

The player customizes their character from a screen GUI which fires server events to change their character appearance. There is no accessing the player profile here. The character will have already loaded in with either their saved appearance (if they have one) or the default which is stored in a HumanoidDescription.

From a distance, it seems to work perfectly well… until you join back into the game to see that the player has no saved profile. Initially, I thought that when a player joins for the very first time their data does not get saved for some reason, but now I’m not so sure. I’m also talking about in-game, and not in-studio testing.

This is the code I use for my module script that requires ProfileService. This is named “DataManager” in ServerScriptService.

local ProfileService = require(script.ProfileService)
local RS = game:GetService("ReplicatedStorage") 
local Players = game:GetService("Players")

local Profiles = {}

local ProfileStore = ProfileService.GetProfileStore(
"PlayerData", {
		-- profile template defaults
		SkinR = 239,
		SkinG = 215,
		SkinB = 167,
		Hair = 16630147,
		Face = 20418658,
		Money = 100,
		Level = 0

	}
) 

local function playerDataLoaded(player) -- initializes the player leaderstats based on what their profile contains
	local profile = Profiles[player]

	local folder = Instance.new("Folder")
	folder.Name = "leaderstats"
	folder.Parent = player
	
	local level = Instance.new("IntValue")
	level.Name = "Tier"
	level.Value = profile.Data.Level
	level.Parent = folder
	
	local money = Instance.new("IntValue")
	money.Name = "Money"
	money.Value = profile.Data.Money
	money.Parent = folder

	spawn(function()
		while true do
			local profile = Profiles[player]
			
			if profile ~= nil then
				level.Value = profile.Data.Level
				money.Value = profile.Data.Money
			end
			
			wait (0.1)
		end
	end)
end

local function onPlayerAdded(player)
	local profile = ProfileStore:LoadProfileAsync(
		"Player_" .. player.UserId, -- this is the key I use 
		"ForceLoad"
	)
	
	if profile ~= nil then
		profile:ListenToRelease(function()
			Profiles[player] = nil
			player:Kick("Your saved data has been loaded remotely. Please rejoin.")
		end)
		
		if player:IsDescendantOf(Players) then
			Profiles[player] = profile
			playerDataLoaded(player, profile)
			
		else -- player left before profile loaded
			profile:Release()
		end
	else -- profile does not exist
		player:Kick("Unable to load player data. Please rejoin the game.")
	end
end

-- player before this script ran:
for _, player in ipairs(Players:GetPlayers()) do
	spawn(function()
		onPlayerAdded(player)
	end)
end


Players.PlayerAdded:Connect(onPlayerAdded)
Players.PlayerRemoving:Connect(function(player)
	local profile = Profiles[player]
	if profile ~= nil then
		profile:Release()
	end
end)

function Profiles:Get(player) -- i don't really use this. It's easier to access the module's table DataManager[player]
	local profile = Profiles[player]
	
	if profile then
		return profile.Data
	else 
		print("No profile found.") -- this is what I keep seeing over and over and over
	end
end

return Profiles

The above code seems to work. But when I try to retrieve the player’s profile from another server script, the profile is never found. I’ve tried playing the game (outside studio), leaving, then rejoining a few minutes later to see if the profile would be loaded the second time, but no. :frowning:

Here is the server script that requires the above module and handles the character appearance (has a HumanoidDescription as a child to apply to the player’s character when the values have been edited appropriately):

local Players = game:GetService("Players")

local RS = game:GetService("ReplicatedStorage")
local SS = game:GetService("ServerScriptService")
local DataManager = require(game.ServerScriptService.DataManager)

local starterDescription = script:FindFirstChild("StarterDescription") -- premade humanoiddesc inside this script

Players.PlayerAdded:Connect(function(player)
	local savedCustomization = DataManager[player]

	if savedCustomization ~= nil then
		local newColor = Color3.new(savedCustomization.Data.SkinR, savedCustomization.Data.SkinG, savedCustomization.Data.SkinB)

		starterDescription.HeadColor = newColor
		starterDescription.TorsoColor = newColor
		starterDescription.LeftArmColor = newColor
		starterDescription.RightArmColor = newColor
		starterDescription.RightLegColor = newColor
		starterDescription.LeftLegColor = newColor
		starterDescription.HairAccessory = savedCustomization.Data.Hair
		starterDescription.Face = savedCustomization.Data.Face
	else
		print("There was no data for "..player.Name) -- this is also what I keep seeing
	end

	local function CharacterAdded(char)	
		local humanoid = char:FindFirstChild("Humanoid")
		
		humanoid.Died:Connect(function()
			starterDescription = char.Humanoid:GetAppliedDescription() or starterDescription -- handles the player appearance, whether or not it exists
			player:LoadCharacterWithHumanoidDescription(starterDescription)
		end)
	end
	
	if player.Character ~= nil then
		CharacterAdded(player.Character)
	end

	player.CharacterAdded:Connect(CharacterAdded)
	
	player:LoadCharacterWithHumanoidDescription(starterDescription)
end)

I’ve followed the tutorials included in the ProfileService post and I’ve done what I think is right to make it fit my needs. It seems like I’ve deleted my code and restarted several times now to no avail.

Also, I’m under the impression that anytime data in a player’s profile is edited, it is saved. This makes me think that my problem has to do with retrieving, and not applying, the player’s data.

Any help or advice on this issue would be greatly appreciated. I’m excited to implement ProfileService, but my lack of expertise has caught up with me.

1 Like

Briefly checking your code I see a potential race condition between the profile being stored to the data manager module table and .PlayerAdded listener on external scripts trying to get profiles.

You’ll need to let your scripts somehow know when the profile is actually available AFTER the player has been added. Custom script signals would be ideal for this.

2 Likes
local profile = DataManager[player]
local function doStuff()

end

if profile then
    doStuff()
else
    spawn(function()
        while player:IsDescendantOf(game:GetService("Players")) do
            wait(0.1)
            profile = DataManager[player]
            if profile then
                doStuff()
                break
            end
        end
    end)
end

I started using ProfileService yesterday and had a similar issue but this is how I fix it, not sure if it’s the best method though. The script you are running loads faster than the ProfileService module.

This is some very useful information that I didn’t think or know about. I’ll look into using script signals and see if they solve the issue. Thank you!