DataStore Service not saving properly

Hello, I’m relatively new to scripting and just joined the DevForum. I’m making a data store service that will save the player’s stage and rebirth values. My data saved when I reached the 2nd stage, but my problem is that the data saved only once and everytime I rejoin the game the stage value stays at the same value even when I reached other stages. I don’t know how to approach this so any help would be appreciated.
*API Services are enabled
Here’s the warn message: Unable to cast to Array - Server - Leaderstats:77

Here’s my script:

game.Players.PlayerAdded:Connect(function(plr)
	local DataStoreService = game:GetService("DataStoreService")
	local myData = DataStoreService:GetDataStore("myData")
	local success, errormsg = pcall(function()
		local playerData = myDataStore:GetAsync(plr.UserId)
		if plr.leaderstats.Stage.Value ~= 0 then 
		
			plr.leaderstats.Stage.Value = playerData
			plr.leaderstats.Rebirths.Value = playerData
			print(plr.Name.."'s data was received from the data store")
		else
			print(plr.Name.."'s has no previously saved data")
			plr.leaderstats.Stage.Value = 1
		end
	end)
	if errormsg then 
		warn(errormsg)
		end
	end)

game.Players.PlayerRemoving:Connect(function(plr)
	local success, errormsg = pcall(function()
		myDataStore:SetAsync(plr.UserId, plr.leaderstats.Stage.Value, plr.leaderstats.Rebirths.Value)
	end)
	if success then 
		print("Successfully saved"..plr.Name.."'s data.")
	else
		warn(errormsg)
	end
end)

Well for starters, I think SetAsync only accepts two arguments, and GetAsync only accepts one. You used GetAsync properly, but it looks like you passed three arguments into SetAsync. Try starting off by storing the Stage value and Rebirth value into a table/dictionary, and then pass that table/dictionary as the second argument. In other words, instead of:

myDataStore:SetAsync(plr.UserId, plr.leaderstats.Stage.Value, plr.leaderstats.Rebirths.Value)

it would be

myDataStore:SetAsync(plr.UserId, dataTable)

Also if possible, may we see the full script? I’m not sure which line would be line 77, and knowing that would help with identifying the error. Also I see “myDataStore” but don’t see where it’s initialized and it’s possible for that to be part of the problem too

I generally use scopes for these. For example to store cash data I use below:
DataStoreService:GetDataStore(“MyGamesDataStore”, “CashScope”)

Take a look at this documentation, you will understand better. Data Stores | Documentation - Roblox Creator Hub

local Checkpoints = game.Workspace.Checkpoints
local DataStoreService = game:GetService("DataStoreService")
local myDataStore = DataStoreService:GetDataStore("myData")

--leaderboard

game.Players.PlayerAdded:Connect(function(plr)
	
	local leaderstats = Instance.new("Folder")
	leaderstats.Name = "leaderstats"
	leaderstats.Parent = plr
	
	local Stage = Instance.new ("IntValue", leaderstats)
	Stage.Name = "Stage"
	Stage.Value = 1
	Stage.Parent = leaderstats
	
	local Rebirths = Instance.new("IntValue")
	Rebirths.Name = "Rebirths"
	Rebirths.Value = 0
	Rebirths.Parent = leaderstats
	
	--player's spawns at the new checkpoint
	plr.CharacterAdded:Connect(function(char)
		wait()
		if Stage.Value > 1 then
			char:MoveTo(Checkpoints:FindFirstChild(Stage.Value -1).Position)  

		end
	end)
	--player will be able to rebirth at the max stage
	local rebirthEvent = game.ReplicatedStorage.RebirthEvent
	rebirthEvent.OnServerEvent:Connect(function()
		
			
			Rebirths.Value += 1
			plr.Character:WaitForChild("HumanoidRootPart").CFrame = game.Workspace.SpawnLocation.CFrame + Vector3.new(0, 3, 0)
			Stage.Value = 1
		
			print("Player has not reached the max stage")
		
	end)
end)
	--Load Data
