DataStoring Help

Greetings developers, I’m Dani. My issue is I don’t really know how to DataStore, so if anyone knows what’s wrong with my script, it’d be nice if you could tell me… (lol). The main issue that I believe is saving the values (the PlayerRemoving portion of the script or maybe the SecData part). So to explain the script I’m taking items out of a folder and “converting” them into a bool value inside the player (the bool values being the child of a instance folder mentioned in the script called “PlayerOwn”) , throughout other scripts I’m simply setting a few values to true (dependent on the player and choices etc) but when the player rejoins the values don’t save, so my guess is that this saving script is the apparent reason to my problem (I also tested and 99% sure saving the data is the issue here) so if anyone notices any possible causes for the issue with saving please let me know!

local DataStoreService = game:GetService("DataStoreService")
local SecondDataStore = DataStoreService:GetDataStore("PlayerOwn")
local srvrss = game:GetService("ServerScriptService")
local ss = game:GetService("ServerStorage")

game.Players.PlayerAdded:Connect(function(Player)
	local PlayerOwn = Instance.new("Folder")
	PlayerOwn.Name = "PlayerOwn"
	PlayerOwn.Parent = Player
	
	for i, v in pairs(ss:WaitForChild("Props"):WaitForChild("Shop"):GetChildren()) do
		if v.ClassName == "Tool" then
			local new = Instance.new("BoolValue")
			new.Name = v.Name
			new.Value = false
			new.Parent = PlayerOwn
			print(new.Value)
		end
	end
	
	local SecData
	pcall(function()
		SecData = SecondDataStore:GetAsync(Player.UserId)
	end)
	if type(SecData) ~= "table" then
		SecData = nil
	end
	if SecData then
		for i, v in pairs(Player:WaitForChild("PlayerOwn"):GetChildren()) do
			if v.ClassName == "BoolValue" then
				v.Value = SecData[v]
			end
		end
	end
end)

game.Players.PlayerRemoving:Connect(function(Player)
	for i, v in pairs(Player:WaitForChild("PlayerOwn"):GetChildren()) do
		if v.ClassName == "BoolValue" then
			SecondDataStore:SetAsync(Player.UserId, {
				[v.Name] = Player.PlayerOwn[v].Value
			})
		end
	end
end)

so there are a few problems you have here

  1. your calling SecondDataStore:SetAsync multiple times with the same key and overwriting the previous saved data

  2. datastore has limits to how often you can write data and your calling SetAsync to often read here to see the limits Data Stores

  3. when the last player leaves the game the server starts to shutdown and it does not wait for you to save data into the datastore so you must use DataModel:BindToClose to stop the server from shutting down to give you enough time to save to the datastore

here is some code i made to help someone else and it covers almost everything i listed above so you can maybe use it to help you

local playerLoaded = {}

game.Players.PlayerAdded:Connect(function(player)
    -- when the player enters the game try to load there data
    local success, value = pcall(dataStore.GetAsync, dataStore, player.UserId)
    if success == false then return end
   
    -- if the value = nil then set value to a empty table
    value = value or {}

    -- clone the data folder inside this script with all its values
    local folder = script.Data:Clone()

    -- loop all values and set them to match what was last saved in the datastore
    for i, child in ipairs(folder) do child.Value = value[child.Name] or child.Value end

    -- place the folder into the player
    folder.Parent = player

    -- set the playerloaded to true so we can keep track if there are players loaded so that bind to close can wait
    playerLoaded[player] = true
end)

game.Players.PlayerRemoving:Connect(function(Player)
    -- if player loaded == nil meaning the player failed to load when they entered we simple exit because we dont want to overwrite the old data
    if playerLoaded[player] == nil then return end
 
    -- create a table and set the the values from there data folder
    local playerData = {}
    for i, child in ipairs(player.Data:GetChildren()) do
        playerData[child.Name] = child.Value
    end

    -- now save this table to the datastore
    local success, value = pcall(dataStore.SetAsync, dataStore, player.UserId, playerData)

    -- now set playerLoaded to nil so that bindtoclose can stop looping and allow the server to close
    playerLoaded[player] = nil
end)

-- while players are loaded dont close the server (only works upto 30 seconds)
game:BindToClose(function()
    while next(playerLoaded) ~= nil do
        task.wait()
    end
end)