DataStores - Beginners to Advanced

if the session is blocked from this cycle there will be no exit, because the save function has such a condition that does not set the session to nil

Do you mean that when the player leaves while his leaderstats didn’t get created yet but his sessionlock set to true, it will stay true, “banning” him for 1800 seconds? Do I understand correctly?

In the last change in the setup function, you removed the check for 1800 seconds, now if the session is blocked, the setup function will be repeated indefinitely. Why did you remove the check for 1800 seconds from the setup function?

Oh, I had the wrong thing copied, in the tutorial, it should be correct though.
Edited my message now.

Thank you for noticing!

Going through your tutorial, it’s pretty well-made mate! I can probably see the amount of work you put into this. Though, could you tell me the theme you use for VSC? It looks very soft. But other than that, amazing job!

Thanks.
I use the One Monokai Theme which is available as extension in VSC and ColdCode to take screenshot excerpts from my code.

i have a question for you, why are you using a bindable Event in the onShutdown function
?

Question, for Berezaa’s Method, is there a way to save multiple values? Thank you!

I would put all the values into a table.

You probably shouldn’t use it anymore though, since Roblox added Versioning to Data Stores which you can use to achieve the same goal.

Once again I remind everyone that if you want a safe Data Store, you should use a module like ProfileService instead, I only created this tutorial to show some methods that can be applied to make datastoring safer.

2 Likes

The part where you call the save function (in the onShutdown part of your tutorial) you never passed through that second argument. I’m just confused on why you didn’t do that when in the previous steps above you mentioned a dontWait parameter in the function for situations where you need everything to save all at once.

Just checking but could you do this?

Local leaderstats = Instance.new("Folder", player)

and

local cash = Instance.new("IntValue", leaderstats)

You can, but you should not. Read this announcement for more information:

1 Like

Sorry for bumping, but for some reason Bez’s method refuses to work and ret is always nil.

local __ISVIP = game:GetService("ReplicatedStorage"):FindFirstChild("__ISVIP")
local DataStoreService = game:GetService("DataStoreService")
local Players = game:GetService("Players")
local RunService = game:GetService("RunService")

local function waitForRequestBudget(requestType)
	local currentBudget = DataStoreService:GetRequestBudgetForRequestType(requestType)

	while currentBudget < 1 do
		currentBudget = DataStoreService:GetRequestBudgetForRequestType(requestType)
		task.wait(5)
	end
end
local function safeCall(playerName, func, self, requestType, ...)
	local success, ret

	repeat
		if requestType then
			waitForRequestBudget(requestType) 
		end
		success, ret = pcall(func, self, ...)

		if not success then
			print("Error: " .. ret)
			if string.find(ret, "501") or string.find(ret, "504") then
				return
			end
		end
	until (success) or (playerName and not Players:FindFirstChild(playerName))

	return success, ret
end

local function setUp(player)
	local name = player.Name
	local userId = player.UserId
	local key = "Player_" .. userId
	
	local str = Instance.new("StringValue")
	str.Name = "Codes"
	
	local leaderstats = Instance.new("Folder")
	leaderstats.Name = "leaderstats"

	
	local TIX = Instance.new("IntValue")
	TIX.Name = "TIX"
	
	local dataStore = DataStoreService:GetDataStore(key)
	local orderedDataStore = DataStoreService:GetOrderedDataStore(key)

	local _, pages = safeCall(name, orderedDataStore.GetSortedAsync, orderedDataStore, Enum.DataStoreRequestType.GetSortedAsync, false, 100)
	local currentPage = pages:GetCurrentPage()

	if #currentPage > 0 then
		for _, dataStoreKey in ipairs(currentPage) do
			if not Players:FindFirstChild(name) then return end
			dataStoreKey = dataStoreKey.value
			local success, ret = safeCall(name, dataStore.GetAsync, dataStore, Enum.DataStoreRequestType.GetAsync, dataStoreKey)

			if success then
				str.Value = ret
				TIX.Value = ret
				TIX.Parent = leaderstats
				str.Parent = player
				leaderstats.Parent = player
				break
			end
		end
	else
		str.Value = ""
		str.Parent = player
		TIX.Value = 100
		TIX.Parent = leaderstats
		leaderstats.Parent = player
	end
end