game.Players.PlayerAdded:Connect(function(plr)
	local DataStoreService = game:GetService("DataStoreService")
	local myData = DataStoreService:GetDataStore("myData")
	local success, errormsg = pcall(function()
		local playerData = myDataStore:GetAsync(plr.UserId)
		if plr.leaderstats.Stage.Value ~= 0 then 
		
			plr.leaderstats.Stage.Value = playerData
			plr.leaderstats.Rebirths.Value = playerData
			print(plr.Name.."'s data was received from the data store")
		else
			print(plr.Name.."'s has no previously saved data")
			plr.leaderstats.Stage.Value = 1
		end
	end)
	if errormsg then 
		warn(errormsg)
		end
	end)

game.Players.PlayerRemoving:Connect(function(plr)
	local dataTable = {plr.leaderstats.Stage.Value;
		plr.leaderstats.Rebirths.Value;
	}
	local success, errormsg = pcall(function()
		myDataStore:SetAsync(plr.UserId, dataTable)
	end)
	if success then 
		print("Successfully saved"..plr.Name.."'s data.")
	else
		warn(errormsg)
	end
end)


--Stage value is increased
for i, checkpoint in pairs(Checkpoints:GetChildren()) do
	checkpoint.Touched:Connect(function(hit)
		if hit.Parent:FindFirstChild("Humanoid") then
			local char = hit.Parent
			local plr = game.Players:GetPlayerFromCharacter(char)
			if tonumber(checkpoint.Name) == plr.leaderstats.Stage.Value then
				plr.leaderstats.Stage.Value += 1
		
			end
		end
	end)
end

I just tried adding the dataTable you mentioned but I’m not sure if I did it right.

Is saving and loading are different scripts? Why are you using a variable for DataStoreService twice?

No it’s the same script I didn’t realize I put it twice

I think that’s why it’s not working but I had to go so I can’t test it

Firstly, I suggest you to use a variable off all services. Don’t use game because service names can also be changed. And GetService() method is much safer.

Also you can use scopes to store different values or you can store a table holding values into DataStore. I think scopes are better because they are just like normal but if you use tables, you must deserialize it or use table methods.

You can use scopes like below:
DataStoreService:GetDataStore(“DataStoreName”, “ScopeName”)

This way data store will split into parts to act like another scope.

1 Like

Before I try to make suggestions, is there a reason you have two separate playerAdded functions? I don’t use two but people do things differently so I just want to understand first.

I am leaving a full example of these for you. You can use them later when you are here.


local Players = game:GetService("Players")
local ServerStorage = game:GetService("ServerStorage")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local DataStoreService = game:GetService("DataStoreService")
local BadgeService = game:GetService("BadgeService")
local RunService = game:GetService("RunService")
local _Workspace = game:GetService("Workspace")

local BucksDataStore = DataStoreService:GetDataStore("LeaderstatsData", "BucksData")
local WinsDataStore = DataStoreService:GetDataStore("LeaderstatsData", "WinsData")

local BindableEvents = ReplicatedStorage.BindableEvents
local RemoteEvents = ReplicatedStorage.RemoteEvents
local OnTouchedWinBrick = BindableEvents.OnTouchedWinBrick
local OnNotificationSent = RemoteEvents.OnNotificationSent
local OnRespawnRequested = RemoteEvents.OnRespawnRequested
local OnMessageSent = RemoteEvents.OnMessageSent

local BadgeData = {
	["First Win!"] = 3424151724753858,
	["First Playthrough!"] = 3843340681779809
}

