Datastores not working correctly

You forget to save when the game restarts. You should also only be saving data in one datastore, to maximize performance.

Code:

local Players = game:GetService("Players")
local DataStoreService = game:GetService("DataStoreService")

local playerDataStore = DataStoreService:GetDataStore("playerData")

local function SaveData(player, data)
	local success, errorMessage = pcall(function()
		playerDataStore:SetAsync(player.UserId, data)
	end)
	
	if not success then
		warn(errorMessage)
	end
end

local function OnPlayerRemoving(player)
	local leaderstats = player.leaderstats

	local data = {
		Deaths = leaderstats["Deaths I"].Value,
		Escapes = leaderstats["Escapes I"].Value
	}

	SaveData(player, data)
end

Players.PlayerAdded:Connect(function(player)
	local playerData = playerDataStore:GetAsync(player.UserId) or {
		Deaths = 0,
		Escapes = 0,
	}
	
	local leaderstats = Instance.new("Folder")
	leaderstats.Name = "leaderstats"

	local deaths1 = Instance.new("IntValue")
	deaths1.Name = "Deaths I"
	deaths1.Value = playerData.Deaths
	deaths1.Parent = leaderstats

	local escapes1 = Instance.new("IntValue")
	escapes1.Name = "Escapes I"
	escapes1.Value = playerData.Escapes
	escapes1.Parent = leaderstats
	
	leaderstats.Parent = player
end)

Players.PlayerRemoving:Connect(OnPlayerRemoving)

game:BindToClose(function()
	for i, player in Players:GetPlayers() do
		task.spawn(OnPlayerRemoving, player)
	end
end)

Studio is also fairly bad at saving data, so you should try checking ingame.

3 Likes

omg that code is beautiful. As i was reading through it, it all just clicked together and i see why its going to work much better than my old code lol. I have one question though! Technically, my game is comprised of three separate parts. So these are all of the values i need to save:

  • Deaths1
  • Escapes1
  • Deaths2
  • Escapes2
  • Deaths3
  • Escapes3

I have adjusted the code accordingly. Can you take a quick glance at it and tell me if it’s all good to go? (i only want the Deaths1 and Escapes1 to appear in leaderstats btw).

local Players = game:GetService("Players")
local DataStoreService = game:GetService("DataStoreService")
local playerDataStore = DataStoreService:GetDataStore("playerData")

local function SaveData(player, data)
	local success, errorMessage = pcall(function()
		playerDataStore:SetAsync(player.UserId, data)
	end)

	if not success then
		warn(errorMessage)
	end
end

local function OnPlayerRemoving(player)
	local leaderstats = player.leaderstats

	local data = {
		Deaths1 = leaderstats["Deaths I"].Value,
		Escapes1 = leaderstats["Escapes I"].Value,
		Deaths2 = leaderstats["Deaths II"].Value,
		Escapes2 = leaderstats["Escapes II"].Value,
		Deaths3 = leaderstats["Deaths III"].Value,
		Escapes3 = leaderstats["Escapes III"].Value
	}

	SaveData(player, data)
end

Players.PlayerAdded:Connect(function(player)
	local playerData = playerDataStore:GetAsync(player.UserId) or {
		Deaths1 = 0,
		Escapes1 = 0,
		Deaths2 = 0,
		Escapes2 = 0,
		Deaths3 = 0,
		Escapes3 = 0
	}

	local leaderstats = Instance.new("Folder")
	leaderstats.Name = "leaderstats"

	local deaths1 = Instance.new("IntValue")
	deaths1.Name = "Deaths I"
	deaths1.Value = playerData.Deaths1
	deaths1.Parent = leaderstats

	local escapes1 = Instance.new("IntValue")
	escapes1.Name = "Escapes I"
	escapes1.Value = playerData.Escapes1
	escapes1.Parent = leaderstats

	leaderstats.Parent = player
end)

Players.PlayerRemoving:Connect(OnPlayerRemoving)

game:BindToClose(function()
	for i, player in Players:GetPlayers() do
		task.spawn(OnPlayerRemoving, player)
	end
end)

You may have connected your Deaths II, Escape II, and the others wrong since you didn’t put them in leaderstats. You also have to set the aforementioned values in your code.

What you may want:

