Issue with Saving Player Data in Roblox Studio

What do you want to achieve?
I want to save player data in my game, - clicks, gems, rebirths and multiplier -, so that players can continue from where they left off when they rejoin the game.

What is the issue?
I have written a script to save player data, but it is only saving the gems and not the rest of the values. I am not sure what the issue is and I have tried a few solutions without success.

What solutions have you tried so far?
I have checked the script for any obvious errors, and I have also looked for solutions on the Developer Hub, but I have not found anything that solves my problem. I have also tried modifying the script to include the clicks, rebirths and multipliers values in the data table that is being saved, but it still only saves the gems value.

The script here:

local DataStoreService = game:GetService("DataStoreService")
local dataStore = DataStoreService:GetDataStore("data")

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

	local clicks = Instance.new("IntValue")
	clicks.Name = "Clicks"
	clicks.Parent = leaderstats

	local gems = Instance.new("IntValue")
	gems.Name = "Gems"
	gems.Parent = leaderstats

	local rebirths = Instance.new("IntValue")
	rebirths.Name = "Rebirths"
	rebirths.Parent = leaderstats

	local multiplier = Instance.new("IntValue")
	multiplier.Name = "Multiplier"
	multiplier.Parent = player

	local data = {}
	local success, errormessage = pcall(function()
		data = dataStore:GetAsync(player.UserId.."-data") or {}
	end)
	if success then
		clicks.Value = data.clicks or 0
		gems.Value = data.gems or 0
		rebirths.Value = data.rebirths or 0
		multiplier.Value = data.multiplier or 0
	else
		warn(errormessage)
	end
end)

game.Players.PlayerRemoving:Connect(function(player)
	local currentPlayer = game.Players:GetPlayerByUserId(player.UserId)
	local success, errormessage = pcall(function()
		local data = {
			clicks = currentPlayer.leaderstats.Clicks.Value,
			gems = currentPlayer.leaderstats.Gems.Value,
			rebirths = currentPlayer.leaderstats.Rebirths.Value,
			multiplier = currentPlayer.Multiplier.Value,
		}
		dataStore:SetAsync(player.UserId.."-data", data)
	end)
	if not success then
		warn(errormessage)
	end
end)

game.ReplicatedStorage.RebirthEvent.OnServerEvent:Connect(function(player)
	local leaderstats = player.leaderstats
	local multiplier = player.Multiplier
	leaderstats.Clicks.Value = 0
	leaderstats.Rebirths.Value += 1
	multiplier.Value += 2
end)

game:BindToClose(function()
	wait(3)
end)

I don’t think there’s a need to get the player again by their UserId, why not use the player object in the parameters?

1 Like

You’re right! Thank you for pointing this out! But that doesn’t fix my issue unfortunately. =/

Could also be this, why are you waiting 3 seconds after the game is closing?
This doesn’t yield the script, though.

Maybe, but with and without it, nothing changes.

Did you try publishing the game to Roblox and then joining the game through roblox instead of studio?

1 Like

An easy way to see what the values are is to print what they are when the player leaves. This would tell you if the values aren’t being calculated properly before the player leaves.

		local data = {
			clicks = currentPlayer.leaderstats.Clicks.Value,
			gems = currentPlayer.leaderstats.Gems.Value,
			rebirths = currentPlayer.leaderstats.Rebirths.Value,
			multiplier = currentPlayer.Multiplier.Value,
		}
        print(clicks, ", ", gems, ", ", rebirths, ", ", multiplier)
1 Like

Yes, I already tried, but it didn’t worked as well.

It gives an attempt to call a nil value. So it’s probably not being calculated correctly when the player leaves…

1 Like

BindToClose will fire this code when the Server closes, In Studio, The game will fire this callback, and then close, so in this case, it will yield for roughly 3 seconds, and then close.

1 Like

Well personally, I would write things like that when making changes to variables.

game.ReplicatedStorage.RebirthEvent.OnServerEvent:Connect(function(player)
	local leaderstats = player.leaderstats
	local multiplier = player.Multiplier
	leaderstats.Clicks.Value = 0
	leaderstats.Rebirths.Value += 1
	multiplier.Value += 2
end)

I would write that as:

game.ReplicatedStorage.RebirthEvent.OnServerEvent:Connect(function(player)
	player.leaderstats.Clicks.Value = 0
	player.leaderstats.Rebirths.Value += 1
	player.leaderstats.Multiplier.Value += 2
end)

I ran into a similar issue with local scripts as you can do it your method you have to re-state that player.leaderstats.Rebirths.Value = (whatever local value you made up). Because in a sense that value is stored locally. Also it looks like you watched CodeGnome, as its very similar to my first database script, but you did not set the data of the session.
For example when you have a datastore and you make changes with local variables leaderstats never changes a thing, I found this out because I didn’t watch his video all the way through haha.
Here is an example:

local Players = game:GetService("Players")
local DataStoreService = game:GetService("DataStoreService")
local database = DataStoreService:GetDataStore("playerDatastore")
local sessionData = {}

function PlayerAdded(Player)
	--Leaderstats
	local leaderstats = Instance.new("Folder", Player)
	leaderstats.Name = "leaderstats"

	local Gold = Instance.new("IntValue", leaderstats)
	Gold.Name = "Gold"
	Gold.Parent = leaderstats
local success = nil
	local playerData = nil
	local attempt = 1

	repeat
		success, playerData = pcall(function()
			return database:GetAsync(Player.UserId)
		end)

		attempt += 1
		if not success then 
			warn (playerData)
			task.wait(3)
		end
	until success or attempt == 5
if success then
		print("Connected to database")
		if not playerData then
			print("Assigning default data")
			playerData = {
				["Gold"] = 5,	
                                -- put your stats here
			}
		end
-- here is where I feel like you aren't setting the values stored in the table BACK into the table.
sessionData[Player.UserId] = playerData
else
		warn("Unable to get data for", Player.UserId)
		Player:Kick("Unable to load your data. Try again later")
	end
	for i, val in pairs(playerData) do
		print(val)
	end

	Gold.Value = sessionData[Player.UserId].Gold
	Gold.Changed:Connect(function()
		sessionData[Player.UserId].Gold = Gold.Value
	end)

What your code does when I read it is it just simply changes the variables, prints it changed them, but never went back and set the values again. I hope this helps as your code is very nice and I just feel you left out that little bit. CodeGnome made a video on this for reference and I take zero credit for the code above. Also do not copy and paste that code, as it is not the complete database script. It will not work.

1 Like

I realized that the problem with my code wasn’t related to the datastore. Although your suggestions helped improve my datastore implementation, the code was still only saving the one value. Upon further investigation, I discovered that local scripts were the cause of the issue. As local scripts only run on the client, they don’t replicate any changes made to the leaderstats values to the server. To fix this, I edited my local scripts a bit and used RemoteEvents, which allowed it to update the leaderstats values and save them to the datastore from the server. Overall, your suggestions and some additional troubleshooting helped me find a solution to my problem. Thank you for your help!

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