How can I improve my datastore script?

Hello, I have a datastore and It’s working good, But I still want to ask if I can improve it
here is my code

local StatsStore = game:GetService("DataStoreService"):GetDataStore("RealStats_0")

local MoneyLib = require(game.ReplicatedStorage.Modules.MoneyLib)

local Debounces = {}

local function Save(player)
	while Debounces[player.Name] do
		task.wait()
	end

	Debounces[player.Name] = true

	local Data = player.RealStats:GetChildren()

	for i,v in ipairs(Data) do
		Data[i] = {v.Name, v.Value}
	end

	local SaveTimes = 0
	print("Saved")
	repeat
		local s,e = pcall(function()
			StatsStore:SetAsync(player.UserId.."_Stats", Data)
		end)

		if not s then 
			warn(e) 
		end		

		SaveTimes += 1
	until s or SaveTimes == 3	

	task.wait(6)

	Debounces[player.Name] = false
end


game.Players.PlayerAdded:Connect(function(player)

	Debounces[player.Name] = false
	
	local leaderstats = script.leaderstats:Clone()
	leaderstats.Parent = player
	
	local RealStats = script.RealStats:Clone()
	RealStats.Parent = player
	
	for i,v in ipairs(leaderstats:GetChildren()) do
		local Stat = RealStats:FindFirstChild(v.Name)
		v.Value = Stat.Value
		Stat:GetPropertyChangedSignal("Value"):Connect(function()
			v.Value = MoneyLib.DealWithPoints(Stat.Value)			
		end)			
	end
	
	local Data = nil

	local GetTimes = 0

	repeat 
		local s,e = pcall(function()
			Data = StatsStore:GetAsync(player.UserId.."_Stats")
		end)	

		if not s then 
			warn(e)
		end
		GetTimes += 1
	until GetTimes == 3 or s

	if Data then
		for i, v in ipairs(Data) do
			local Stat = RealStats:FindFirstChild(v[1])
			if Stat then
				Stat.Value = v[2]
			end
		end
	end
	
	local Time = 0
	
	coroutine.wrap(function()
		while task.wait(1) do
			if not player.Parent then return end
			Time += 1
			RealStats.Playtime.Value += 1			
			if Time == 60 then
				coroutine.wrap(Save)(player)
				Time = 0
			end
		end		
	end)()
end)


game.Players.PlayerRemoving:connect(function(player)
	Save(player)
end)

game:BindToClose(function()
	if game:GetService("RunService"):IsStudio() then
		task.wait(6)
	else
		local finished = Instance.new("BindableEvent")
		local allPlayers = game.Players:GetPlayers()
		local leftPlayers = #allPlayers

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

		finished.Event:Wait()
	end
end)
1 Like

If I am not mistaken, your use of a bindableEvent is not needed. The game would automatically wait for your functions to finish saving the data or close once the maximum 30 seconds is reached.

I would personally rewrite the function to look something like this.

game:BindToClose(function()
	if game:GetService("RunService"):IsStudio() then
		task.wait(6)
		return
	end
	
	local Players = game:GetService("Players"):GetPlayers()
	
	-- // This is A Faster Loop Than IPairs
	for i = 1, #Players do
		local Player = Players[i]
		
		coroutine.wrap(Save)(Player)
	end

end)




end)

Okay so from what I’ve heard, when you loop on all the players and use coroutine after the loop ends BindToClose finishes without waiting for the stats to be saved, that’s why there is a remote event there