Data store error

Error trying to save data in dataStore

The moment I enter the game and exit it I get a dataStore error saying ServerScriptService.PlayerData:82: attempt to index nil with number. But I made sure that if the player starts and has no money or pets, assign a default value, then update the player’s money as Cash.Value changes and when closing the game save the pets. What I don’t understand is why once all this is done the values ​​are not saved and are reset. Does anyone have a solution? Thank you :slight_smile:


local httpService = game:GetService("HttpService")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local dataStoreService = game:GetService("DataStoreService")
local dataBase = dataStoreService:GetDataStore("MyDataStore")
local Players = game:GetService("Players")
local RS = game:GetService("RunService")
local DataObject = {0, {}}

function clonAndMovePet(player, petName)
	local petsFolder = ReplicatedStorage:FindFirstChild("PetsFolder")
	if not petsFolder then return end

	local petModel = petsFolder:FindFirstChild(petName)
	if not petModel then return end

	local petsPlayerFolder = workspace:FindFirstChild("PlayerPets")
	if not petsPlayerFolder then
		petsPlayerFolder = Instance.new("Folder")
		petsPlayerFolder.Name = "PlayerPets"
		petsPlayerFolder.Parent = workspace
	end

	local playerFolder = petsPlayerFolder:FindFirstChild(player.Name)
	if not playerFolder then
		playerFolder = Instance.new("Folder")
		playerFolder.Name = player.Name
		playerFolder.Parent = petsPlayerFolder
	end

	local clonedPet = petModel:Clone()
	clonedPet.Parent = playerFolder
end

function PlayerAdded(player)
	local leaderstats = Instance.new("Folder", player)
	leaderstats.Name = "leaderstats"
	local CashValue = Instance.new("IntValue", leaderstats)
	CashValue.Name = "CashValue"
	local petInventoryFolder = Instance.new("Folder", player)
	petInventoryFolder.Name = "PetInventoryFolder"

	local attempt = 1
	local success = nil
	local playerData = nil

	repeat
		success, playerData = pcall(function()
			DataObject = dataBase:GetAsync(player.UserId)
		end)
		attempt += 1
		if not success then
			warn(playerData)
			task.wait(3)
		end
	until success or attempt == 5


	if not DataObject then
		DataObject = {0, {}}
	end
	
	if DataObject[1] ~= nil then
		CashValue.Value = DataObject[1]
	end
	
	CashValue.Changed:Connect(function()
		DataObject[1] = CashValue.Value
	end)
	
	if DataObject[2] ~= nil then
		
		local petData = DataObject[2]
		for petName, petValue in pairs(petData) do
			local petFolder = Instance.new("Folder", petInventoryFolder)
			petFolder.Name = petName
			local petValueObj = Instance.new("StringValue", petFolder)
			petValueObj.Name = "ID"
			petValueObj.Value = petValue
		end
	end

	if DataObject then
		print(DataObject[2])

		if DataObject[2] then
			for petID, petName in pairs(DataObject[2]) do
				clonAndMovePet(player, petName)
			end
		elseif DataObject[2] == nil then
			DataObject[2] = {}
		end
	else
		print("DataObject es nulo en PlayerAdded")
	end
end

function PlayerRemoving(player)
	local petInventoryFolder = player:FindFirstChild("PetInventoryFolder")
	local petData = {}

	for i, petFolder in pairs(petInventoryFolder:GetChildren()) do
		for i, petValue in pairs(petFolder:GetChildren()) do
			if petValue.Name == "ID" then
				petData[petFolder.Name] = petValue.Value
			end
		end
	end

	if DataObject then
		DataObject[2] = petData
	else
		print("No data object")
	end

	if DataObject then
		local attempt = 1
		local success = nil
		local errorMsg = nil

		repeat
			success, errorMsg = pcall(function()
				dataBase:SetAsync(player.UserId, DataObject)
			end)
			attempt += 1
		until success or attempt == 5

		if success then
			print("Data saved successfully for", player.Name)
		else
			warn("Error saving data", errorMsg)
		end
	else
		print("No data object")
	end

	print(DataObject)
end

function ServerShutDown()
	if RS:IsStudio() then
		return
	end
	task.spawn(function()
		PlayerRemoving(Players:GetPlayers())
	end)
end

game:BindToClose(ServerShutDown)
game.Players.PlayerRemoving:Connect(PlayerRemoving)
game.Players.PlayerAdded:Connect(PlayerAdded)


First thing I noticed is that you are passing a table (Players:GetPlayers()) to the PlayerRemoving function when the ServerShutDown function is called.

If you are testing this in studio, i.e. with a single player, then this function will always be called since the server will automatically shutdown when there are no players left in the server.

1 Like

You are correct, I just tested it by commenting out the ServerShutDown function, and it worked perfectly. Anyway, I don’t understand why this was causing the DataStore not to work when it should save it in case the servers are shut down in the game. I’m sure you answered this question in the previous comment, but I didn’t quite understand it, hahaha, my head is a bit fried after 12 hours of programming straight.

The issue when a server closes is it calls PlayerRemoving as the player leaves (the last and only player in this case) and then the server dies (because no players are left) and BindToClose is automatically called to accommodate the times when the server is forced to shutdown when Roblox’s servers go offline or if there is a system wide crash (this is the developers last-ditch opportunity to ask the DataStore to save as much data as possible to prevent data loss). The trick is to separate the PlayerRemoving event from an actual server close and the failsafe BindToClose.

So if I understood correctly, we try to separate bindToClose from the playerRemoving function because if not we are saving nil data because bindToClose is executed even when the player left the server, so it saves nil data? I’m sorry for the inconvenience again hahaha

Not entirely, BindToClose is an emergency, failsafe mechanism that attempts to mitigate a server shutdown preventing players data saving correctly. PlayerRemoving may be interrupted or halted when the server dies (if it crashes or their is a system failure) so just before it dies the server will send a BindToClose event to pump as much data from connected clients to the server, so it can recover as much player data as possible.

1 Like

oh now I understand it haha, so how can I save the data correctly when the server is about to close? Thank you very much again :slight_smile: