NumberValue will save in leaderstats, but not as a value inside player when I rejoin [SOLVED]

@Maelstorm_1973, this is what the script looks like currently:

local DataStoreService = game:GetService("DataStoreService")
local playerData = DataStoreService:GetDataStore("PlayerData")

local function onPlayerJoin(player) 
	local leaderstats = Instance.new("Folder")  --Sets up leaderstats folder
	leaderstats.Name = "leaderstats"
	leaderstats.Parent = player
    --other leaderstats go here
    local upgrades = Instance.new("NumberValue") 
	upgrades.Name = "Multiplier"
	upgrades.Parent = player
    
    local playerUserId = player.UserId.."_Player"
	local data = playerData:GetAsync(playerUserId)  

	if data then
		--other leaderstats go here
		upgrades.Value = data['Multiplier']
		if upgrades.Value <= 0 then
			upgrades.Value = 1
		end
	else
		--other leaderstats go here
		upgrades.Value = 1
	end
end

local function create_table(player)
	local player_stats = {
		leaderboard = {};
		data = {};
	}

	for _, stat in pairs(player:GetChildren()) do
		if stat:IsA("NumberValue") then
			player_stats.data[stat.Name] = stat.Value
		end
	end

	
	for _, stat in pairs(player.leaderstats:GetChildren()) do
		player_stats.leaderboard[stat.Name] = stat.Value
	end

	return player_stats
end

local function onPlayerExit(player)  
	local player_stats = create_table(player)
	local success, err = pcall(function()
		local playerUserId = player.UserId.."_Player"
		playerData:SetAsync(playerUserId, player_stats) 
	end)

	if not success then
		warn('Could not save data!')
	end
end

game.Players.PlayerAdded:Connect(onPlayerJoin)
game.Players.PlayerRemoving:Connect(onPlayerExit)
game:BindToClose(function()
	for i, player in pairs(game.Players:GetChildren()) do
		onPlayerExit(player)
	end
end)

Not sure where I should add the createDataValues() code.

If you need the other stats I can add them in.

1 Like

You should print what value is data["Multiplier"]. You should also remove this statement:

	for _, stat in pairs(player:GetChildren()) do
		if stat:IsA("NumberValue") then
			player_stats.data[stat.Name] = stat.Value
		end
	end

^ That is unnecessary since the number values are stored in the leaderstats folder.

If the Multiplier is stored inside the player, is that not required?

I’m trying to save the Multiplier value inside the player and not as a value, in the Leaderstats Folder.

1 Like

In general, if you’re confused about why some variable doesn’t have a value you expect it to have, it’s very helpful to turn on the Watch window in studio, set a breakpoint at the start of your script, and then step through the code line by line and watching all the variable values to ensure they behave as expected. You would have solved this much faster that way.

2 Likes

What do you mean by that?

The problem currently with the script is that it doesn’t save the Multiplier value, when it is inside the player. @Maelstorm_1973, has already recommended using the createDataValues() function within the script, but I am not sure what this would look like, when it is incorporated within my current script.

1 Like

That is incorrect. If you look at his player joining code, it is clearly parenting the multiplier value to the player instead of the leaderboard.

1 Like

r u changing through localscripts

1 Like

Specifically changing what through localscripts?


Edit: The NumberValue is changed through a script.

1 Like

the numbervalue

1 Like

Put the createDataValues() above the onPlayerJoin() function. Then after you create the leaderstats folder and read the data from the DataStore, call createDataValues() and it will create the data structure. However, if it’s a new player, then data will be nil and you will have to manually create the data structure, but you can place that code in a separate function. Here’s some rough pseudo code:

-- Creates and populates a new number value and parents
-- it to the instance specified by parent.
local function createNumberValue(name, value, parent)
	local nva = Instance.new("NumberValue")
	nva.Name = name
	nva.Value = value
	nva.Parent = parent
end