local function save(player, dontWait)
	if not __ISVIP.Value then
		local userId = player.UserId
		local key = "Player_" .. userId
		local leaderstats = player:FindFirstChild("leaderstats")

		if leaderstats then
			local cashValue = leaderstats.TIX.Value
			local strValue = player.Codes.Value
			local dataStore = DataStoreService:GetDataStore(key)
			local orderedDataStore = DataStoreService:GetOrderedDataStore(key)

			local _, pages = safeCall(nil, orderedDataStore.GetSortedAsync, orderedDataStore, Enum.DataStoreRequestType.GetSortedAsync, false, 1)
			local latest = pages:GetCurrentPage()[1] or 0
			local should = (type(latest) == "table" and latest.value or 0) + 1


			safeCall(nil, orderedDataStore.UpdateAsync, orderedDataStore, (not dontWait and Enum.DataStoreRequestType.UpdateAsync), should, function()
				return should
			end)
			safeCall(nil, dataStore.UpdateAsync, dataStore, (not dontWait and Enum.DataStoreRequestType.UpdateAsync), should, function()
				return cashValue,strValue
			end)
		end
	end
end

local function onShutdown()
	if __ISVIP.Value then return end
	if RunService:IsStudio() then
		task.wait(2)
	else
		local finished = Instance.new("BindableEvent")
		local allPlayers = Players:GetPlayers()
		local leftPlayers = #allPlayers

		for _,player in ipairs(allPlayers) do
			coroutine.wrap(function()
				save(player, true)
				leftPlayers -= 1
				if leftPlayers == 0 then
					finished:Fire()
				end
			end)()
		end

		finished.Event:Wait()
	end
end
Players.PlayerAdded:Connect(setUp)
Players.PlayerRemoving:Connect(save)
game:BindToClose(onShutdown)
for _, player in Players:GetPlayers() do
	coroutine.wrap(setUp)(player)
end



while true do
	task.wait(60)
	for _, player in Players:GetPlayers() do
		coroutine.wrap(save)(player)
	end
end

Im confused about “.GetAsync” in pcalls and requesting budgets. is it a function call or what?

idk nothing aboir=t datastire and i only know whats the basics. I dont understand the budgettype thin and the getordereddatastored. What does it mean?

I was trying to understand it.

But I tried everything for it to work with boolvalues but yea still does not work…

local DataStoreService = game:GetService("DataStoreService")
local Players = game:GetService("Players")

local dataStore = DataStoreService:GetDataStore("PlayerLocker")

local function setUp(player)
	local userId = player.UserId
	local key = userId.."_RadioEquipped"
	
	local FolderOwned = Instance.new("Folder")
	FolderOwned.Name = "ItemsOwned"
	FolderOwned.Parent = player

	local RadioOwned = Instance.new("BoolValue")
	RadioOwned.Name = "RadioOwned"
	RadioOwned.Parent = FolderOwned

	local FolderEquipped = Instance.new("Folder")
	FolderEquipped.Name = "ItemsEquipped"
	FolderEquipped.Parent = player

	local RadioEquipped = Instance.new("BoolValue")
	RadioEquipped.Name = "RadioEquipped"
	RadioEquipped.Parent = FolderEquipped

	local data = dataStore:GetAsync(key)
	
	RadioEquipped.Value = data or false
end

local function save(player)
	local userId = player.UserId
	local key = userId.."_RadioEquipped"
	local ItemsEquipped = player:FindFirstChild("ItemsEquipped")
	
	if ItemsEquipped then
		local RadioEquippedvalue = ItemsEquipped.RadioEquipped.Value
		dataStore:SetAsync(key, RadioEquippedvalue)
	end
end

local function onShutDown()
	task.wait(1)
end

game:BindToClose(onShutDown)
Players.PlayerAdded:Connect(setUp)

You didn’t add the Players.PlayerRemoving event. Also, your BindToClose is incomplete.

Sorry, I don’t understand the PCall part for the three parameters. Can someone please explain in more detail for that part?

pcall has 2 arguments, the function and the arguments you want to pass in (optional)
pcall(dataStore.GetAsync, dataStore, key); its just the same as

pcall(function()
    return dataStore:GetAsync(key)
end)

if you got confused with, why pass in the dataStore? any method in a table/object has its functional part, when you do dataStore:GetAsync(key), dataStore is being passed in as the first parameter even tho you don’t see it, which is equal to dataStore.GetAsync(dataStore, key)

1 Like

Ahh I see thank you for telling me!

1 Like

im going to try and understand the code, so i can implement something like this in future projects

thanks for the help (: