Obby Autosave error

Issues with Autosave script for an Obby

I got the Autosave + Stage script through a tutorial from 2 different Youtubers I feel like there are a few properties i should change to make it work?

---Stage Script----

local STAT_NAME = "Stage"
local PREVENT_SKIPPING = true

local checkpoints = {}

local i = 1
while true do
	local checkpoint = Workspace:FindFirstChild("Checkpoint " .. i, true)
	if not checkpoint then print("Last Checkpoint : " .. i-1) break end
	table.insert(checkpoints, checkpoint)
	i = i + 1
end

game.Players.PlayerAdded:connect(function(player)
	local leaderstats = player:FindFirstChild("leaderstats") or Instance.new("Model", player)
	leaderstats.Name = "leaderstats"
	
	local checkpointStat = Instance.new("IntValue", leaderstats)
	checkpointStat.Name = STAT_NAME
	checkpointStat.Value = 1
	
	player.CharacterAdded:connect(function(character)
		local goto = checkpoints[checkpointStat.Value]
		if goto then
			repeat wait() until character.Parent
			character:MoveTo(goto.Position)
		else
			warn("Checkpoint " .. checkpointStat.Value .. " not found")
		end
	end)
end)

for index, checkpoint in ipairs(checkpoints) do
	checkpoint.Touched:connect(function(hit)
		local player = game.Players:GetPlayerFromCharacter(hit.Parent)
		if not player then return end
		local humanoid = hit.Parent:FindFirstChild("Humanoid")
		if not humanoid or humanoid.Health <= 0 then return end
		local leaderstats = player:FindFirstChild("leaderstats")
		if not leaderstats then return end
		local checkpointStat = leaderstats:FindFirstChild(STAT_NAME)
		if not leaderstats then return end
		
		if (PREVENT_SKIPPING and checkpointStat.Value + 1 == index) or (not PREVENT_SKIPPING and checkpointStat.Value < index) then
			checkpointStat.Value = index
		end
	end)