Players.PlayerAdded:Connect(function(Player)
	if not BadgeService:UserHasBadgeAsync(Player.UserId, BadgeData["First Playthrough!"]) then
		. . .
	end
	
	local Leaderstats = Instance.new("Folder", Player)
	Leaderstats.Name = "leaderstats"
	
	local BucksValue = Instance.new("IntValue", Leaderstats)
	BucksValue.Name = "Bucks"
	
	local WinsValue = Instance.new("IntValue", Leaderstats)
	WinsValue.Name = "Wins"	
	
	local StageValue = Instance.new("StringValue", Leaderstats)
	StageValue.Name = "Checkpoint"
	StageValue.Value = 1
	
	if not RunService:IsStudio() and BucksDataStore:GetAsync(Player.UserId) then
		BucksValue.Value = BucksDataStore:GetAsync(Player.UserId)
	end

	if not RunService:IsStudio() and WinsDataStore:GetAsync(Player.UserId) then
		WinsValue.Value = WinsDataStore:GetAsync(Player.UserId)
	end
	
	local Spawnpoint = Instance.new("ObjectValue", ServerStorage.Spawnpoints)
	Spawnpoint.Name = Player.UserId
	
	task.wait()
	Spawnpoint.Changed:Connect(function(Value)
		if not Value or Value == nil then StageValue.Value = 1 else
			if (Value.Parent.Name:lower():split("stage")[2] + 2) == #_Workspace.Stages:GetChildren() then
				StageValue.Value = "FINISH"
			else StageValue.Value = Value.Parent.Name:lower():split("stage")[2] + 1 end
		end
	end)
	
	Player.CharacterAdded:Connect(function(Character)		
		. . .
	end)
	
	Player.CharacterAppearanceLoaded:Connect(function(Character)
		. . .
	end)
end)

Players.PlayerRemoving:Connect(function(Player)
	if RunService:IsStudio() then return end

	local Leaderstats = Player:FindFirstChild("leaderstats")
	local BucksValue = Leaderstats:FindFirstChild("Bucks")
	local WinsValue = Leaderstats:FindFirstChild("Wins")

	local Success, Response
	Success, Response = pcall(function()
		BucksDataStore:SetAsync(Player.UserId, BucksValue.Value)
	end)

	if not Success then
		warn("An error occured during saving Bucks data:", Response)
	end

	Success, Response = pcall(function()
		WinsDataStore:SetAsync(Player.UserId, WinsValue.Value)
	end)

	if not Success then
		warn("An error occured during saving wins data:", Response)
	end
end)

OnTouchedWinBrick.Event:Connect(function(Player)
	local Leaderstats = Player:FindFirstChild("leaderstats")
	local BucksValue = Leaderstats:FindFirstChild("Bucks")
	local WinsValue = Leaderstats:FindFirstChild("Wins")

	BucksValue.Value += 450
	WinsValue.Value += 1
	ReplicatedStorage.WinnerAmount.Value += 1

	OnMessageSent:FireAllClients(string.format("%s completed the obstacle course!", Player.DisplayName), "RBXGeneral.Player.Completed")

	if not BadgeService:UserHasBadgeAsync(Player.UserId, BadgeData["First Win!"]) then
		. . .
	end
end)

1 Like

No there’s no specific reason. Should you only use one PlayerAdded function or does it not matter?

If you use only one then the variables will already declared.
But in the second one you have to get variables.

1 Like

If you use two then you have to declare so much all over again, it’s super inefficient and bad practice. I’ve never used two so I’m not sure if it necessarily “matters”, but ok here are my suggesstions.

Firstly I’ll address the PlayerRemoving function. I suggest you do it like this, and I’ll explain after:

game.Players.PlayerRemoving:Connect(function(plr)
	local data = {
		stageValue = plr.leaderstats.Stage.Value;
		rebirthValue = plr.leaderstats.Rebirths.Value;
	}
	local success, errormsg = pcall(function()
		myDataStore:SetAsync(plr.UserId, data)
	end)

	if success then 
		print("Successfully saved"..plr.Name.."'s data.")
	else
		warn(errormsg)
	end
end)

I’m pretty sure the way I saved the data would be called a dictionary??? Leonart correct me if I’m wrong. Anyways saving it this way will allow all information to individually be assigned to it’s own value in the table. Next, you will need to change the way you load this data:

