If you don’t save user data with ProfileService or DataStore2, then you’re saving data wrongly (unless you’ve taken proper steps to making a secure data store). I wanted to make this tutorial because I had to figure out how to use Profile Service, and it was hard. But in this tutorial, it won’t be, because you’re not figuring this out on your own.
Why can’t I just setup my own data store?
Well, the issue with that is it’ll take up so much valuable time. There’s so much research and experience needed to make your own data store module that you’d have to dedicate months of hard work and research. But if there’s already a FREE resource out there, why not use it? A bunch of big games use it too.
How to write it out
Make a new server script, and paste this code into it:
-- ProfileTemplate table is what empty profiles will default to.
-- Updating the template will not include missing template values
-- in existing player profiles!
local ProfileTemplate = {
Cash = 0,
Items = {},
LogInTimes = 0,
}
----- Loaded Modules -----
local ProfileService = require(game.ServerScriptService.ProfileService)
----- Private Variables -----
local Players = game:GetService("Players")
local ProfileStore = ProfileService.GetProfileStore(
"PlayerData",
ProfileTemplate
)
local Profiles = {} -- [player] = profile
----- Private Functions -----
local function GiveCash(profile, amount)
-- If "Cash" was not defined in the ProfileTemplate at game launch,
-- you will have to perform the following:
if profile.Data.Cash == nil then
profile.Data.Cash = 0
end
-- Increment the "Cash" value:
profile.Data.Cash = profile.Data.Cash + amount
end
local function DoSomethingWithALoadedProfile(player, profile)
profile.Data.LogInTimes = profile.Data.LogInTimes + 1
print(player.Name .. " has logged in " .. tostring(profile.Data.LogInTimes)
.. " time" .. ((profile.Data.LogInTimes > 1) and "s" or ""))
GiveCash(profile, 100)
print(player.Name .. " owns " .. tostring(profile.Data.Cash) .. " now!")
end
local function PlayerAdded(player)
local profile = ProfileStore:LoadProfileAsync("Player_" .. player.UserId)
if profile ~= nil then
profile:AddUserId(player.UserId) -- GDPR compliance
profile:Reconcile() -- Fill in missing variables from ProfileTemplate (optional)
profile: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] = profile
-- A profile has been successfully loaded:
DoSomethingWithALoadedProfile(player, profile)
else
-- Player left before the profile loaded:
profile:Release()
end
local DataLoaded = Instance.new("StringValue",player)
DataLoaded.Name = "DataLoaded"
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
end
----- Initialize -----
-- In case Players have joined the server earlier than this script ran:
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)
Note, do not remove the comments yet, they’ll help you. Also, go to here if you can’t figure something out or if you need more information:
Save your player data with ProfileService! (DataStore Module) - Resources / Community Resources - DevForum | Roblox
Step 1: Firstly, test it out a little with their default “Cash” leader stat. If it saves your data after the Cash is changed, go on from there by removing the cash giving functions.
Step 2: Put in your default player data in the Profile Template.
Step 3 Go to the PlayerRemoved function and find this area:
function PlayerRemoved(player)
local profile = Profiles[player]
if profile ~= nil then
profile:Release()
end
end
Now, just save your player’s data the same way you would with the default data saving method, by doing something like this:
profile.Data.Wins = player.leaderstats.Wins.Value
Step 4 If you didn’t set up your data store saving method properly and your game is kind of popular, then you’ll want to use the data store you’ve been using and disable it from saving. Then, when they first join, make a new value inside the player called, “ProfileHoldOut.” At the very bottom of the if statement for success of loading their data, you do:
YourDataStore:RemoveAsync(player.UserId)
Then after your if statement for success of loading their data, you delete ProfileHoldOut. Now, go to the Profile Service script you made, and at the very top of the player added function, you put:
repeat task.wait(0.1) until not player:FindFirstChild("ProfileLoadoutHold")
Now after all data is loaded, you’ll see a value called “DataLoaded” is created. This is for client scripts loading information like displaying their equipped weapon for example. So before trying to display a player’s possible equipped weapon, you can do this:
player.EquippedWeapon.ChildAdded:Connect(function()
end)
Since data doesn’t load right away, you’ll have to instead listen for when the object will get added.
Closing
You might be wondering about why I didn’t make a tutorial about DataStore2, and that’s because DataStore2 is older than Profile Service. If you do use DataStore2, you don’t need to change to Profile Service. DataStore2 works great and is used in games like Dungeon Quest. It’s always best to use the latest and greatest stuff though.
Please tell me if I did anything wrong in this tutorial, I’ll edit it right away!