Scripting HelpersARCHIVE
A little piece of Roblox history. Preserved for everyone, one question at a time.Read about the archive →
COMMUNITY QUESTION

My Data Save script. I've tried changing it many times and each time it doesn't save. What happened?

Here's my script but it won't work

local dataStoreService = game:GetService("DataStoreService") local dataStore = dataStoreService:GetDataStore("Leaderstats")

local dataStoreService = game:GetService("DataStoreService")
local dataStore = dataStoreService:GetDataStore("Leaderstats")

game.Players.PlayerAdded:Connect(function(plr)
	local leaderstats = Instance.new("Folder",plr)
	leaderstats.Name = "leaderstats"
	
	local level = Instance.new("IntValue", leaderstats)
	level.Name = "Level"
	level.Value = 1
	pcall(function()
		local levelValue = dataStore:GetAsync(plr.UserId.."_Level",level.Value)
		
		if levelValue == nil then
			warn (plr.UserId.. " Level Data Failed To Load or the Value wasn't saved!")
		else
			level.Value = levelValue
		end
		
	end)
end)

game.Players.PlayerRemoving:Connect(function(plr)
	local leaderstats = plr:WaitForChild("leaderstats")
	local level = leaderstats:WaitForChild("Level")
	
	pcall(function()
		dataStore:SetAsync(plr.UserId.. "_Level",level.Value)
		
	end)
end)

1 comment

0

Just do dataStore:GetAsync(plr.UserId.."_Level")

3 answers

Change your line 12 to:

local levelValue = dataStore:GetAsync(plr.UserId.."_Level")

-- You only need to put 1 argument which is the key

Also if you are testing this in studio do this for your saving line: ( Server shutdowns/closes as you stop/leave the server in studio )

local function save(plr)
    local leaderstats = plr:WaitForChild("leaderstats")
    local level = leaderstats:WaitForChild("Level")

    pcall(function()
        dataStore:SetAsync(plr.UserId.. "_Level",level.Value)
    end)
end

game.Players.PlayerRemoving:Connect(function(plr)
    save(plr)
end)

game:BindToClose(function() -- runs when game closes/shutdowns
    for _, plr in pairs(game.Players:GetPlayers()) do
        save(plr)
    end
end)

2 comments

0

Well, make sure to add 'if game:GetService('RunService'):IsStudio() then return end', in BindToClose function as not including it may result in firing both the 'BindToClose' and 'PlayerRemoving' together in studio while testing and may result in warning related to queue fill.

0

Oh yeah, thanks for the tip

it's probably because you didn't save on BindToClose

the studio test session can close before the game actually has a chance to save your data, but using BindToClose keeps the game running either until all the code that's bound to close has finished running or until 30 seconds have passed

so, instead of just using PlayerRemoving, we could try saving player data in BindToClose AND PlayerRemoving

however, saving every player's data individually in BindToClose would take much more than 30 seconds, soooo...we can cheat a liiiitle bit and just wait for PlayerRemoving to save player data while BindToClose just hangs back and doesn't really do any of the Datastorificating

local playersWaitingToSave = 0

Players.PlayerRemoving:Connect(function(plr)
	playersWaitingToSave += 1
	
	-- blablabla saving logic goes here

	playersWaitingToSave -= 1
end)

game:BindToClose(function()
	-- busy waiting like this is bad practice, but i'm lazy and don't feel like explaining the Better Way, which would take ages to explain unless you're very much not a beginner...but in that case you would probably already know how to safely and effectively save data soooo
	repeat wait(1) until playersWaitingToSave == 0
end)

you COULD use coroutines and events, or just coroutines by themselves if you have a Humongous Cerebrum, and that would allow you to save directly in BindToClose and probably be safer in the event that the server closes but the players don't actually get removed, but like i said, i don't feel like covering 56 different topics in the same answer so i'm just showing the easy way :^)

ANSWER: To prevent data loss, you should do game.Players.PlayerRemoving AND game:BindToClose(). You should also only use one argument in datastore:GetAsync(). So your whole code will be (+ loading code + save function):

local dataStoreService = game:GetService("DataStoreService")
local dataStore = dataStoreService:GetDataStore("Leaderstats")

game.Players.PlayerAdded:Connect(function(plr)
    local leaderstats = Instance.new("Folder",plr)
    leaderstats.Name = "leaderstats"

    local level = Instance.new("IntValue", leaderstats)
    level.Name = "Level"
    level.Value = 1
    pcall(function()
        local levelValue = dataStore:GetAsync(plr.UserId.."_Level")

        if levelValue == nil then
            warn (plr.UserId.. " Level Data Failed To Load or the Value wasn't saved!")
        else
            level.Value = levelValue
        end

    end)
end)

game.Players.PlayerRemoving:Connect(function(plr)
    local leaderstats = plr:WaitForChild("leaderstats")
    local level = leaderstats:WaitForChild("Level")

    pcall(function()
        dataStore:SetAsync(plr.UserId.. "_Level",level.Value)

    end)
end)

local function save(plr)
	local leaderstats = plr:WaitForChild("leaderstats")
    	local level = leaderstats:WaitForChild("Level")

    	pcall(function()
        	dataStore:SetAsync(plr.UserId.. "_Level",level.Value)
    	end)
end

game:BindToClose(function()
	for _, player in pairs(game.Players:GetPlayers()) do
		pcall(function()
			coroutine.wrap(save)(player) -- added this line to prevent data loss (elyzzia was too lazy to do this)
		end)
	end
end)

Tip/Tips: Adding wait() in game:BindToClose() does not help. It only worsens the data loss that can occur.

3 comments

0

that just makes the saving run separately from the main thread, meaning that the main thread finishes executing before anything saves, and then you just have the original problem where the server closes before anything can save again

0

that's why i said you need to use coroutines in combination with events OR a whole bunch of fancy coroutine yielding stuff

0

i'm not sure what to really do because that's what i was told on how to save properly

This discussion is preserved as it was. New answers, comments, and votes are closed.