-- Creates a new data tree if the player is new to the game.
local function newDataValues(player)
	-- New Leaderstats Data
	createValue("Score", 0, player.leaderstats)
	
	-- New Player Data
	createValue("Multiplier", 1, player)
end

-- Recreates the data tree from the given table.
local function loadDataValues(player, stats)
	for name, value in pairs(stats.leaderstats) do
		createNumberValue(name, value, player.leaderstats)
	end
	for name, value in pairs(stats.data) do
		createNumberValue(name, value, player)
	end
end

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

	-- Attempt to read player data from datastore.
	local key = player.UserId
	local data
	local success, errorMessage = pcall(function()
		data = playerData:GetAsync(key)
	end)
	if success == true then
		if data == nil then
			newDataValues(player)
		else
			loadDataValues(player, data)
		end
	else
		warn("Player data was not loaded:", player.Name, player.UserId, errorMessage)
		player:Kick("There was an error loading your data.  " ..
			"In order to prevent your data from being corrupted, " ..
			"you have been removed from the game. If this problem " ..
			"continues, please contact Roblox or the game developer " ..
			"for assistance.")
		return
	end
end

That should do it. I wrote this code from scratch, so there might be some errors in it.

1 Like

Would that be under the OnPlayerJoin() function?

Nevermind, it was the same scripts but just combined. Is the script that you provided, the whole script?

1 Like

I just modified my code and added some stuff to it. Check it out. What do you mean under onPlayerJoin()? The code that I have you can use verbatim, but modified to your requirements. createDataValues was renamed to loadDataValues() to better reflect what the function does.

EDIT: Fixed a couple of errors that I found.

EDIT: I should mention that anytime that you are dealing with the DataStore, you want to wrap the API calls in a pcall. Since the DataStore service is accessed over a network, it can fail. Without the pcall wrapper, an error can kill the server (depending on how you architect your code) because your code will just flat stop.

1 Like

What about the create_table(), OnPlayerExit(), and BindToClose() functions? Do I keep those?

local DataStoreService = game:GetService("DataStoreService")
local playerData = DataStoreService:GetDataStore("PlayerData")

local function onPlayerJoin(player) 
	local leaderstats = Instance.new("Folder")  --Sets up leaderstats folder
	leaderstats.Name = "leaderstats"
	leaderstats.Parent = player
    --other leaderstats go here
    local upgrades = Instance.new("NumberValue") 
	upgrades.Name = "Multiplier"
	upgrades.Parent = player
    
    local playerUserId = player.UserId.."_Player"
	local data = playerData:GetAsync(playerUserId)  

	if data then
		--other leaderstats go here
		upgrades.Value = data['Multiplier']
		if upgrades.Value <= 0 then
			upgrades.Value = 1
		end
	else
		--other leaderstats go here
		upgrades.Value = 1
	end
end

local function create_table(player)
	local player_stats = {
		leaderboard = {};
		data = {};
	}

	for _, stat in pairs(player:GetChildren()) do
		if stat:IsA("NumberValue") then
			player_stats.data[stat.Name] = stat.Value
		end
	end

	
	for _, stat in pairs(player.leaderstats:GetChildren()) do
		player_stats.leaderboard[stat.Name] = stat.Value
	end

	return player_stats
end

local function onPlayerExit(player)  
	local player_stats = create_table(player)
	local success, err = pcall(function()
		local playerUserId = player.UserId.."_Player"
		playerData:SetAsync(playerUserId, player_stats) 
	end)

	if not success then
		warn('Could not save data!')
	end
end

game.Players.PlayerAdded:Connect(onPlayerJoin)
game.Players.PlayerRemoving:Connect(onPlayerExit)
game:BindToClose(function()
	for i, player in pairs(game.Players:GetChildren()) do
		onPlayerExit(player)
	end
end)

Yes, you keep those.

30charrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrssssssssssssssssssss

There are currently 96 lines of code. Is this code okay?:

local DataStoreService = game:GetService("DataStoreService")
local playerData = DataStoreService:GetDataStore("PlayerData")

