Issue using datastore to save checkpoint leaderstat

hello! I’m trying to learn DataStore and implement it into my obby test. However, it’s not working as intended.

local PlayerInventory = DataStoreService:GetDataStore("PlayerInventory")
local PlayerCheckpoint = DataStoreService:GetDataStore("PlayerInventory", "Checkpoint")

game.Players.PlayerAdded:Connect(function(player)
	
	local leaderstats = Instance.new("Folder")
	leaderstats.Parent = player
	leaderstats.Name = "leaderstats"
	
	local Checkpoint = Instance.new("IntValue")
	Checkpoint.Parent = leaderstats
	Checkpoint.Value = 0
	Checkpoint.Name = "Checkpoint"
	
	local success, currentCheckpoint = pcall(function() 
		Checkpoint.Value = PlayerCheckpoint:GetAsync(player.UserId)
	end)
	if success then
		Checkpoint.Value = currentCheckpoint
	else
		Checkpoint.Value = 0
	end
	
	local checkpoints = workspace.Obby.Checkpoints
	
	for checkpoint = 1, #checkpoints:GetChildren() do
		checkpoints[checkpoint].Touched:Connect(function(touch)
			Checkpoint.Value = checkpoint
		end)
	end
	
	game.Players.PlayerRemoving:Connect(function(player)
		local success, updatedCheckpoint = pcall(function()
			return PlayerCheckpoint:SetAsync(player.UserId, Checkpoint.Value)
		end)
		if success then
			Checkpoint.Value = updatedCheckpoint
		end
	end)
end)

The datastore part of this script is supposed to

  1. Load the checkpoint leaderstat of someone who joins the game
  2. Save their data when the player leaves

Sorry if there’s a simple solution I haven’t tried. again, this is my first time using datastore and I don’t understand everything

Could you explain what about it isn’t working? Are there any output messages, errors, etc.?

From a quick glance you should probably change the PlayerCheckpoint variable.

local PlayerCheckpoint = DataStoreService:GetDataStore("Checkpoint")

You should probably put the “playerremoving” function ouside the the “playeradded” function. Also the player might not safe because the server closes before playerremoving gets called so add a “game:BindToClose()” to your script and save when the server closes or you could just use a “task.wait(3)” so “playerremoved” before the server closes.

I guess the problem is all related to the PlayerRemoving event. Basically you’re not using the SetAsync correctly.

The problem with this is that you’re not assigning the new data to their ‘Checkpoint’ datastore. When you do “Checkpoint.Value”, you’re just adding a new value to their server leaderstats, BUT not saving it!, you should do:

game.Players.PlayerRemoving:Connect(function(player)
		local success, Checkpoints  = pcall(function()
			return PlayerCheckpoint:GetAsync(player.UserId)
		end)
         if sucess then
            -- 'Checkpoints' in the pcall will be nil If the player doesn't have data.
            local PlayerCurrentCheckpoint = player.leaderstats:FindFirstChild("Checkpoint").Value
            PlayerCheckpoint:SetAsync(player.UserId, PlayerCurrentCheckpoint)
end
end)

And also, as @aron01224 said, put the PlayerRemoving event outside the PlayerAdded. By the way, I recommend to you simplify your script; Starting from the Touched event could be in another script.

And If possible, save the Checkpoint.Value to the Checkpoint data instantly when their data in the leaderstats changed. This means that it’s not necessary to save the player data when he left the game with the ‘PlayerRemoving’, because their Checkpoint will be instantly save at the moment of getting a new.

And, to load their data when he join to the game, you should do:

local success, PlayerCurrentCheckpointSaved = pcall(function() 
		return PlayerCheckpoint:GetAsync(player.UserId)
	end)

	if PlayerCurrentCheckpointSaved == nil then
        warn("The data of this player is empty. Happens if the player join to the game first time.")
		Checkpoint.Value = 0
	else
		Checkpoint.Value = PlayerCurrentCheckpointSaved
	end

Remember to put this in the PlayerAdded event and after when you create all related to the leaderstats folder.

i can’t believe I forgot that part. There’s nothing in the output, even when I step onto another checkpoint to update the character’s leaderstat. Then when I rejoin, I start at spawn.

hello! thanks for the insight into my problem. i’ve updated the script yet it isn’t saving data whenever I leave.

local PlayerCheckpoint = DataStoreService:GetDataStore("Checkpoint")