local Players = game:GetService("Players")
local DataStoreService = game:GetService("DataStoreService")
local playerDataStore = DataStoreService:GetDataStore("playerData")

local function SaveData(player, data)
	local success, errorMessage = pcall(function()
		playerDataStore:SetAsync(player.UserId, data)
	end)

	if not success then
		warn(errorMessage)
	end
end

local function OnPlayerRemoving(player)
	local leaderstats = player.leaderstats
	local otherFolder = player.OtherFolder

	local data = {
		Deaths1 = leaderstats["Deaths I"].Value,
		Escapes1 = leaderstats["Escapes I"].Value,
		Deaths2 = otherFolder["Deaths II"].Value,
		Escapes2 = otherFolder["Escapes II"].Value,
		Deaths3 = otherFolder["Deaths III"].Value,
		Escapes3 = otherFolder["Escapes III"].Value
	}

	SaveData(player, data)
end

Players.PlayerAdded:Connect(function(player)
	local playerData = playerDataStore:GetAsync(player.UserId) or {
		Deaths1 = 0,
		Escapes1 = 0,
		Deaths2 = 0,
		Escapes2 = 0,
		Deaths3 = 0,
		Escapes3 = 0
	}

	local leaderstats = Instance.new("Folder")
	leaderstats.Name = "leaderstats"

	local deaths1 = Instance.new("IntValue")
	deaths1.Name = "Deaths I"
	deaths1.Value = playerData.Deaths1
	deaths1.Parent = leaderstats

	local escapes1 = Instance.new("IntValue")
	escapes1.Name = "Escapes I"
	escapes1.Value = playerData.Escapes1
	escapes1.Parent = leaderstats

	leaderstats.Parent = player
	
	local otherFolder = player:WaitForChild("OtherFolder")
	otherFolder["Deaths II"].Value = playerData.Deaths2
	otherFolder["Escapes II"].Value = playerData.Escapes2
	otherFolder["Deaths III"].Value = playerData.Deaths3
	otherFolder["Escapes III"].Value = playerData.Escapes3
end)

Players.PlayerRemoving:Connect(OnPlayerRemoving)

game:BindToClose(function()
	for i, player in Players:GetPlayers() do
		task.spawn(OnPlayerRemoving, player)
	end
end)

It may also be preferable for you to create the OtherFolder in this script too, as it will be easier to manage later. It’s your choice though, so do as you like.

By the way, the reason why studio bugs out is because sometimes the server will close before the player leaves (which will never happen in a real game), therefore not being able to save the data.

Thank you for the compliment!

2 Likes

It could be because the player’s data fails to load in, causing their save to “reset” back to zero when they leave. You could probably kick the player from the game and tell the PlayerRemoving function not to save the incorrect data.

sorry if that was worded poorly

2 Likes

i agree, i think it would be best to create the other folder in the script. Will this code work?:

local Players = game:GetService("Players")
local DataStoreService = game:GetService("DataStoreService")
local playerDataStore = DataStoreService:GetDataStore("playerData")

local function SaveData(player, data)
	local success, errorMessage = pcall(function()
		playerDataStore:SetAsync(player.UserId, data)
	end)

	if not success then
		warn(errorMessage)
	end
end

local function OnPlayerRemoving(player)
	local leaderstats = player.leaderstats
	local storedData = player.storedData

	local data = {
		Deaths1 = leaderstats["Deaths I"].Value,
		Escapes1 = leaderstats["Escapes I"].Value,
		Deaths2 = storedData["Deaths II"].Value,
		Escapes2 = storedData["Escapes II"].Value,
		Deaths3 = storedData["Deaths III"].Value,
		Escapes3 = storedData["Escapes III"].Value
	}

	SaveData(player, data)
end

Players.PlayerAdded:Connect(function(player)
	local playerData = playerDataStore:GetAsync(player.UserId) or {
		Deaths1 = 0,
		Escapes1 = 0,
		Deaths2 = 0,
		Escapes2 = 0,
		Deaths3 = 0,
		Escapes3 = 0
	}

	local leaderstats = Instance.new("Folder")
	leaderstats.Name = "leaderstats"

	local deaths1 = Instance.new("IntValue")
	deaths1.Name = "Deaths I"
	deaths1.Value = playerData.Deaths1
	deaths1.Parent = leaderstats

	local escapes1 = Instance.new("IntValue")
	escapes1.Name = "Escapes I"
	escapes1.Value = playerData.Escapes1
	escapes1.Parent = leaderstats

	leaderstats.Parent = player
	
	local storedData = Instance.new("Folder")
	storedData.Name = "storedData"
	
	storedData["Deaths II"].Value = playerData.Deaths2
	storedData["Escapes II"].Value = playerData.Escapes2
	storedData["Deaths III"].Value = playerData.Deaths3
	storedData["Escapes III"].Value = playerData.Escapes3
	
	storedData.Parent = player
end)

