Save your player data with ProfileService! (DataStore Module)

Please add support for typechecking! :pray:

2 Likes

There’s one on GitHub made by another user iirc, just check the Github repo

just release it, people be craving for an update

how can i migrate my data to profile service? i’ve been using the traditional datastore and experiencing data loss

Definitely interested in the ReplicaService rewrite, even without documentation

ProfileStore

An updated module, successor to ProfileService, has been RELEASED:

3 Likes

I’m having an issue.

I’m using TeleportAsync to attempt to reconnect afk players prior to Roblox’s disconnect. I have my script set to release the players data, then I use ListenToHopReady as well as a small delay prior to teleporting. I’ve verified that :IsActive() is being set to false. When the player is rejoined to the same server, no problem. However, if the player is put into a different server LoadProfileAsync is returning nil indicating two servers attempting to access the profile. In the case that LoadProfileAsync returns nil, I have it set to reconnect players through TeleportAsync which seems to work fine.

What might I be doing wrong? Ideally I want to avoid this double teleport

ProfileService is no-longer supported or being updated, but I would like to know if anyone else has experienced this same problem.

Randomly last night starting around 7:30 PM 3 thousand errors over a period of 6 hours occurred relating to profile service.

I have not seen this error occur on any of my other experiences so it’s pretty bizarre. Thanks!

I’m also getting occasional errors loading data upon joining a place after a teleport

Hello, I am constantly getting an error (not in studio) that tells me that the profile is not existing.
I followed the youtube video given on the very top as a recommendation.
I personally do not get this issue in studio, but in the actual game many users seem to trigger this error.

The source code is pretty much like in the video in terms of getting / loading the actual data in.
I will provide source code to this post here later but does anyone have a idea why this could be?

Thanks in advance.

Yours sincerely, Sxato | Marv.

Source code:

local function added(Player)
    dataTemplate = dataStructure.getStructure(Player)
    local profileStore = PS.GetProfileStore("PlayerProfile", dataTemplate["Saveables"])
    local profile
    repeat
        local success, errormsg = pcall(function()
            profile = profileStore:LoadProfileAsync("TestPlayerProfile" .. Player.UserId)
        end)
        if not success then warn(errormsg) task.wait(1) end
    until success
    
    
    if profile then
        profile:AddUserId(Player.UserId)
        profile:Reconcile()
        
        profile:ListenToRelease(function()
            profiles[Player] = nil
            Player:Kick()
        end)
        
        if not Player:IsDescendantOf(Players) then
            profile:Release()
        else
            profiles[Player] = profile
        end
else
Player:kick()
end
    
    Player:SetAttribute("DataLoaded", true)
end

Can anyone confirm that this should work? Because for some reason my assert in another function is being triggered. The assert has the following in it:

