Checkpoint System Bug

Hello!

I’m trying to make a simple obby that saves your stage progress for you to resume when you come back. I am using the DataStoreService for this, and with it, I load the player’s stage data. Then, I use that data to set the position of their HumanoidRootPart to the position of their stage checkpoint (plus a couple studs on the Y axis, of course).

The saving works fine.

The positioning, on the other hand, not so much. I’d say that this fails around 30% of the time.

On a normal occasion, I would spawn in and be positioned at my stage checkpoint where I left off from the last time I played the game. The leaderboard always shows the correct stage value as well.

However, 30% of the time, I spawn at the SpawnLocation, or the default spawn for new players that join the game. This doesn’t tamper with the data, which is a good sign. All I need to do to be correctly positioned back at my saved stage is to reset my character. I don’t want to do that, though, because how would players know to reset if they spawn back at the start? They’d probably freak out about their data not loading, and there’s no way I’m putting a sign or GUI that tells them to reset their character.

Anyways, I’m sure the issue is somewhere in the server script that saves/loads the player’s stage data and positions their character. I’ve narrowed it down to the following snippet of code from that script:

player.CharacterAdded:Connect(function(character)
		
	local humanoid,hrp = character:WaitForChild("Humanoid"),character:WaitForChild("HumanoidRootPart")
		
	wait() -- I've tried messing with this line, but no luck.
		
	if humanoid and hrp then
			
		if stage.Value ~= 0 then
				
			local part = workspace.ObbyStages:FindFirstChild(stage.Value)
			hrp.CFrame = part.CFrame + Vector3.new(0,1,0)
				
		end
			
	end
		
end)
Full Script
local players = game:GetService("Players")
local DataStoreService = game:GetService("DataStoreService")
local saveDataStore = DataStoreService:GetDataStore("ClassicModeDataStore")

local function savePlayerData(player)
	
	local success,errormessage = pcall (function()
	
		local saveData = {}
		
		for _,stat in pairs(player.leaderstats:GetChildren()) do
			
			saveData[stat.Name] = stat.Value
			
		end
		
		saveDataStore:SetAsync(player.UserId,saveData)
		
	end)	
	
	if not success then return errormessage end
	
end

players.PlayerAdded:Connect(function(player)
	
	local stats = Instance.new("Folder")
	stats.Name = "leaderstats"
	stats.Parent = player
	
	local stage = Instance.new("IntValue")
	stage.Name = "Stage"
	stage.Parent = stats
	
	local data = saveDataStore:GetAsync(player.UserId)
	
	if data then
		
		print(player.Name.. " has joined the game. They have spawned at stage "..data.Stage..".")
		
		for _,stat in pairs(stats:GetChildren()) do
			
			stat.Value = data[stat.Name]
			
		end
		
	else
		
		print("A new visitor, "..player.Name..", has joined the game! They do not have any data to load.")	
		
	end
	
	player.CharacterAdded:Connect(function(character)
		
		local humanoid,hrp = character:WaitForChild("Humanoid"),character:WaitForChild("HumanoidRootPart")
		
		wait()
		
		if humanoid and hrp then
			
			if stage.Value ~= 0 then
				
				local part = workspace.ObbyStages:FindFirstChild(stage.Value)
				hrp.CFrame = part.CFrame + Vector3.new(0,1,0)
				
			end
			
		end
		
	end)
	
end)

players.PlayerRemoving:Connect(function(player)
	
	local err = savePlayerData(player)
	
	if err then print(err) end
	
end)

game:BindToClose(function()
	
	for _,player in pairs(players:GetPlayers()) do
		
		local err = savePlayerData(player)
		
		if err then print(err) end
		
	end
	
	wait(2)
	
end)

I’ve viewed a couple of topics on the DevForum to try and solve my problem, but none of the posters had an issue that happened inconsistently. I hope you guys can give me some pointers or possible solutions, as I have observed this issue for quite a while now.

Thanks!

3 Likes

You may be missing when the player/ character is added to the game.
I suggest making the player added function a separate function, and moving the character added to the very top, perhaps make that a separate function too.

Reason you need separate functions is you have to get all players and/or characters that might have loaded in before the script got to that point

You can do it by doing it like this:

function characterAdded(character)
--code here
end

function playerAdded(player)
    player.CharacterAdded:Connect(characterAdded)
   --code here
end
for i,player in ipairs(game:GetService("Players"):GetPlayers()) do
    playerAdded(player)
end
game:GetService("Players").PlayerAdded:Connect(playerAdded)

Of course you should also get all characters, that may have loaded in before the execution of the code, but I’ve never had an issue where this isn’t enough.

To be on the safe side, in the playerAdded function you can do:

local character = player.Character
if character then
  characterAdded(character)
end
player.CharacterAdded:Connect(characterAdded)

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.