game.Players.PlayerAdded:Connect(function(plr)
	-- You may notice that I did not did not declare DataStoreService OR myDataStore again, this is because you already declared both at the start of your script and declaring it again is not necessary + inefficient


	local leaderstats = Instance.new("Folder")
	leaderstats.Name = "leaderstats"
	leaderstats.Parent = plr
	
	local Stage = Instance.new ("IntValue", leaderstats)
	Stage.Name = "Stage"
	Stage.Value = 1
	Stage.Parent = leaderstats
	
	local Rebirths = Instance.new("IntValue")
	Rebirths.Name = "Rebirths"
	Rebirths.Value = 0
	Rebirths.Parent = leaderstats
	
	--player's spawns at the new checkpoint
	plr.CharacterAdded:Connect(function(char)
		wait()
		if Stage.Value > 1 then
			char:MoveTo(Checkpoints:FindFirstChild(Stage.Value -1).Position)  

		end
	end)
	--player will be able to rebirth at the max stage
	local rebirthEvent = game.ReplicatedStorage.RebirthEvent
	rebirthEvent.OnServerEvent:Connect(function()
		
			
			Rebirths.Value += 1
			plr.Character:WaitForChild("HumanoidRootPart").CFrame = game.Workspace.SpawnLocation.CFrame + Vector3.new(0, 3, 0)
			Stage.Value = 1
		
			print("Player has not reached the max stage")
		
	end)
end)

local success, errormsg = pcall(function()
		local data = myDataStore:GetAsync(plr.UserId)
		if plr.leaderstats.Stage.Value ~= 0 then 
		
			plr.leaderstats.Stage.Value = data.stageValue
			plr.leaderstats.Rebirths.Value = data.rebirthsValue
			print(plr.Name.."'s data was received from the data store")
		else
			print(plr.Name.."'s has no previously saved data")
			plr.leaderstats.Stage.Value = 1
		end
	end)
	if errormsg then 
		warn(errormsg)
	end
end)

There are actually quite a few more things I would do differently, so I’m not sure this will work. But I tried adjusting it to your coding style so I suggest seeing if this would work first. Anyway this would make your final code:

local Checkpoints = game.Workspace.Checkpoints
local DataStoreService = game:GetService("DataStoreService")
local myDataStore = DataStoreService:GetDataStore("myData")

game.Players.PlayerAdded:Connect(function(plr)
	-- You may notice that I did not did not declare DataStoreService OR myDataStore again, this is because you already declared both at the start of your script and declaring it again is not necessary + inefficient


	local leaderstats = Instance.new("Folder")
	leaderstats.Name = "leaderstats"
	leaderstats.Parent = plr
	
	local Stage = Instance.new ("IntValue", leaderstats)
	Stage.Name = "Stage"
	Stage.Value = 1
	Stage.Parent = leaderstats
	
	local Rebirths = Instance.new("IntValue")
	Rebirths.Name = "Rebirths"
	Rebirths.Value = 0
	Rebirths.Parent = leaderstats
	
	--player's spawns at the new checkpoint
	plr.CharacterAdded:Connect(function(char)
		wait()
		if Stage.Value > 1 then
			char:MoveTo(Checkpoints:FindFirstChild(Stage.Value -1).Position)  

		end
	end)
	--player will be able to rebirth at the max stage
	local rebirthEvent = game.ReplicatedStorage.RebirthEvent
	rebirthEvent.OnServerEvent:Connect(function()
		
			
			Rebirths.Value += 1
			plr.Character:WaitForChild("HumanoidRootPart").CFrame = game.Workspace.SpawnLocation.CFrame + Vector3.new(0, 3, 0)
			Stage.Value = 1
		
			print("Player has not reached the max stage")
		
	end)

local success, errormsg = pcall(function()
		local data = myDataStore:GetAsync(plr.UserId)
		if plr.leaderstats.Stage.Value ~= 0 then 
		
			plr.leaderstats.Stage.Value = data.stageValue
			plr.leaderstats.Rebirths.Value = data.rebirthsValue
			print(plr.Name.."'s data was received from the data store")
		else
			print(plr.Name.."'s has no previously saved data")
			plr.leaderstats.Stage.Value = 1
		end
	end)
	if errormsg then 
		warn(errormsg)
	end
end)

game.Players.PlayerRemoving:Connect(function(plr)
	local data = {
		stageValue = plr.leaderstats.Stage.Value;
		rebirthValue = plr.leaderstats.Rebirths.Value;
	}
	local success, errormsg = pcall(function()
		myDataStore:SetAsync(plr.UserId, data)
	end)

	if success then 
		print("Successfully saved"..plr.Name.."'s data.")
	else
		warn(errormsg)
	end
end)

