ProfileServices handles a lot of things so that you don’t have to think about them. Assuming you already have proper error handling (which ProfileService does for you), when you use GetAsync and UpdateAsync normally, your data is still sometimes susceptible to being overwritten such as when a player joins and leaves quickly.
Since ProfileService doesn’t use DataStore2’s OrderedBackups method, transferring should be easy. However, you should note that you will have to convert old player data to another format (ProfileService stores some metadata along with your actual data).
As for global leaderboards, I’m not sure what the best method is, but right now I simply use a FastSpawn (or spawn/coroutine) in my :ListenToRelease() connection that saves data to a global leaderboard.
Assuming your data format can be stored in a single table, all you’d have to do is:
local your_data -- [table]
Profile.Data = your_data
ProfileService does not have a “format”, but you simply can’t set Profile.Data itself to anything other than a table. Obviously, you can store (mostly) whatever you like inside a table.
Try surfing the documentation and hopefully you’ll get a better understanding of the ProfileService workflow.
Go through the troubleshooting manual. If your code does not have faults noted in troubleshooting, you can DM me your data store script source which you can upload to something like pastebin.
I am getting error “tables cannot be cyclic”, how do I fix.
Code (this is not the full code). The code is inside a module script called Data Manager which is parent of profile service module:
local function PlayerAdded(Plr)
local PlayerProfile = ProfileStores.PlayerData:LoadProfileAsync(
"Plr_"..Plr.UserId,
"ForceLoad"
)
if (not PlayerProfile) then
Plr:Kick("Data failed to load.")
else
PlayerProfile:Reconcile()
PlayerProfile:ListenToRelease(function()
Profiles.PlayerData[Plr] = nil
Plr:Kick()
end)
if (Plr:IsDescendantOf(Players)) then
Profiles.PlayerData[Plr] = PlayerProfile
Bindables.OnPlayerProfileLoaded:Fire(Plr,PlayerProfile) -- Error is here
else
PlayerProfile:Release()
end
end
end
Leaderstats (a different script):
DataManager:OnPlayerProfileLoaded(function(Plr,Profile) -- This listens to the bindable event fired
local Wins = Plr:WaitForChild("leaderstats"):WaitForChild("Wins")
Wins.Value = Profile.Data.Wins
end)
If you want the full scripts I can private message them to you.
Edit: This only seems to be happening when I pass the profile as an argument. If I pass for e.g an empty table it works. How do I fix this?
BindableEvents can’t send live references to objects (objects are just tables with data). A BindableEvent will attempt to make a copy of a table, but, in the case of copying a Profile object, it will fail due to circular references being used in the ProfileService system.
Consider using a custom signal module (instead of BindableEvents) like MadworkSignal which can pass live references - It’s part of the ReplicaService package.
Edit: Replaced pointer(...) with coroutine.wrap(pointer)(...). I don’t think this will have any performance problems because the event will only fire when player joins.
Just one question will this be compatible into making game leaderboards and uses API like GetOrderedDataStores() and does session locking actually prevent this type of issues, second, does it have functions like Increment() or Set() because it’s just so long to write profile.Data.Cash = profile.Data.Cash + numberHere and lastly, do you have a function that sets table’s or not or it just automatically just saves it when it is inserted? if not then I’d like an example from you
ProfileService is NOT for making leaderboards / creating ordered OrderedDaraStores. It’s not designed for it nor it would make it easy to do it. OrderedDaraStores don’t need nearly as many precautions (messing them up is rarely a tragedy) and are easy to implement on their own.
local data = profile.Data
data.Cash += number_here
ProfileService keeps Profile.Data saved to the DataStore - you can put mostly any kind of values inside and it will save as long as Profile:IsActive() returns true or until the profile is released.
and the loading code the same code you gave but edited
-- Init profile service
local GameProfileStore = ProfileService.GetProfileStore(
"PlayerData",
PlayerDataSample
)
local function Debug(profile)
if profile ~= nil then
print("Successfully loaded data!")
else
print("Failed to load Data")
end
end
local function KickPlayer(Player)
local PlayerData = GameProfileStore:LoadProfileAsync(
"Player_" .. Player.UserId,
"ForceLoad"
)
if PlayerData ~= nil then
PlayerData:Reconcile() -- Fill in missing variables from ProfileTemplate (optional)
PlayerData:ListenToRelease(function()
Profiles[Player] = nil
-- The profile could've been loaded on another Roblox server:
Player:Kick()
end)
if Player:IsDescendantOf(Players) == true then
Profiles[Player] = PlayerData
-- A profile has been successfully loaded:
Debug(PlayerData)
else
-- Player left before the profile loaded:
PlayerData:Release()
end
else
-- The profile couldn't be loaded possibly due to other
-- Roblox servers trying to load this profile at the same time:
Player:Kick()
end
spawn(function()
if BannedPlayers[tostring(Player.Name)] then
Player:Kick("You are permanently Banned from this game, you are not welcome.")
elseif PlayerData.Stats.Banned == true then
Player:Kick("You are permanently Banned from this game, you are not welcome.")
else
print(Player.Name.." is authorized for this game!")
end
end)
end
local function PlayerAdded(Player)
print("Loading Data for "..Player.Name)
local PlayerData = GameProfileStore:LoadProfileAsync(
"Player_" .. Player.UserId,
"ForceLoad"
)
if PlayerData ~= nil then
PlayerData:Reconcile() -- Fill in missing variables from ProfileTemplate (optional)
PlayerData:ListenToRelease(function()
Profiles[Player] = nil
-- The profile could've been loaded on another Roblox server:
Player:Kick()
end)
if Player:IsDescendantOf(Players) == true then
Profiles[Player] = PlayerData
-- A profile has been successfully loaded:
Debug(PlayerData)
else
-- Player left before the profile loaded:
PlayerData:Release()
end
else
-- The profile couldn't be loaded possibly due to other
-- Roblox servers trying to load this profile at the same time:
Player:Kick()
end
btw the last function without the end isn’t the bug the loading is
Saving same as the old code
local function PlayerRemoved(Player)
local Backpack = Player.Backpack
local profile = Profiles[Player]
if profile ~= nil then
profile:Release()
end
end
Would be helpful if you read through the troubleshooting page first as it seems you’re trying to store Roblox instances which are not serializable (“Path” member).
Yes. It seems like you are saving too much to the DataStore for your items. Simply save the weapon’s identifier and any metadata, and store all of the extra stuff (Path,ClassFor,Type,ChanceToGet,MaxUpgrades,Debounce,CanDamage,IsTradeable) for items in a database.
This module is not for making leaderboards (normal datastore works fine for it) or even currency. This module used mainly to save things like inventory for example.