assert(profiles[Player], string.format("Profile does not exist for: %s", Key)

any help is appreciated.

Hey, basically I have no idea what I’m doing, and just copy-pasted it from another one of my games where data saving works, but it doesn’t in this game.

Here’s my script. Can someone tell me what the issue is?

local ProfileTemplate = {
	PlayerStats = {Kills = 0, Wins = 0, Level = 1, Exp = 0, Coins = 0},
	Weapons = 
		{
			WeaponStats = {EquippedKnife = "Classic", EquippedGun = "Classic", EquippedSword = "Classic"},
			Knives = {Classic = 1, Bronze = 0, Silver = 0, Gold = 0, Platinum = 0, Lapis = 0, Obsidian = 0, Diamond = 0, Amethyst = 0, Ruby = 0, Emerald = 0, Turqoise = 0, Flame = 0, Frost = 0, Shadow = 0, Lunar = 0, Supernova = 0, Void = 0},
			Guns = {Classic = 1, Bronze = 0, Silver = 0, Gold = 0, Platinum = 0, Lapis = 0, Obsidian = 0, Diamond = 0, Amethyst = 0, Ruby = 0, Emerald = 0, Turqoise = 0, Flame = 0, Frost = 0, Shadow = 0, Lunar = 0, Supernova = 0, Void = 0},
			Swords = {Classic = 1, Bronze = 0, Silver = 0, Gold = 0, Platinum = 0, Lapis = 0, Obsidian = 0, Diamond = 0, Amethyst = 0, Ruby = 0, Emerald = 0, Turqoise = 0, Flame = 0, Frost = 0, Shadow = 0, Lunar = 0, Supernova = 0, Void = 0}
		}		
}

----- Loaded Modules -----

local ProfileService = require(script.ProfileService)

----- Private Variables -----

local Players = game:GetService("Players")

local ProfileStore = ProfileService.GetProfileStore("PlayerData", ProfileTemplate)

local Profiles = {}

local function DoSomethingWithALoadedProfile(Player, Profile)
	local PlayerStats = Player:WaitForChild("PlayerStats")
	local Weapons = Player:WaitForChild("Weapons")
	
	PlayerStats.Kills.Value = Profile.Data.PlayerStats.Kills
	PlayerStats.Wins.Value = Profile.Data.PlayerStats.Wins
	PlayerStats.Level.Value = Profile.Data.PlayerStats.Level
	PlayerStats.Exp.Value = Profile.Data.PlayerStats.Exp
	PlayerStats.Coins = Profile.Data.PlayerStats.Coins
	
	Weapons.EquippedKnife = Profile.Data.Weapons.WeaponStats.EquippedKnife
	Weapons.EquippedGun = Profile.Data.Weapons.WeaponStats.EquippedGun
	Weapons.EquippedSword = Profile.Data.Weapons.WeaponStats.EquippedSword
	
	
	PlayerStats.Kills.Changed:Connect(function(Value)
		Profile.Data.PlayerStats.Kills = Value
	end)

	PlayerStats.Wins.Changed:Connect(function(Value)
		Profile.Data.PlayerStats.Wins = Value
	end)

	PlayerStats.Level.Changed:Connect(function(Value)
		Profile.Data.PlayerStats.Level = Value
	end)

	PlayerStats.Exp.Changed:Connect(function(Value)
		Profile.Data.PlayerStats.Exp = Value
	end)
	
	PlayerStats.Coins.Changed:Connect(function(Value)
		Profile.Data.PlayerStats.Coins = Value
	end)
	
	for _, Knife in Weapons.Knives:GetChildren() do
		Knife.Changed:Connect(function(Value)
			Profile.Data.Weapons.Knives[Knife.Name] = Value
		end)
	end

	for _, Gun in Weapons.Guns:GetChildren() do
		Gun.Changed:Connect(function(Value)
			Profile.Data.Weapons.Guns[Gun.Name] = Value
		end)
	end

	for _, Sword in Weapons.Swords:GetChildren() do
		Sword.Changed:Connect(function(Value)
			Profile.Data.Weapons.Swords[Sword.Name] = Value
		end)
	end
end

local function PlayerAdded(Player)
	local Profile = ProfileStore:LoadProfileAsync(tostring(Player.UserId))
	if Profile ~= nil then
		Profile:AddUserId(Player.UserId)
		Profile:Reconcile()
		Profile:ListenToRelease(function()
			Profiles[Player] = nil
			Player:Kick()
		end)
		
		if Player:IsDescendantOf(Players) == true then
			Profiles[Player] = Profile
			DoSomethingWithALoadedProfile(Player, Profile)
		else
			Profile:Release()
		end
	else
		Player:Kick() 
	end
end

----- Initialize -----

for _, Player in ipairs(Players:GetPlayers()) do
	task.spawn(PlayerAdded, Player)
end

----- Connections -----

Players.PlayerAdded:Connect(PlayerAdded)

Players.PlayerRemoving:Connect(function(Player)
	local Profile = Profiles[Player]
	if Profile ~= nil then
		Profile:Release()
	end
end)
1 Like

session locking should solve pretty much all of the issue from the datastore side. anything in another layer in on you of course

this could be a race condition
your code might check for the profile before the profile is loaded! datastores aren’t immediate.

do some printing to see the order

usually when I used profileservice I’d have an extra wrapper for it with Signals and :WaitFor functions to deal with this and to overall simplify the use of profiles

Thanks for your response, I ended up yielding the Datastore with a “repeat until” so I do not let the system continue until the profile is there or the player is gone.

All I had to do was change the name of one variable! I love scripting :grin::grin::grin::grin:

Everytime I join the game I got the error where the session is already loaded, changing data key doesn’t solve the problem

I’m attempting to use multiple profile stores in the same game (which probably isn’t intended, but I’m attempting to keep data for specific worlds separate from the player) and I’ve noticed save data being shared between the two, they’re both using different profile stores and player profiles, along with being controlled by different server scripts so I do not know how this is happening.

Do you recommend ProfileStore over ProfileService and why?

You should read the post he linked.

1 Like

If I have two game instances (like a universe), the first one is where character-creation (players customising character) and second is the actual game.

  1. For the scenario above, using profilestore, if I want to add a new data-value (e.g. new currency like diamond) can I just populate the data-template that’s in the first game instance (character creation place) or must I have the data-template in both game instances/places and keep editing both whenever adding new data values?

Asking because, it’d be a lot convenient if I have the template in one place, add a new data, and even if player teleports to the 2nd game instance (where I load profilestore as well) for the datastore to detect the newly created data values that was loaded in the 1st game instance, rather than having to manage multiple game instances and having to put the new data currency in each of them.