Help with DataStores for obby game

I want to make an obby game where there are different stages. Each stage is in a different place because I want to change game settings for each one. There are buttons with the level names on the front. I want it so that a player has to finish the previous stage in order to be able to press the next button, so that they will go to the next stage. (The first button just teleports the player right away)

The problem is that I am having trouble figuring out how to transfer data between the places.

I tried following video tutorials on YouTube but none really fitted what I was trying to accomplish, so I tried scripting it on my own using various sites for certain parts of the code.

This is what I have:

Script for the spawning place:

--DataStore variables
local DataStoreService = game:GetService("DataStoreService")

local LevelsBeatenStore = DataStoreService:GetDataStore("LevelsBeatenStore")

local BeatenStore = DataStoreService:GetDataStore("BeatenStore")

local Stage = DataStoreService:GetDataStore("Stage")

game.Players.PlayerAdded:Connect(function(player)
	--Creates variables
	LevelsBeaten = {}
	local Beaten = false
	local StageNum = 1
	local data
	--Loads the table of levels beaten
	local success, errormessage = pcall(function()
		data = LevelsBeatenStore:GetAsync(player.UserId.."-LevelsBeaten")
	end)
	
	if success then
		print("Data successfully loaded")
		LevelsBeaten = data
		print(LevelsBeaten)
	else
		print("There was an error loading data")
		warn(errormessage)
	end
	--Loads if they beat the stage or not
	local success, errormessage = pcall(function()
		data = BeatenStore:GetAsync(player.UserId.."-Beaten")
	end)

	if success then
		print("Data successfully loaded")
		Beaten = data
		print(Beaten)
	else
		print("There was an error loading data")
		warn(errormessage)
	end
	--Loads the stage number
	local success, errormessage = pcall(function()
		data = Stage:GetAsync(player.UserId.."-Stage")
	end)

	if success then
		print("Data successfully loaded")
		StageNum = data
		print(StageNum)
	else
		print("There was an error loading data")
		warn(errormessage)
	end
	
	if LevelsBeaten == nil then
		LevelsBeaten = {Beaten}
	else
		table.insert(LevelsBeaten,StageNum,Beaten)
	end
	
	print(LevelsBeaten)

end)

game.Players.PlayerRemoving:Connect(function(player)
	local success, errormessage = pcall(function()
		LevelsBeatenStore:SetAsync(player.UserId.."-LevelsBeaten",LevelsBeaten)
	end)
	
	if success then
		print("Data successfully saved")
	else
		print("There was an error saving data")
		warn(errormessage)
	end
	
	local success, errormessage = pcall(function()
		LevelsBeatenStore:SetAsync(player.UserId.."-Beaten",BeatenStore)
	end)

	if success then
		print("Data successfully saved")
	else
		print("There was an error saving data")
		warn(errormessage)
	end
	
	local success, errormessage = pcall(function()
		LevelsBeatenStore:SetAsync(player.UserId.."-Stage",Stage)
	end)

	if success then
		print("Data successfully saved")
	else
		print("There was an error saving data")
		warn(errormessage)
	end
end)

game.ReplicatedStorage.Event.Event:Connect(function()
	game.ReplicatedStorage.LevelBeaten:Fire(LevelsBeaten)
end)

Script for the button:

script.Parent.ClickDetector.MouseClick:Connect(function(player)
	game.ReplicatedStorage.Event:Fire()
	game.ReplicatedStorage.LevelBeaten.Event:Connect(function(LevelsBeaten)
		if LevelsBeaten[1] == true then
			game:GetService("TeleportService"):Teleport(5882129816, player)
		else
			print("Has not beaten the previous level")
		end
	end)
end)

Script for the first stage:

local DataStoreService = game:GetService("DataStoreService")

local BeatenStore = DataStoreService:GetDataStore("BeatenStore")

local Stage = DataStoreService:GetDataStore("Stage")

