DataStores - Beginners to Advanced

Uh, is this how your DataStore script looks like?

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.

3 Likes

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. :grinning:

1 Like

What does this mean? Do I have to do something other than the DataStore script? Thanks.

1 Like

When setting your leaderstat’s values, don’t do it over the client, as the server won’t see it.
For example:

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.

2 Likes

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.

I recommend replacing

with

This creates a BindableEvent and fires it if the data of all players has been saved.

image

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!

4 Likes

Things you mention is really help full in my own Data Store and i apply things you mention and as of now I haven’t get any single of data loss.

I just mention that Seasion locking loads too slowly thats why i dont use UpdateAsync to load data.

1 Like

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?

How would you modify the datastore script to save multiple items to the same datastore?

1 Like

I would save the values in an array or dictionary.

1 Like

The problem is during the autosave and the player leaves it will warnque.

Hm, that’s possible, yeah, but I think it should be fine if it’s just one warning, the queue’s limit should be around 30 requests.

You could maybe higher the amount of time to autosave to lower the probability of it occuring.

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

Thanks!

When saving, you would just use the table instead of a value.


(simplified, should include pcalls, retries, … and maybe use UpdateAsync over SetAsync of course)

Now, when getting the data, it would return the table {50, 100, 150}.


(again, simplified)

From that, you can access the values by simply doing table[1], table[2], …

One thing I would include is ret. Maybe the player is new, in that case, ret should be nil and nil[1], … errors.

But then again, you could use “or {}” after defining table, and that should be fine too, because that way, {}[1] won’t error, just return nil.

2 Likes

Alright thanks!

[symbols limit]

I’m not sure what u mean, can someone explain in more detail?

I don’t think there is too much documentation about this, you can read about it here, though, it’s in the first blue box.

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)

sorry i am confused at where should i put that in?