local DataStoreService = game:GetService("DataStoreService")
local Players = game:GetService("Players")
local dataStore = DataStoreService:GetDataStore("DataStoreName") --put your DataStore name here
local function setUp(player)
local userId = player.UserId
local key = "Player_" .. userId
local leaderstats = Instance.new("Folder")
leaderstats.Name = "leaderstats"
local money = Instance.new("IntValue")
money.Name = "Money"
local data = dataStore:GetAsync(key)
money.Value = data or 0
money.Parent = leaderstats
leaderstats.Parent = player
end
local function save(player)
local userId = player.UserId
local key = "Player_" .. userId
local leaderstats = player:FindFirstChild("leaderstats")
if leaderstats then
local cashValue = leaderstats.Money.Value
dataStore:SetAsync(key, cashValue)
end
end
local function onShutDown()
wait(1)
end
for _,player in ipairs(Players:GetPlayers()) do
coroutine.wrap(setUp)(player)
end
Players.PlayerAdded:Connect(setUp)
Players.PlayerRemoving:Connect(save)
game:BindToClose(onShutDown)
while true do
wait(60)
for _,player in ipairs(Players:GetPlayers()) do
coroutine.wrap(save)(player)
end
end
That works for me.
Remember to set the values over the server, setting them over the client won’t change them on the server, thus making the DataStore save the old ones (because of Filtering Enabled).
Change .Cash to .Money so it fits your system in your Save function.
The thing is, it’s not reliable for me because I borrowed a custom wait function and use it to wait for 5 seconds and it waits for 4.5 instead. Your function is the same thing.
I created another post because the leaderstats didn’t show, but @colbert2677 said that the while true loop was causing the leaderstats to not show. I have the leaderstats, and the datastore working now.
This seems correct however the server will still not see the change, the player’s cash won’t be set to 500 on the server, only on the client. This is because of Filtering Enabled to prevent exploiters giving themselves unfair advantages.
It seems like when simply iterating through every player and using a function wrapped into a coroutine to save every player’s data, BindToClose will just continue and the game will but shutdown without taking into consideration if the data was saved or not.
This creates a BindableEvent and fires it if the data of all players has been saved.
yields BindToClose until finished is fired (after 30 seconds, the game will be shut down regardless, though).
If you see any occurences of the first method or anything else that should be changed in my tutorial, please tell me.
I updated the tutorial so it uses the latter, but maybe I’ve overseen an occurence.
Thanks!
Thanks! Even though I recommend using ProfileService because bugs are very unlikely to happen (tested a lot), it’s great if your own DataStore is that secure!
Are you sure it’s because of SessionLocking? With SessionLocking you just set a value in your data table to os.time() and compare/update that. I’m not sure about how well UpdateAsync performs against GetAsync in terms of performance, UpdateAsync returns the old data and saves the data returned in the function, so that could make it slower?
That’s a really good tutorial! But i have one simple question: How can we save tables?
Should we do this in order to save them? or is there any other way to do that?
if success then
MusicStatus.Value = ret[1] or false
FOV.Value = ret[2] or 70
musicId.Value = ret[3] or 1836789720
end
can i get help with my datastore?
when i join the game every value just become 100 and cant save
here is my script:
local DataStoreService = game:GetService("DataStoreService")
local Players = game:GetService("Players")
local dataStore = DataStoreService:GetDataStore("DataStoreName") --put your DataStore name here
local function setUp(player)
local userId = player.UserId
local key = "Player_" .. userId
local leaderstats = Instance.new("Folder")
leaderstats.Name = "leaderstats"
local click = Instance.new("IntValue")
click.Name = "Clicks"
local diamond = Instance.new("IntValue")
diamond.Name = "Diamonds"
local rebirth = Instance.new("IntValue")
rebirth.Name = "Rebirths"
local data = dataStore:GetAsync(key)
click.Value = data or 0
diamond.Value = data or 100
rebirth.Value = data or 0
click.Parent = leaderstats
diamond.Parent = leaderstats
rebirth.Parent = leaderstats
leaderstats.Parent = player
end
local function save(player)
local userId = player.UserId
local key = "Player_" .. userId
local leaderstats = player:FindFirstChild("leaderstats")
if leaderstats then
local clickValue = leaderstats.Clicks.Value
dataStore:SetAsync(key, clickValue)
local diamondValue = leaderstats.Diamonds.Value
dataStore:SetAsync(key, diamondValue)
local rebirthValue = leaderstats.Rebirths.Value
dataStore:SetAsync(key, rebirthValue)
end
end
local function onShutDown()
wait(1)
end
for _,player in ipairs(Players:GetPlayers()) do
coroutine.wrap(setUp)(player)
end
Players.PlayerAdded:Connect(setUp)
Players.PlayerRemoving:Connect(save)
game:BindToClose(onShutDown)
while true do
wait(60)
for _,player in ipairs(Players:GetPlayers()) do
coroutine.wrap(save)(player)
end
end
It’s the way you are setting data, you are overriding the recent value set each time. Instead, you should save your data in an array or dictionary:
(pseudo-code)
local data = GetAsync(userId) --(simplified, I think you should make this more secure)
--data is your array/dictionary now
click.Value = data[1] or default
diamond.Value = data[2] or default
rebirth.Value = data[3] or default
...
local tableToSave = {clickValue, diamondValue, rebirthValue}
SetAsync(userId, tableToSave) --(simplified, I think you should make this more secure)