Players.PlayerRemoving:Connect(OnPlayerRemoving)

game:BindToClose(function()
	for i, player in Players:GetPlayers() do
		task.spawn(OnPlayerRemoving, player)
	end
end)
2 Likes

Even though it doesn’t really count to help, but i heavily recommend you using ProfileService so you won’t have any further problems

You need to create the other IntValues too.

Code:

local Players = game:GetService("Players")
local DataStoreService = game:GetService("DataStoreService")
local playerDataStore = DataStoreService:GetDataStore("playerData")

local defaultData = {
	leaderstats = {
		["Deaths I"] = 0,
		["Escapes I"] = 0
	},
	
	storedData = {
		["Deaths II"] = 0,
		["Escapes II"] = 0,
		["Deaths III"] = 0,
		["Escapes III"] = 0
	}
}

local function SaveData(player, data)
	local success, errorMessage = pcall(function()
		playerDataStore:SetAsync(player.UserId, data)
	end)

	if not success then
		warn(errorMessage)
	end
end

local function OnPlayerRemoving(player)
	local data = defaultData
	
	for folderName, folderValues in defaultData do
		local folder = player[folderName]
		
		for valueName, _ in folderValues do
			data[folderName][valueName] = folder[valueName].Value
		end
	end

	SaveData(player, data)
end

Players.PlayerAdded:Connect(function(player)
	local playerData = playerDataStore:GetAsync(player.UserId) or defaultData
	
	for folderName, folderValues in defaultData do
		local folder = Instance.new("Folder")
		folder.Name = folderName

		for valueName, _ in folderValues do
			local intValue = Instance.new("IntValue")
			intValue.Name = valueName
			intValue.Value = playerData[folderName][valueName]
			intValue.Parent = folder
		end
		
		folder.Parent = player
	end
end)

Players.PlayerRemoving:Connect(OnPlayerRemoving)

game:BindToClose(function()
	for i, player in Players:GetPlayers() do
		task.spawn(OnPlayerRemoving, player)
	end
end)

I’ve refactored your code to be easier to read, you will have to reset your data for this to work.

Command to reset data (put it in your command bar):

local DataStoreService = game:GetService("DataStoreService")
local playerDataStore = DataStoreService:GetDataStore("playerData")
playerDataStore:RemoveAsync(YourId)

Also, further elaborating on this:

If you want to test saving in studio, open up a Local Server test, as it will emulate how an actual game would work.

In each of the different parts(places) of my game (part 1, part 2, part 3) i need only the respective values to be in leaderstats:

  • Part 1: Deaths1 & Escapes1
  • Part 2: Deaths2 & Escapes2
  • Part 3: Deaths3 & Escapes3

How can i go about editing the script so that i can easily make those changes for each Part of my game without ruining the data saves?

You’re going to have to load all the data anyways to keep it persistent, so you can keep it as is.

If you want to hide the leaderstats though, you can do this:

local Players = game:GetService("Players")
local DataStoreService = game:GetService("DataStoreService")
local playerDataStore = DataStoreService:GetDataStore("playerData")

local defaultData = {
	leaderstats = {
		["Deaths I"] = 0,
		["Escapes I"] = 0
	},

	storedData = {
		["Deaths II"] = 0,
		["Escapes II"] = 0,
		["Deaths III"] = 0,
		["Escapes III"] = 0
	}
}

local leaderstatsName = game.PlaceId == firstPlace and "leaderstats" or "folder"

local function SaveData(player, data)
	local success, errorMessage = pcall(function()
		playerDataStore:SetAsync(player.UserId, data)
	end)

	if not success then
		warn(errorMessage)
	end
end