local function createValue(name, value, parent)
	local nva = Instance.new("NumberValue")
	nva.Name = "Multiplier"
	nva.Value = 1
	nva.Parent = players
end

-- Creates a new data tree if the player is new to the game.
local function newDataValues(player)
	-- New Leaderstats Data
	createValue("Score", 0, player.leaderstats)

	-- New Player Data
	createValue("Multiplier", 1, player)
end

-- Recreates the data tree from the given table.
local function loadDataValues(player, stats)
	for name, value in pairs(stats.leaderstats) do
		createNumberValue(name, value, player.leaderstats)
	end
	for name, value in pairs(stats.data) do
		createNumberValue(name, value, player)
	end
end

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

	-- Attempt to read player data from datastore.
	local key = player.UserId
	local data
	local success, errorMessage = pcall(function()
		data = playerData:GetAsync(key)
	end)
	if success == true then
		if data == nil then
			newDataValues(player)
		else
			loadDataValues(player, data)
		end
	else
		warn("Player data was not loaded:", player.Name, player.UserId, errorMessage)
		player:Kick("There was an error loading your data.  " ..
			"In order to prevent your data from being corrupted, " ..
			"you have been removed from the game. If this problem " ..
			"continues, please contact Roblox or the game developer " ..
			"for assistance.")
		return
	end
end

local function create_table(player)
	local player_stats = {
		leaderboard = {};
		data = {};
	}

	for _, stat in pairs(player:GetChildren()) do
		if stat:IsA("NumberValue") then
			player_stats.data[stat.Name] = stat.Value
		end
	end

	
	for _, stat in pairs(player.leaderstats:GetChildren()) do
		player_stats.leaderboard[stat.Name] = stat.Value
	end

	return player_stats
end

local function onPlayerExit(player)  
	local player_stats = create_table(player)
	local success, err = pcall(function()
		local playerUserId = player.UserId.."_Player"
		playerData:SetAsync(playerUserId, player_stats) 
	end)

	if not success then
		warn('Could not save data!')
	end
end

game.Players.PlayerAdded:Connect(onPlayerJoin)
game.Players.PlayerRemoving:Connect(onPlayerExit)
game:BindToClose(function()
	for i, player in pairs(game.Players:GetChildren()) do
		onPlayerExit(player)
	end
end)

I tried to specify the parent, but the variable hasn’t been defined.

nva.Parent = players

Also in the script, the createNumberValue(), has a blue line under it.

local function loadDataValues(player, stats)
	for name, value in pairs(stats.leaderstats) do
		createNumberValue(name, value, player.leaderstats)
	end
	for name, value in pairs(stats.data) do
		createNumberValue(name, value, player)
	end
end

I think the script might be too complicated for my needs. I only need it to save the Multiplier value’s data, which is from inside the player.

It’s parent, not players. As for createNumberValue, I forgot to change the name in the script. I’ll take care of it right now.

EDIT: Fixed.

nva.Parent = players is wrong. It needs to be nva.Parent = parent as I have in the script.

Complex? 96 lines of code? That is nothing. I have script files with over 5000 lines of code. My game has over 50,000 lines of code. Welcome to the wonderful world of software engineering. Code is only going to get more complex as you move on in your coding career.

1 Like

I am surprised that saving it inside the player rather than in the Leaderstats Folder is more difficult. For saving it in leaderstats it seems to be about half as lines long, with less functions, etcetera. The script can also be more reliable if you try to keep the number of lines down to a minimum, and I find that scripts which are shorter are more likely to work. Nonetheless, of course that does not compare to some other scripts which have loads of lines, of which are very much necessary.

Also, is kicking them from the game, if it doesn’t load in their data an effective way of preventing their data from being lost?

Am I doing something wrong here? If so, hopefully it is fixable:

local DataStoreService = game:GetService("DataStoreService")
local playerData = DataStoreService:GetDataStore("PlayerData")