--Stage value is increased
for i, checkpoint in pairs(Checkpoints:GetChildren()) do
	checkpoint.Touched:Connect(function(hit)
		if hit.Parent:FindFirstChild("Humanoid") then
			local char = hit.Parent
			local plr = game.Players:GetPlayerFromCharacter(char)
			if tonumber(checkpoint.Name) == plr.leaderstats.Stage.Value then
				plr.leaderstats.Stage.Value += 1
		
			end
		end
	end)
end

Try it and let me know if anything isn’t working.

Thank you! I’ll get back to you whenever I get the chance to test it and look it over.

I’m not sure it will work so don’t thank me just yet haha, if it doesn’t I’ll remake it using my coding style (Extremely similar to Leonart’s example so I do highly suggest looking at that).

1 Like

Yeah haha I just appreciate the time you took to help me out. I tested it out and got this error code

  ServerScriptService.Leaderstats:51: attempt to index number with 'stageValue'  -  Server - Leaderstats:60

It would be nice to see and learn from your coding style especially if its better or more efficient.

That was actually my mistake, and I do see it and can point it out if you would like, but given your other statement I’ll point out some things I do differently than you.

Firstly, I set all my constants and global values as variables at the very beginning of the script. Constants being anything that does not change and global values being anything that will always apply to everything in the document (Any values you’re currently typing over and over again). This includes services (The ones you commonly use). I also like to create my “data” variable at the very top.

Therefore if I were you I’d start my entire script like this:

local DataStoreService = game:GetService("DataStoreService")
local myDataStore = DataStoreService:GetDataStore("myData")
--Here I'll place the services I notice you'll need throughout your script.
local players = game:GetService("Players")
local RS = game:GetService("ReplicatedStorage")
--That were the only two

local data
local Checkpoints = game.Workspace.Checkpoints
local rebirthEvent = RS.RebirthEvent

Now instead of typing all of your PlayerAdded and PlayerRemoved content directly under an anonymous function, being as to how WE KNOW PlayerAdded and PlayerRemoving takes in plr as a passed value, I assign those to a defined function with the given name and call that function when PlayerAdded or PlayerRemoving happens at the very bottom. And because we created a global players variable for the players service, you don’t have to keep typing game.Players and can just type the variable. I also see you have the CharacterAdded event connected to an anonymous function as well, so I’d suggest making a defined function for when the character is added as well. New Code:

local DataStoreService = game:GetService("DataStoreService")
local myDataStore = DataStoreService:GetDataStore("myData")
local players = game:GetService("Players")
local RS = game:GetService("ReplicatedStorage")

local data
local Checkpoints = game.Workspace.Checkpoints
local rebirthEvent = RS.RebirthEvent


local function onCharacterAdded(char, Stage) -- Just added
	wait()
	if Stage.Value  > 1 then
		char:MoveTo(Checkpoints:FindFirstChild(Stage.Value - 1).Position)
	end
end


local function onPlayerAdded(plr) -- Just added
	local userId = plr.UserId

	local leaderstats = Instance.new("Folder")
	leaderstats.Name = "leaderstats"
	leaderstats.Parent = plr
	
	local Stage = Instance.new ("IntValue", leaderstats)
	Stage.Name = "Stage"
	Stage.Value = 1
	Stage.Parent = leaderstats
	
	local Rebirths = Instance.new("IntValue")
	Rebirths.Name = "Rebirths"
	Rebirths.Value = 0
	Rebirths.Parent = leaderstats

	plr.CharacterAdded:Connect(function()
		onCharacterAdded(plr.Character, Stage)
	end)

	rebirthEvent.OnServerEvent:Connect(function()
		Rebirths.Value += 1
		plr.Character:WaitForChild("HumanoidRootPart").CFrame = game.Workspace.SpawnLocation.CFrame + Vector3.new(0, 3, 0)
		Stage.Value = 1
		
		print("Player has not reached the max stage")
	end)
end

local function onPlayerRemoving(plr) -- Just added

end

players.PlayerAdded:Connect(onPlayerAdded) -- Just Added
players.PlayerRemoving:Connect(onPlayerRemoving) -- Just Added