local function OnPlayerRemoving(player)
	local data = defaultData

	for folderName, folderValues in defaultData do
		local folder = player[folderName == "leaderstats" and leaderstatsName or folderName]

		for valueName, _ in folderValues do
			data[folderName][valueName] = folder[valueName].Value
		end
	end

	SaveData(player, data)
end

Players.PlayerAdded:Connect(function(player)
	local playerData = playerDataStore:GetAsync(player.UserId) or defaultData

	for folderName, folderValues in defaultData do
		local folder = Instance.new("Folder")
		folder.Name = folderName == "leaderstats" and leaderstatsName or folderName

		for valueName, _ in folderValues do
			local intValue = Instance.new("IntValue")
			intValue.Name = valueName
			intValue.Value = playerData[folderName][valueName]
			intValue.Parent = folder
		end
		
		folder.Parent = player
	end
end)

Players.PlayerRemoving:Connect(OnPlayerRemoving)

game:BindToClose(function()
	for i, player in Players:GetPlayers() do
		task.spawn(OnPlayerRemoving, player)
	end
end)

When i go to my second place, for example, though it doesn’t load my saved Part 1 values.
Here’s the code for the second place:

local Players = game:GetService("Players")
local DataStoreService = game:GetService("DataStoreService")
local playerDataStore = DataStoreService:GetDataStore("playerData")

local defaultData = {
	leaderstats = {
		["Deaths II"] = 0,
		["Escapes II"] = 0,
	},

	storedData = {
		["Deaths I"] = 0,
		["Escapes I"] = 0,
		["Deaths III"] = 0,
		["Escapes III"] = 0
	}
}

local leaderstatsName = game.PlaceId == myreal2ndplaceidishere and "leaderstats" or "folder"

local function SaveData(player, data)
	local success, errorMessage = pcall(function()
		playerDataStore:SetAsync(player.UserId, data)
	end)

	if not success then
		warn(errorMessage)
	end
end

local function OnPlayerRemoving(player)
	local data = defaultData

	for folderName, folderValues in defaultData do
		local folder = player[folderName == "leaderstats" and leaderstatsName or folderName]

		for valueName, _ in folderValues do
			data[folderName][valueName] = folder[valueName].Value
		end
	end

	SaveData(player, data)
end

Players.PlayerAdded:Connect(function(player)
	local playerData = playerDataStore:GetAsync(player.UserId) or defaultData

	for folderName, folderValues in defaultData do
		local folder = Instance.new("Folder")
		folder.Name = folderName == "leaderstats" and leaderstatsName or folderName

		for valueName, _ in folderValues do
			local intValue = Instance.new("IntValue")
			intValue.Name = valueName
			intValue.Value = playerData[folderName][valueName]
			intValue.Parent = folder
		end

		folder.Parent = player
	end
end)

Players.PlayerRemoving:Connect(OnPlayerRemoving)

game:BindToClose(function()
	for i, player in Players:GetPlayers() do
		task.spawn(OnPlayerRemoving, player)
	end
end)

Yeah, is that not what you wanted the script to do?

If you want everything to load, use the original script here:

That is what i want, but when i check the storedData folder inside of the Player, where Deaths1, Escapes1, Deaths3, & Escapes3 should all be, they’re in the folder but their values are all at zero, so they didn’t save from place 1 or place 3. I’m trying to get all of the data to transfer to the other places because in my Lobby place i have leaderboards for each of the different values.

Oh, I just realized you did it wrong. If you want the leaderstats to only show the current world’s count, then we have to do something different.

Code:

local Players = game:GetService("Players")
local DataStoreService = game:GetService("DataStoreService")
local playerDataStore = DataStoreService:GetDataStore("playerData")

local defaultData = {
	["Deaths I"] = 0,
	["Escapes I"] = 0,
	["Deaths II"] = 0,
	["Escapes II"] = 0,
	["Deaths III"] = 0,
	["Escapes III"] = 0
}

local leaderstatValues = {"Death I", "Escapes I"} -- Change this depending on level

local function SaveData(player, data)
	local success, errorMessage = pcall(function()
		playerDataStore:SetAsync(player.UserId, data)
	end)

	if not success then
		warn(errorMessage)
	end
end

local function OnPlayerRemoving(player)
	local leaderstats = player.leaderstats
	local storedData = player.storedData
	
	local data = {}

	for valueName, _ in defaultData do
		data[valueName] = table.find(leaderstatValues, valueName) and leaderstats[valueName].Value or storedData[valueName].Value
	end

	SaveData(player, data)
