What is wrong with my datastore?

When I join the game, update a value, then leave and rejoin, the value is at 0. My datastore isn’t saving. There are multiple values.

I tested in game.

Can someone help?

local DataStore = game:GetService("DataStoreService")
local GameData = DataStore:GetDataStore("GameData")
local AccessData = DataStore:GetDataStore("TP_Data")

local function addValues(player)
	local folder = Instance.new("Folder")
	folder.Name = "leaderstats"
	folder.Parent = player

	local coins = Instance.new("IntValue")
	coins.Name = "Coins"
	coins.Parent = folder

	local diamonds = Instance.new("IntValue")
	diamonds.Name = "Diamonds"
	diamonds.Parent = folder

	local rocks = Instance.new("IntValue")
	rocks.Name = "Rocks"
	rocks.Parent = folder
	
	print("Values created")

	---------- TP Access ------------

	local accessFolder = Instance.new("Folder")
	accessFolder.Name = "TP_Folder"
	accessFolder.Parent = player

	local beach = Instance.new("BoolValue")
	beach.Name = "Beach"
	beach.Parent = accessFolder

	local magma = Instance.new("BoolValue")
	magma.Name = "Magma"
	magma.Parent = accessFolder

	local winter = Instance.new("BoolValue")
	winter.Name = "Winter"
	winter.Parent = accessFolder

	local candy = Instance.new("BoolValue")
	candy.Name = "Candyland"
	candy.Parent = accessFolder
	
	print("Access folders created")

	local playerUserId = "Player_" .. player.UserId
	local data = GameData:GetAsync(playerUserId)
	local TP_Data = AccessData:GetAsync(playerUserId)

	if data then
		coins.Value = data["Coins"]
		diamonds.Value = data["Diamonds"]
		rocks.Value = data["Rocks"]
		print("Updated values")
	else
		coins.Value = 0
		diamonds.Value = 0
		rocks.Value = 0
		print("Updated values")
	end

	if TP_Data then
		beach.Value = TP_Data["Beach"]
		magma.Value = TP_Data["Magma"]
		winter.Value = TP_Data["Winter"]
		candy.Value = TP_Data["Candyland"]
		print("Access values updated")
	else
		beach.Value = false
		magma.Value = false
		winter.Value = false
		candy.Value = false
		print("Access values updated")
	end
end

local function create_table_leaderstats(player)
	local player_stats = {}
	for _, stat in pairs(player.leaderstats:GetChildren()) do
		player_stats[stat.Name] = stat.Value
	end
	print("leaderstats values table made")
	return player_stats
end

local function create_table_access(player)
	local player_stats = {}
	for _, stat in pairs(player.TP_Folder:GetChildren()) do
		player_stats[stat.Name] = stat.Value
	end
	print("access values table made")
	return player_stats
end

local function on_Exit(player)
	local leaderstatsData = create_table_leaderstats(player)
	local accessData = create_table_access(player)
	
	local success, err = pcall(function()
		local playerUserId = "Player_" .. player.UserId
		GameData:SetAsync(playerUserId, leaderstatsData)
		AccessData:SetAsync(playerUserId, accessData)
		print("saved")
	end)
	
	if success then
		print("Worked")
	else
		warn(err.." - DATA NOT SAVED!")
	end
end

game.Players.PlayerAdded:Connect(addValues)
print("D1")
game.Players.PlayerRemoving:Connect(on_Exit)
print("D2")

game:BindToClose(function()
	task.wait(1)
	for _, plr in pairs(game:GetService("Players"):GetPlayers()) do
		on_Exit(plr)
	end
	print("saved values for server shutdown")
end)
1 Like
Mostly likely the server is closing before your data is saved, call