end```


-----AutoSave Script------


local Players = game:GetService("Players")
local DataStoreService = game:GetService("DataStoreService")
local SaveDataStore = DataStoreService:GetDataStore("SaveData")


local function SavePlayerData(player)
 
 local success,errormsg = pcall(function()
 
  local SaveData = {}
  
  for i,stats in pairs(player.leaderstats:GetChildren()) do
   
   SaveData[stats.Name] = stats.Value
  end 
  SaveDataStore:SetAsync(player.UserId,SaveData)
 end)
 
 if not success then 
  return errormsg
 end   
end 


Players.PlayerAdded:Connect(function(player)
 
 local Stats = Instance.new("Folder")
 Stats.Name = "leaderstats"
 Stats.Parent = player
 
 local Stage = Instance.new("StringValue")
 Stage.Name = "Stage"
 Stage.Parent = Stats
 Stage.Value = 1
 
 local Data = SaveDataStore:GetAsync(player.UserId)
 
 if Data then
   
  print(Data.Stage)
  
  for i,stats in pairs(Stats:GetChildren()) do
   
   stats.Value = Data[stats.Name]  
  end   
 else  
  print(player.Name .. " has no data.")   
 end
 
 
 player.CharacterAdded:Connect(function(character)
  
  local Humanoid = character:WaitForChild("Humanoid")
  local Torso = character:WaitForChild("HumanoidRootPart")
  
  wait()
  
  if Torso and Humanoid then
   if Stage.Value ~= 0 then
    
    local StagePart = workspace.Stages:FindFirstChild(Stage.Value)
    Torso.CFrame = StagePart.CFrame + Vector3.new(0,1,0)     
   end 
  end 
 end)  
end)


Players.PlayerRemoving:Connect(function(player)
 
 local errormsg = SavePlayerData(player)
 
 if errormsg then 
  warn(errormsg)  
 end 
end)

game:BindToClose(function()
 for i,player in pairs(Players:GetPlayers()) do 
  
  local errormsg = SavePlayerData(player)
  if errormsg then
   warn(errormsg)
  end   
 end
 wait(2) 
end)

This is the error I’m getting in the output:

19:30:43.913 - 502: API Services rejected request with error. HTTP 403 (Forbidden)
19:30:43.915 - Stack Begin
19:30:43.916 - Script ‘ServerScriptService.Save’, Line 36
19:30:43.916 - Stack End

this is the line 36: local Data = SaveDataStore:GetAsync(player.UserId)

Just a question. Are you testing this in studio? If so, do you have studio access to API services enabled?

I was testing it in studio yes, And I didn’t have it enabled I’ve googled on how to enable it. I’m not getting that error anymore but It still doesn’t autosave. I also have another problem now that If I die I’ll respawn all the way at the start even though the stages seems to work correctly

output:
19:58:19.165 - ServerScriptService.Save:62: attempt to index nil with ‘CFrame’

19:58:19.167 - Stack Begin

19:58:19.167 - Script ‘ServerScriptService.Save’, Line 62

19:58:19.168 - Stack End

In the stage script, try putting the while loop under everything else. I dont think anything under a while loop will run.

Edit: nvm i didn’t see the break

Might help

image

the output error leads me to
local StagePart = workspace.Checkpoints:FindFirstChild(Stage.Value)
Torso.CFrame = StagePart.CFrame + Vector3.new(0,1,0)
end
end
end)
end)

Hey! So… Yeah, you’ll get obviously to the start because you need to add this line of code under the CharacterAdded event:

repeat wait() until character~=nil -- defining the parameter of the CharacterAdded event as character, this will work

And for the other maybe you’ll like my DataStore that I put on my Guide for Begginers on how to create an Obby (isn’t public right now).

DataStore:

--[[
-- Copyright (c) 2020
-- All rights reserved to Lil_SharkyBoy
---------------------------------------------------
-- Author: Claudio
-- Username: Lil_SharkyBoy
-- Written: 04 / 21 / 2020

-- Description
---------------------------------------------------
The following DS is just for example and isn't for anything else. The purpose if this one is to teach the people how to actually
script a Obby on ROBLOX and start making their own very first games on the Platform.

Also, this DataStore is handwriten and all from scratch by me. There's some parts that I had to make a research for, but obviously I'm not perfect or the best Programmer on ROBLOX, right?

Nah, just kidding I just wanted to use this template for source control, lel.
--]]

--[[ Variables ]]--
local dss = game:GetService("DataStoreService") -- gets the DataStoreService
local dsSaved = dss:GetDataStore("StagesData") -- get the datastore from our already variables existing DataStoreService

--[[ Main script ]]--
game:GetService("Players").PlayerAdded:Connect(function(plr) -- gets the player
	
	local folder = Instance.new("Folder", plr) -- creates a folder on the player object
	folder.Name = "leaderstats" -- this makes it shows as a leaderboard, if you don't want to show it then just change the name
	
	local stageValue = Instance.new("IntValue", folder) -- creates an intvalue on the folder
	stageValue.Name = "Stage" -- change the name if you wish to
	
	local dataStored = dsSaved:GetAsync(plr.UserId)
	
	if dataStored ~= nil and dataStored then
		
		-- this will run if there's data already 
		print( "Loading data... Data loaded! Your actual stage is: " .. (dataStored.Stage) )
		
		-- basically, the "GetChildren()" just give an array containing all the instance's direct children 
		for _, stat in pairs(folder:GetChildren()) do
			
			-- changes the value, to the data stored one indexing by the name of the stat
			stat.Value = dataStored[stat.Name]
			
		end
		
	elseif not dataStored and dataStored == nil then
			
		-- this will run if there's no data
		print( "Player: " .. (plr.Name) .. " has no data! Creating a new one..." )
		
	end
	
	--[[ Character teleportation / When the player joins, this will verify if there's data and teleport the player to his last stage ]]--
	plr.CharacterAdded:Connect(function(chr) -- gets the character
		
		-- stops when the chacter isn't nil
		repeat wait() until chr ~= nil 
		
		local hrp = chr.HumanoidRootPart -- we get the humanoidrootpart
		
		-- verifies if the folder exist, the stageValue.Value isn't nil or false and the hrp is already detected or exist so it can run
		if folder ~= nil and stageValue.Value ~= nil and hrp then 
			
			-- finds the stage we are on
			local checkpoints = workspace.Checkpoints:FindFirstChild(tostring(stageValue.Value)) 
			
			-- teleport the player to the actual checkpoint
			hrp.CFrame = CFrame.new(checkpoints.Position + Vector3.new(0, 1, 0)) -- the vector3 is for teleport the player a little bit more far on the y axis, so it's also not colliding with the part and not getting stuck
			
		end
		
	end)
	
	
end)

--[[ Saving Data when the player leaves ]]--
local function saveDataForThePlayer(plr)
	
	local scope = plr.UserId
	
	-- creates a table / array
	local data = {}
	
	-- this is really common on the datastores tbh
	local s, e = pcall(function()
	
		-- pass through all the values, folder or anything that's on the "leaderstats" folder, indexing first the key and then the value in this case "_" is the key and "stat" is the value
		for _, stat in pairs(plr:WaitForChild("leaderstats"):GetChildren()) do
			
			-- it gets the stat from it names indexing this key on the table and the give the stat the right value
			data[stat.Name] = stat.Value
			
		end
		
		-- and we just made a queue for the ROBLOX API to stores our data of our player
		dsSaved:SetAsync(scope, data)
		
	end)
	
	-- this is for verifying if it was succesful or there was an error and return the error
	if not s and e then return e end
	
end

game:GetService("Players").PlayerRemoving:Connect(function(plr)
	
	-- creates a variable for our actual function to save player's data
	local s = saveDataForThePlayer(plr)
	
	-- this can get us the error reason by printint it
	if s then print(s) end
	
end)

--[[ To be honest, I didn't never used this ]]--
game:BindToClose(function()
	
	-- gets every player on the server, 1 by 1
	for _, plr in pairs(game:GetService("Players"):GetPlayers()) do
		
		-- i already explained this line of code
		local err = saveDataForThePlayer(plr)
		
		-- i already explained this line of code
		if err then print(err) end
		
	end
	
end)

Edit: Also, I have a script more compacted to our Checkpoints! And here it is:

--[[ Variables ]]--
local cpFolder = workspace:FindFirstChild("Checkpoints")

--[[ Main script ]]--
for _, v in pairs(cpFolder:GetChildren()) do -- pass trough the checkpoints
	
	-- check if the v, is a part 
	if v:IsA("BasePart") then
		
		-- the event gets fired everytime we touch a checkpoint
		v.Touched:Connect(function(hit)
			
			-- verfies if it isn't nil and checks that the touching part is a Character
			if hit.Parent ~= nil and hit.Parent:WaitForChild("Humanoid") then
				
				-- get the character from the player
				local player = game:GetService("Players"):GetPlayerFromCharacter(hit.Parent)
				
				-- verifies if the value of the Stage is less than the stage he's touching
				if player:WaitForChild("leaderstats").Stage.Value < tonumber(v.Name) then
					
					-- set Stage.Value to the name (in number) of our checkpoint we are touching
					player:WaitForChild("leaderstats").Stage.Value = tonumber(v.Name)
					
				else
						
					-- prints "You are already on this stage!" if the player already has that stage completed 
					print("You are already on this stage!")
					
				end
				
			end
			
		end)
		
	end
	
end

I hope this helps you!

3 Likes

I noticed that in the while loop you do Workspace:FindFirstChild("Checkpoint " .. i)

This should be Workspace.Checkpoints:FindFirstChild("Checkpoint" .. i)

Thank you for the help, Somehow got it to work with both your advice!

No. This code sample won’t do anything. If character was nil when the loop started, it’ll loop forever since you’re never updating the variable. If character was already defined when the loop started, there’s no point to having the loop — it’ll just skip right past it.

2 Likes

Not gonna lie, this works for me. :man_shrugging:

why would you need to wait for the character in the characteradded event anyways?

That’s because character will always already be defined, so it’s equivalent to just writing wait(). (repeat loops will always run one iteration even if the condition is false.)

2 Likes

This makes the character goes into the stage you are (like if you’re on the stage 4 then you’ll be teleported to that when you die).

Thanks for the recommendation @posatta!

1 Like

Hello I am French and I have a similar concern if you want to help me add discord Donoozeus#8529

1 Like