Now, you want the stored data to be loaded into the player the second they joined. So you want to check for success with the pcall as well as apply that data if it is successful INSIDE the onPlayerAdded function. So modify your onPlayerAdded function to look like the following:

local function onPlayerAdded(plr) 
	local userId = plr.UserId -- Always assign variables for constant values instead of typing the definition over and over again. So now whenever you need the User Id you just have to type userId instead of plr.UserId
	
	local leaderstats = Instance.new("Folder")
	leaderstats.Name = "leaderstats"
	leaderstats.Parent = plr
	
	local Stage = Instance.new ("IntValue", leaderstats)
	Stage.Name = "Stage"
	Stage.Value = 1
	Stage.Parent = leaderstats
	
	local Rebirths = Instance.new("IntValue")
	Rebirths.Name = "Rebirths"
	Rebirths.Value = 0
	Rebirths.Parent = leaderstats

	plr.CharacterAdded:Connect(function()
		onCharacterAdded(plr.Character, Stage)
	end)

	rebirthEvent.OnServerEvent:Connect(function()
		Rebirths.Value += 1
		plr.Character:WaitForChild("HumanoidRootPart").CFrame = game.Workspace.SpawnLocation.CFrame + Vector3.new(0, 3, 0)
		Stage.Value = 1
		
		print("Player has not reached the max stage")
	end)
	
	local success, errormsg = pcall(function() -- Just Added
		data = myDataStore:GetAsync(userId) -- Here is where we assign the data
	end

	if success and data ~= nil then -- If retrieving the data is successful and there is actually value in the data, load it.
	--The stage value will never be 0 as you literally set it to 1 before you let any of this happen, and therefore that if statement is not needed.
		plr.leaderstats.Stage.Value = data.stageValue
		plr.leaderstats.Rebirths.Value = data.rebirthsValue
		print(plr.Name.."'s data was received from the data store")
	else -- Otherwise
		if errormsg then
			warn(errormsg)
		end
		
		if data == nil then
			print(plr.Name.."'s has no previously saved data")
		end
	end
end

That should be it for the onPlayerAdded function, now for removing. For the most part, it’s fine. But a major problem is that it doesn’t check for if the server is closing, meaning if you were to ever shut down the server while there are players, they will lose their data. It also means that the last player is not always guaranteed to have their data save if the server happens to shut down before it finishes saving data for that last player. So I suggest:

local function onPlayerRemoving(plr, closing) -- Notice we added a new value to be passed in, it is a boolean value.
	local success = nil -- Define this variable up here but give it a value of nil
	local errormsg = nil -- This one too
	local userId = plr.UserId -- let's not forget to assign a variable
	
	local data = {
		stageValue = plr.leaderstats.Stage.Value;
		rebirthValue = plr.leaderstats.Rebirths.Value;
	}
	
	if closing == true then -- Now, if we're closing, we want to return the userId and data to be used in the function binded to the game's closing (Below)
		return userId, data
	else
		success, errormsg = pcall(function()
			myDataStore:SetAsync(userId, data) -- If you end up with a problem where the data doesn't save (At any point at all, even if it's not now), add a wait(7) before this line.
		end)
	end
	
	if success then 
		print("Successfully saved"..plr.Name.."'s data.")
	else
		warn(errormsg)
	end
end

game:BindToClose(function() -- This saves data if the game closes (Or server shuts down) while there are still several players in it.
	for _, Player in pairs(game.Players:GetPlayers()) do
		print("Saving data before server closes")
		local userId, data = onPlayerRemoving(Player, true)
		myDataStore:SetAsync(userId, data)
		wait(.7)
	end
end)

I think this should be just about everything. So you will need to include that for loop you have at the bottom. Which turns your final script into:

local DataStoreService = game:GetService("DataStoreService")
local myDataStore = DataStoreService:GetDataStore("myData")
local players = game:GetService("Players")
local RS = game:GetService("ReplicatedStorage")

local data
local Checkpoints = game.Workspace.Checkpoints
local rebirthEvent = RS.RebirthEvent


local function onCharacterAdded(char, Stage) -- Just added
	wait()
	if Stage.Value  > 1 then
		char:MoveTo(Checkpoints:FindFirstChild(Stage.Value - 1).Position)
	end
end

local function onPlayerAdded(plr) 
	local userId = plr.UserId -- Always assign variables for constant values instead of typing the definition over and over again. So now whenever you need the User Id you just have to type userId instead of plr.UserId
	
	local leaderstats = Instance.new("Folder")
	leaderstats.Name = "leaderstats"
	leaderstats.Parent = plr
	
	local Stage = Instance.new ("IntValue", leaderstats)
	Stage.Name = "Stage"
	Stage.Value = 1
	Stage.Parent = leaderstats
	
	local Rebirths = Instance.new("IntValue")
	Rebirths.Name = "Rebirths"
	Rebirths.Value = 0
	Rebirths.Parent = leaderstats

	plr.CharacterAdded:Connect(function()
		onCharacterAdded(plr.Character, Stage)
	end)

	rebirthEvent.OnServerEvent:Connect(function()
		Rebirths.Value += 1
		plr.Character:WaitForChild("HumanoidRootPart").CFrame = game.Workspace.SpawnLocation.CFrame + Vector3.new(0, 3, 0)
		Stage.Value = 1
		
		print("Player has not reached the max stage")
	end)
	
	local success, errormsg = pcall(function() -- Just Added
		data = myDataStore:GetAsync(userId) -- Here is where we assign the data
	end

	if success and data ~= nil then -- If retrieving the data is successful and there is actually value in the data, load it.
	--The stage value will never be 0 as you literally set it to 1 before you let any of this happen, and therefore that if statement is not needed.
		plr.leaderstats.Stage.Value = data.stageValue
		plr.leaderstats.Rebirths.Value = data.rebirthsValue
		print(plr.Name.."'s data was received from the data store")
	else -- Otherwise
		if errormsg then
			warn(errormsg)
		end
		
		if data == nil then
			print(plr.Name.."'s has no previously saved data")
		end
	end
end

local function onPlayerRemoving(plr, closing) -- Notice we added a new value to be passed in, it is a boolean value.
	local success = nil -- Define this variable up here but give it a value of nil
	local errormsg = nil -- This one too
	local userId = plr.UserId -- let's not forget to assign a variable
	
	local data = {
		stageValue = plr.leaderstats.Stage.Value;
		rebirthValue = plr.leaderstats.Rebirths.Value;
	}
	
	if closing == true then -- Now, if we're closing, we want to return the userId and data to be used in the function binded to the game's closing (Below)
		return userId, data
	else
		success, errormsg = pcall(function()
			myDataStore:SetAsync(userId, data) -- If you end up with a problem where the data doesn't save (At any point at all, even if it's not now), add a wait(7) before this line.
		end)
	end
	
	if success then 
		print("Successfully saved"..plr.Name.."'s data.")
	else
		warn(errormsg)
	end
end

game:BindToClose(function() -- This saves data if the game closes (Or server shuts down) while there are still several players in it.
	for _, Player in pairs(game.Players:GetPlayers()) do
		print("Saving data before server closes")
		local userId, data = onPlayerRemoving(Player, true)
		myDataStore:SetAsync(userId, data)
		wait(.7)
	end
end)

players.PlayerAdded:Connect(onPlayerAdded)
players.PlayerRemoving:Connect(onPlayerRemoving)

--Stage value is increased
for i, checkpoint in pairs(Checkpoints:GetChildren()) do
	checkpoint.Touched:Connect(function(hit)
		if hit.Parent:FindFirstChild("Humanoid") then
			local char = hit.Parent
			local plr = game.Players:GetPlayerFromCharacter(char)
			if tonumber(checkpoint.Name) == plr.leaderstats.Stage.Value then
				plr.leaderstats.Stage.Value += 1
		
			end
		end
	end)
end

Tell me how this goes! If it gives you the same error "attempt to index number with ‘stageValue’, then that means the variable “data” was set to a number instead of the table at some point in the script. It could be that data still holds some previous value, so before asking me for help, try changing the data store’s name so a different data store will be utilized.

Hey, I just tested this out and it’s all working! At first the rebirths values weren’t saving, but I realized it was spelled wrong when retrieving the data. Thank you!

1 Like

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