game.Players.PlayerAdded:Connect(function(player)
	
	local leaderstats = Instance.new("Folder")
	leaderstats.Parent = player
	leaderstats.Name = "leaderstats"
	
	local Checkpoint = Instance.new("IntValue")
	Checkpoint.Parent = leaderstats
	Checkpoint.Value = 0
	Checkpoint.Name = "Checkpoint"
	
	local checkpoints = workspace.Obby.Checkpoints
	
	for checkpoint = 1, #checkpoints:GetChildren() do
		checkpoints[checkpoint].Touched:Connect(function(touch)
			Checkpoint.Value = checkpoint
		end)
	end
	
	local success, PlayerCurrentCheckpointSaved = pcall(function() 
		return PlayerCheckpoint:GetAsync(player.UserId)
	end)

	if PlayerCurrentCheckpointSaved == nil then
		warn("The data of this player is empty. Happens if the player join to the game first time.")
		Checkpoint.Value = 0
	else
		Checkpoint.Value = PlayerCurrentCheckpointSaved
	end
end)

game.Players.PlayerRemoving:Connect(function(player)
	local success, Checkpoints  = pcall(function()
		return PlayerCheckpoint:GetAsync(player.UserId)
	end)
	if success then
		local PlayerCurrentCheckpoint = player.leaderstats:FindFirstChild("Checkpoint").Value
		PlayerCheckpoint:SetAsync(player.UserId, PlayerCurrentCheckpoint)
		print("Data saved before leaving")
	end
end)

Here’s what the script currently looks like. I put a print statement in the PlayerRemoving event to ensure that data is being saved when the player leaves, but that doesn’t print. The warning message in the PlayerAdded event does print, though.

Try adding a BindToClose() function to the code.

game:BindToClose(function()
    for _, player in pairs(game.Players:GetPlayers()) do
        --save the data, maybe try making a function to do so in order to be more efficient.
    end
end)

Maybe try changing your function names in order to be more efficient.

local function loadData(player)
    --loading code here
end

local function saveData(player)
    --saving code here
end

game.Players.PlayerAdded:Connect(loadData)
game.Players.PlayerAdded:Connect(saveData)

game:BindToClose(function()
    if game["Run Service"]:IsStudio() then task.wait(5) return end --PlayerRemoving will fire in studio testing
    for _, player in pairs(game.Players:GetPlayers()) do --save every player's data
        saveData(player)
    end
end)

The BindToClose() function will run any provided code before the game shuts down.

The problem could simply be that the game shuts down before it has time to save the data, which this should fix.

It also might be worth adding an autosave at the bottom of your loading code, just as a backup.

while task.wait(300) do --autosave every 5 minutes
    save(player) --where 'player' is the Player object provided by PlayerAdded
end

Hello, do this.

local DataStoreService = game:GetService("DataStoreService")
local PlayerCheckpoint = DataStoreService:GetDataStore("Checkpoint")
local PlayersService = game:GetService("Players")
local ObbyCheckpoints = game:GetService("Workspace").Obby.Checkpoints

PlayerService.PlayerAdded:Connect(function(Player)
    local leaderstats = Instance.new("Folder")
	leaderstats.Parent = player
	leaderstats.Name = "leaderstats"
	
	local Checkpoint = Instance.new("IntValue")
	Checkpoint.Parent = leaderstats
	Checkpoint.Name = "Checkpoint"

local suc, PlayerCurrentCheckpoint = pcall(function()
return PlayerCheckpoint:GetAsync(Player.UserId)
end)

if PlayerCurrentCheckpoint ~= 0 then
Checkpoint.Value = PlayerCurrentCheckpoint
print("Player checkpoints loaded: ", Checkpoint.Value)
else -- "Nil" or 0 as default.
Checkpoint.Value = 0 
end
end)

for i, Checkpoints in pairs(ObbyCheckpoints:GetChildren()) do
      Checkpoints.Touched:Connect(function(touch)
			local Player = PlayersService:GetPlayerFromCharacter(touch.Parent)
            local Checkpoint = Player.leaderstats:FindFirstChild("Checkpoint")
            Checkpoint.Value += 1
            PlayerCheckpoint:SetAsync(Player.UserId, Checkpoint.Value)
            print("Saving player checkpoints!")
		end)
	end

This allow you to load the player when PlayerAdded. And also, If the player get a new checkpoint, their data will be automatically save in the Checkpoints datastore. This allows you to avoid having to do the PlayerRemoving event.