game.Players.PlayerAdded:Connect(function(player)
	Beaten = game.ReplicatedStorage.Beaten
	StageNum = game.ReplicatedStorage.Stage
	local data
	local success, errormessage = pcall(function()
		data = BeatenStore:GetAsync(player.UserId.."-Beaten")
	end)
	
	if success then
		print("Data successfully loaded")
		Beaten = data
	else
		print("There was an error loading data")
		warn(errormessage)
	end
end)

game.Players.PlayerRemoving:Connect(function(player)
	if Beaten.Value == false then
		local success, errormessage = pcall(function()
			BeatenStore:SetAsync(player.UserId.."-Beaten",Beaten.Value)
		end)

		if success then
			print("Data successfully saved")
		else
			print("There was an error saving data")
			warn(errormessage)
		end
	end
	
	local success, errormessage = pcall(function()
		Stage:SetAsync(player.UserId.."-Stage",StageNum.Value)
	end)

	if success then
		print("Data successfully saved")
	else
		print("There was an error saving data")
		warn(errormessage)
	end
end)

It’s a little long but that’s mainly because I don’t really know what I’m doing and most of this is new to me. (Things I don’t understand too well: DataStores, RemoteFunctions, and Tables)

1 Like

Have you checked the out put to see if there are any errors?

You can try this script! I hope it helps :slight_smile:

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


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)

Also, put it in server script service and name it SaveingScript @Pusheen27Cat2

1 Like

Please correct me if I’m wrong, but DataStores are supposed to persist between different places in a game by default:

1 Like

Yes, what I am getting right now is this: 18:54:22.494 - ServerScriptService.Save:59: invalid argument #2 to ‘insert’ (number expected, got nil)

Okay let me try and see if it helps.

1 Like

Try clicking on that error to locate it’s source. The error is saying that the argument you passed in (argument #2) was nil, when it expected a number.

1 Like

I’m still just confused on how it all works, so I can adjust the code in the future if I need to.

Yes, I’m not sure why the value is nil when the StageNum is supposed to be 1.

Are there any new errors in the out put?

This is the only error that there is.

Have you clicked on the red error?

Try to put this script in workspace to count the stages, it might need the same scripts I have.

local Stages = workspace:WaitForChild("Stages")

for i,Stage in pairs(Stages:GetChildren()) do
		
	Stage.Touched:Connect(function(touch)
			
		local humanoid
		
		if touch.Parent:FindFirstChild("Humanoid") then
			
			humanoid = touch.Parent.Humanoid
		end
		
		if touch.Parent and touch.Parent.Parent:FindFirstChild("Humanoid") then
			
			humanoid = touch.Parent.Parent.Humanoid
		end
		
		if humanoid then
			
			local player = game.Players:GetPlayerFromCharacter(humanoid.Parent)
			
			local PlayerStage = player.leaderstats.Stage.Value
			
			if tonumber(Stage.Name) == PlayerStage + 1 then
				
				player.leaderstats.Stage.Value = player.leaderstats.Stage.Value + 1
			
			elseif tonumber(Stage.Name) > PlayerStage + 1 then
				
				humanoid.Health = 0
			end
		end
	end)
end

@Pusheen27Cat2

Yes, it takes me to when I try to add whether the player has beaten the stage or not to the table.

StageNum will be nil if the data is nil. To prevent this error from occurring, you could first check to see if StageNum is not nil:

local StageNum
local success, result = pcall(function()
    StageNum = Stage:GetAsync(player.UserId .. "-Stage")
end
if success then
    if StageNum then -- check to see if the data is not nil
        -- rest of your code
    end
else
    warn(result)
end

Oh, try publishing the game and entering it, then get to stage 5 or so, then rejoin!

Thanks, but the issue is that each stage is in another place because they will be large and have different game settings.

@Pusheen27Cat2 can you provide a picture or video (if possible) or can you send me the link to the game so I can play it and see if the thing is working or not?

Oh, okay. So now it just warns me that the data is nil, which it shouldn’t be.

Sure, Zenith Lands (Alpha) - Roblox