-- Creates and populates a new number value and parents
-- it to the instance specified by parent.
local function createNumberValue(name, value, parent)
	local nva = Instance.new("NumberValue")
	nva.Name = "Multiplier"
	nva.Value = 1
	nva.Parent = parent
end

-- Creates a new data tree if the player is new to the game.
local function newDataValues(player)
	-- New Leaderstats Data
	createValue("Score", 0, player.leaderstats)

	-- New Player Data
	createValue("Multiplier", 1, player)
end

-- Recreates the data tree from the given table.
local function loadDataValues(player, stats)
	for name, value in pairs(stats.leaderstats) do
		createNumberValue(name, value, player.leaderstats)
	end
	for name, value in pairs(stats.data) do
		createNumberValue(name, value, player)
	end
end

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

	-- Attempt to read player data from datastore.
	local key = player.UserId
	local data
	local success, errorMessage = pcall(function()
		data = playerData:GetAsync(key)
	end)
	if success == true then
		if data == nil then
			newDataValues(player)
		else
			loadDataValues(player, data)
		end
	else
		warn("Player data was not loaded:", player.Name, player.UserId, errorMessage)
		player:Kick("There was an error loading your data.  " ..
			"In order to prevent your data from being corrupted, " ..
			"you have been removed from the game. If this problem " ..
			"continues, please contact Roblox or the game developer " ..
			"for assistance.")
		return
	end
end

local function create_table(player)
	local player_stats = {
		leaderboard = {};
		data = {};
	}

	for _, stat in pairs(player:GetChildren()) do
		if stat:IsA("NumberValue") then
			player_stats.data[stat.Name] = stat.Value
		end
	end

	
	for _, stat in pairs(player.leaderstats:GetChildren()) do
		player_stats.leaderboard[stat.Name] = stat.Value
	end

	return player_stats
end

local function onPlayerExit(player)  
	local player_stats = create_table(player)
	local success, err = pcall(function()
		local playerUserId = player.UserId.."_Player"
		playerData:SetAsync(playerUserId, player_stats) 
	end)

	if not success then
		warn('Could not save data!')
	end
end

game.Players.PlayerAdded:Connect(onPlayerJoin)
game.Players.PlayerRemoving:Connect(onPlayerExit)
game:BindToClose(function()
	for i, player in pairs(game.Players:GetChildren()) do
		onPlayerExit(player)
	end
end)

createValue() is still underlined in blue in the script. It needs to be specified:

local function newDataValues(player)
	-- New Leaderstats Data
	createValue("Score", 0, player.leaderstats)

	-- New Player Data
	createValue("Multiplier", 1, player)
end

The Multiplier (NumberValue), doesn’t seem to appear inside the player, when I try and test the game.

Additionally, I also get an error from the Output:

  22:06:05.789  ServerScriptService.Leaderstats Folder.Leaderstats:16: attempt to call a nil value  -  Server - Leaderstats:16

I did say that I fixed that problem on line 16. Go back and look at post 33 in the thread. If their data does not load, if they continue to play, it will be as a new player so they will lose their old data…which brings me to some additional code that is required that I just thought about…

local kickPlayer = {}


		kickPlayer[key] = true
		warn("Player data was not loaded:", player.Name, player.UserId, errorMessage)
		player:Kick("There was an error loading your data.  " ..
			"In order to prevent your data from being corrupted, " ..
			"you have been removed from the game. If this problem " ..
			"continues, please contact Roblox or the game developer " ..
			"for assistance.")
		return


local function onPlayerExit(player) 
	if kickPlayer[player.UserId] ~= nil then
		return
	end
	local player_stats = create_table(player)
	local success, err = pcall(function()
		local playerUserId = player.UserId.."_Player"
		playerData:SetAsync(playerUserId, player_stats) 
	end)

	if not success then
		warn('Could not save data!')
	end
end

That will prevent the data from the player getting saved which prevents overwriting their old data.

@Maelstorm_1973 Can you show me a link to that post, on your latest post? Post 33 was a post that I made.