game:BindToClose(function() — function called before server closes
task.wait(1)— waits one second. The server closing is delayed for one second
end
1 Like

How would I save the data with this? After task.wait(1), I use the on_Exit function and loop through each player to save their data?

It would be outside of all your functions.



game.Players.PlayerAdded:Connect(addValues)
game.Players.PlayerRemoving:Connect(on_Exit)

game:BindToClose(function()
— wait code here
task.wait 1
end

Game bind to close does not fire when a player leaves but when the server closes. When the last player leaves, this will give them time to save their data before the server closes

game:BindToClose(function()
	task.wait(1)
	for _, plr in pairs(game:GetService("Players"):GetPlayers()) do
		on_Exit(plr)
	end
end)

Still doesn’t save. I don’t know whats going on lol.

Edit: Ima test in game and see

last player leaves the game
game:BindToClose(function()— game sees there is a bind on close function so it fires it before the server ends

tast.wait(1) — waits one. It delays the server from closing so your saving code can run
end)—finishes the function

— server closes

Sorry if I make a spelling mistake, I am on tablet

If this does not work add print statements when loading and saving the data. Send the output afterwards

Still not working. The output is printing all the print codes. I think it could do with saving, but I’m not quite sure because the code should be correct.

How about when you loop through the loaded data tab and print the values and keys. Your changing the values on the server?

I just found out that “TP_Access” is the only thing saving. The leaderstats aren’t saving! How is that possible when the saving process is the exact same for both?!?

I do not know. Maybe have separate pcalls for them?

Think the problem would be fixed if you only used one datastore. It will also probably up your game performance

local Players = game:GetService("Players")
local DataStores = game:GetService("DataStoreService")
local DataStore = DataStores:GetDataStore("DataStore")

local XProtectedCall = xpcall
local TypeCheck = type

Players.PlayerAdded:Connect(function(Player)
	local Leaderstats = Instance.new("Folder")
	Leaderstats.Name = "leaderstats"
	Leaderstats.Parent = Player

	local Coins = Instance.new("IntValue")
	Coins.Name = "Coins"
	Coins.Parent = Leaderstats

	local Diamonds = Instance.new("IntValue")
	Diamonds.Name = "Diamonds"
	Diamonds.Parent = Leaderstats

	local Rocks = Instance.new("IntValue")
	Rocks.Name = "Rocks"
	Rocks.Parent = Leaderstats

	local TPFolder = Instance.new("Folder")
	TPFolder.Name = "TP_Folder"
	TPFolder.Parent = Player

	local Beach = Instance.new("BoolValue")
	Beach.Name = "Beach"
	Beach.Parent = TPFolder

	local Magma = Instance.new("BoolValue")
	Magma.Name = "Magma"
	Magma.Parent = TPFolder

	local Winter = Instance.new("BoolValue")
	Winter.Name = "Winter"
	Winter.Parent = TPFolder

	local Candy = Instance.new("BoolValue")
	Candy.Name = "Candyland"
	Candy.Parent = TPFolder
	
	local Success, Result = XProtectedCall(function()
		return DataStore:GetAsync(Player.UserId)
	end, function(Error)
		return Error
	end)
	
	if Success then
		if Result then
			print(Result)
			if TypeCheck(Result) == "table" then
				local PlayerStats = Result[1]
				Coins.Value = PlayerStats[Coins.Name]
				Diamonds.Value = PlayerStats[Diamonds.Name]
				Rocks.Value = PlayerStats[Rocks.Name]
				
				local PlayerTPs = Result[2]
				Beach.Value = PlayerTPs[Beach.Name]
				Magma.Value = PlayerTPs[Magma.Name]
				Winter.Value = PlayerTPs[Winter.Name]
				Candy.Value = PlayerTPs[Candy.Name]
			end
		end
	else
		warn(Result)
	end
end)

Players.PlayerRemoving:Connect(function(Player)
	local PlayerStats = {}
	for _, Stat in ipairs(Player.leaderstats:GetChildren()) do
		PlayerStats[Stat.Name] = Stat.Value
	end
	
	local PlayerTPs = {}
	for _, TP in ipairs(Player.TP_Folder:GetChildren()) do
		PlayerTPs[TP.Name] = TP.Value
	end
	
	local Data = {PlayerStats, PlayerTPs}
	
	local Success, Result = XProtectedCall(function()
		return DataStore:SetAsync(Player.UserId, Data)
	end, function(Error)
		return Error
	end)
	
	if Success then
		if Result then
			print(Result)
		end
	else
		warn(Result)
	end
end)

game:BindToClose(function()
	for _, Player in ipairs(Players:GetPlayers()) do
		local PlayerStats = {}
		for _, Stat in ipairs(Player.leaderstats:GetChildren()) do
			PlayerStats[Stat.Name] = Stat.Value
		end

		local PlayerTPs = {}
		for _, TP in ipairs(Player.TP_Folder:GetChildren()) do
			PlayerTPs[TP.Name] = TP.Value
		end

		local Data = {PlayerStats, PlayerTPs}

		local Success, Result = XProtectedCall(function()
			return DataStore:SetAsync(Player.UserId, Data)
		end, function(Error)
			return Error
		end)

		if Success then
			if Result then
				print(Result)
			end
		else
			warn(Result)
		end
	end
end)

You shouldn’t yield with task.wait() since it can cause loss, instead you should yield with a coroutine

I’d recommend using a function to contain the save data rather than copy/pasting it 2-3 times. Also, why are you using xpcall, when it doesn’t have any benefits over pcall in this case? Also, why did you assign it
and type to a much longer variable name?

(You also probably shouldn’t use SetAsync, UpdateAsync with a handler is safer)

Twice & I didn’t wrap it inside a function for the sake of clarity, xpcall was used to demonstrate its use and assigning the global function “type” to a local variable integrates it into the local environment (allowing for it to be fetched/indexed quicker) same applies for “xpcall”.

You should really use UpdateAsync so data doesn’t get lost, or use one of the open source modules. Using SetAsync is a data loss crisis waiting to happen.

Other things about the script

Using xpcall actually takes away from clarity, since xpcall is used for custom error handling (which you’re not doing any of). Making it not a function also takes away from clarity, since you don’t know if they’re supposed to have different uses.

Also, I decided to run a benchmark of using type and a variable containing type (using this module, script I benchmarked with below in the folding tab)

Benchmarking Script
local tbl = table.create(1e6,"t") 
local last = false
local ServerScriptService = game:GetService('ServerScriptService')
local Benchmarker = require(ServerScriptService:WaitForChild("ModuleScript"))

local function globalType()
	for i,v in ipairs(tbl) do 
		if type(i) == "string" then 
		--	last = true 
		else 
		--	last = false
		end  
	end
end

local typeVariable = type

local function typeVar()
	for i,v in ipairs(tbl) do 
		if typeVariable(v) == "string" then 
			--last = true 
		else 
		--	last = false
		end  
	end
end

Benchmarker:compare(globalType,typeVar)

According to the results, using type() is much faster:

It’s a small difference so use whatever you want, but type is more efficient

1 Like

I stuck with SetAsync() as was done in the provided script, my comment about clarity was regarding the bit of code which was copied twice, use of xpcall was to demonstrate its use (to those who are unfamiliar) and finally your bench-marking results are incorrect, localizing any variable will allow for it to be fetched faster (as you circumvent the need to index the global environment table in order to find it). I’m going to assume your use of that module was improper somehow.

Fixed! I added @sau2000 's suggestion and it saves. I just restarted my studio and it worked. Thanks for everyone who contributed!