end

Players.PlayerAdded:Connect(function(player)
	local playerData = playerDataStore:GetAsync(player.UserId) or defaultData
	
	local leaderstats = Instance.new("Folder")
	leaderstats.Name = "leaderstats"
	
	local storedData = Instance.new("Folder")
	storedData.Name = "storedData"
	
	for valueName, _ in defaultData do
		local intValue = Instance.new("IntValue")
		intValue.Name = valueName
		intValue.Value = playerData[valueName]
		intValue.Parent = table.find(leaderstatValues, valueName) and leaderstats or storedData
	end
	
	leaderstats.Parent = player
	storedData.Parent = player
end)

Players.PlayerRemoving:Connect(OnPlayerRemoving)

game:BindToClose(function()
	for i, player in Players:GetPlayers() do
		task.spawn(OnPlayerRemoving, player)
	end
end)

You will have to reset your data again.

Do i need to reset everybody’s data as well? because there are a couple of other people that have tested my game out and they have data. Is there a way to reset all data? if that’s what i need to do?

You can use this in your command bar:

local DSS = game:GetService("DataStoreService")
local DataStore = DSS:GetDataStore("playerData")
local KeysPages = DataStore:ListKeysAsync()
		
while true do
	local CurrentKeysPage = KeysPages:GetCurrentPage()
			
	for j, Key in pairs(CurrentKeysPage) do
		DataStore:RemoveAsync(Key.KeyName)
	end
			
	if KeysPages.IsFinished then break end
			
	KeysPages:AdvanceToNextPageAsync()
end

If you want me to implement a way that preserves their data, tell me. There will be a performance cost though.

The data still isn’t saving or transferring.
Code:

local Players = game:GetService("Players")
local DataStoreService = game:GetService("DataStoreService")
local playerDataStore = DataStoreService:GetDataStore("playerData")

local defaultData = {
	["Deaths I"] = 0,
	["Escapes I"] = 0,
	["Deaths II"] = 0,
	["Escapes II"] = 0,
	["Deaths III"] = 0,
	["Escapes III"] = 0
}

local leaderstatValues = {"Deaths I", "Escapes I"} -- Change this depending on level

local function SaveData(player, data)
	local success, errorMessage = pcall(function()
		playerDataStore:SetAsync(player.UserId, data)
	end)

	if not success then
		warn(errorMessage)
	end
end

local function OnPlayerRemoving(player)
	local leaderstats = player.leaderstats
	local storedData = player.storedData

	local data = {}

	for valueName, _ in defaultData do
		data[valueName] = table.find(leaderstatValues, valueName) and leaderstats[valueName] or storedData[valueName]
	end

	SaveData(player, data)
end

Players.PlayerAdded:Connect(function(player)
	local playerData = playerDataStore:GetAsync(player.UserId) or defaultData

	local leaderstats = Instance.new("Folder")
	leaderstats.Name = "leaderstats"

	local storedData = Instance.new("Folder")
	storedData.Name = "storedData"

	for valueName, _ in defaultData do
		local intValue = Instance.new("IntValue")
		intValue.Name = valueName
		intValue.Value = playerData[valueName]
		intValue.Parent = table.find(leaderstatValues, valueName) and leaderstats or storedData
	end

	leaderstats.Parent = player
	storedData.Parent = player
end)

Players.PlayerRemoving:Connect(OnPlayerRemoving)

game:BindToClose(function()
	for i, player in Players:GetPlayers() do
		task.spawn(OnPlayerRemoving, player)
	end
end)

You’re testing this ingame right? If so, does it save when you leave and rejoin for place one?

Yes, i tested it ingame. It doesn’t save when i leave and rejoin place one.

Are there any errors or warnings?

You only ran the command once right?

I ran the command for myself a couple of times on accident lol. And i ran the one that resets all datastores once.
Here’s what’s in the Output:

  • DataStoreService: CantStoreValue: Cannot store Dictionary in data store. Data stores can only accept valid UTF-8 characters. API: SetAsync, Data Store: playerData - Studio
  • Cannot store Dictionary in data store. Data stores can only accept valid UTF-8 characters. - Server - Leaderstats/Datastore:22