Datastore Issue

I’m making something similar to a minigame where when the player finishes the obby, it adds 50 points and 1 win into the player’s leaderstats. I want to make it so that these stats save and load properly whenever a player leaves and rejoins the game.

The stats either don’t save/load properly. For example, before I leave the game, I would have 200 points and 4 wins, but when I rejoin, I would only have 4 points and 4 wins. I tried using a pcall function, and it says that the data saved successfully.

This is the leaderstats script:

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

game.Players.PlayerAdded:Connect(function(plr)
	
	local leaderstats = Instance.new("Folder")
	leaderstats.Name = "leaderstats"
	leaderstats.Parent = plr
	
	local val = Instance.new("IntValue")
	val.Name = "Points"
	val.Value = dataStore:GetAsync(plr.UserId) or 0
	val.Parent = leaderstats
	
	local val = Instance.new("IntValue")
	val.Name = "Wins"
	val.Value = dataStore:GetAsync(plr.UserId) or 0
	val.Parent = leaderstats
	
end)

game.Players.PlayerRemoving:Connect(function(plr)
	
	local success, errormessage = pcall(function()
		dataStore:SetAsync(plr.UserId, plr.leaderstats.Points.Value)
		dataStore:SetAsync(plr.UserId, plr.leaderstats.Wins.Value)
	end)
	
	if success then
		print ("Player Data successfully saved!")
	else
		print ("There was an error when saving data.")
		warn (errormessage)
	end
	
end)

This is the script that increases the wins and points of the player who finished the obby first:

db = false
script.Parent.Touched:connect(function(hit)
    if hit.Parent:FindFirstChild("Humanoid") then
        local player = game.Players:GetPlayerFromCharacter(hit.Parent)
        if db == false then
            db = true
            player.leaderstats.Wins.Value = player.leaderstats.Wins.Value +1
            player.leaderstats.Points.Value = player.leaderstats.Points.Value +50
        end
    end
end)

Explorer

Okay, you are using the same name to define a variable in two different ways. Change the second local val to something different

1 Like

I haven’t touched Datastores in a while. But I think I found your issue.

Try setting your values into a dictionary. Then, save the Dictionary with the SetAsync call. From what I can see, it seems you’re overriding the player data with a single value on these lines.

dataStore:SetAsync(plr.UserId, plr.leaderstats.Points.Value) – You’re setting plr.UserId with Point.Value
dataStore:SetAsync(plr.UserId, plr.leaderstats.Wins.Value) – You’re overriding the plr.UserId data with Wins.Value data.

Try this instead.

local data = {Wins = player.leaderstats.Wins.Value , Points = player.leaderstats.Points.Value};
dataStore:SetAsync(plr.UserId, data);

When you load the data, it would be:

local data = dataStore:GetAsync(plr.UserId);

local val = Instance.new("IntValue")
val.Name = "Points"
val.Value = data.Points
val.Parent = leaderstats
	
val = Instance.new("IntValue")
val.Name = "Wins"
val.Value = data.Wins
val.Parent = leaderstats
2 Likes

That wouldn’t matter. Since he’s overriding the val variable with a new instance.

1 Like

Yeah, if he’s overriding the variable, then he will only ever get 1 Value, because it gets overridden, as far as I know. At least to my knowledge, in this case he will have two intvalues with the same name and value, because he has chosen the same name. Unless him defining local Val stopped the script from using the previous val(which it could’ve I’m not too sure)

1 Like

No, he still gets 2 objects from it.

When he initially creates the first val object. He initializes it with a Instance.new(“IntValue”). In the computer memory, a new 4 byte memory block (Size of an integer in memory) has been reserved for “Points”. When he parents Points to leaderstats, leaderstats will start tracking the object.

Then, he initializes val to a new Instance.new(“IntValue”). So, a new 4 byte memory block is allocated in computer memory. Since leaderstats has the “Points” IntValue parented to it. It will not get picked up by the garbage collector and deleted. However, he does lose the reference to “Points”.

local val = Instance.new("IntValue") -- Creates new IntValue
val.Name = "Points"
val.Value = data.Points
val.Parent = leaderstats -- leaderstats now has this IntValue parented to it.
	
val = Instance.new("IntValue") -- Loses the reference to the "Points" IntValue and sets it to a new IntValue instance.
val.Name = "Wins"
val.Value = data.Wins
val.Parent = leaderstats -- Parents it to the leaderboard, but still has a reference to the "Wins" IntValue with the val variable
3 Likes

But if he didn’t parent “Points” to the leaderstats, then your argument is valid. “Points” would not have been used by anything and the garbage collector would delete it.

Since he’d be overriding it with “Wins” and would lose the reference to “Points”.

1 Like

I just found out that I have another problem, the datastore only worked for myself since I already had data before, but when new players join, their leaderstats show up as - for points and - for wins instead of saying 0. I’m looking in the console and it says

ServerScriptService.leaderstats:13: attempt to index nil with ‘Points’
Stack Begin
Script ‘ServerScriptService.leaderstats’, Line 13
Stack End

and when they leave the game, it states, “Wins is not a valid member of Folder”

This is my current script, I might’ve did something wrong:

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

game.Players.PlayerAdded:Connect(function(plr)
	
	local data = dataStore:GetAsync(plr.UserId);
	
	local leaderstats = Instance.new("Folder")
	leaderstats.Name = "leaderstats"
	leaderstats.Parent = plr
	
	local val = Instance.new("IntValue")
	val.Name = "Points"
	val.Value = data.Points
	val.Parent = leaderstats
	
	local val = Instance.new("IntValue")
	val.Name = "Wins"
	val.Value = data.Wins
	val.Parent = leaderstats
	
end)

game.Players.PlayerRemoving:Connect(function(plr)
	
	local success, errormessage = pcall(function()
		local data = {Wins = plr.leaderstats.Wins.Value , Points = plr.leaderstats.Points.Value};dataStore:SetAsync(plr.UserId, data);
	end)
	
	if success then
		print ("Player Data successfully saved!")
	else
		print ("There was an error when saving data.")
		warn (errormessage)
	end
	
end)

You still need to check if the joined player has data. If you don’t, the new player will have no data which results in a nil exception.

When a player joins, check if the data is nil by doing the following:

    local data = dataStore:GetAsync(plr.UserId);
	
    if (data == nil) then -- Check if the data exists.
        data = {Wins = 0 , Points = 0}; -- Default new data values
        dataStore:SetAsync(plr.UserId, data); -- Create their first save data, just in case you want to save data later in the game.
    end

	local leaderstats = Instance.new("Folder")
	leaderstats.Name = "leaderstats"
	leaderstats.Parent = plr
	
	local val = Instance.new("IntValue")
	val.Name = "Points"
	val.Value = data.Points
	val.Parent = leaderstats
	
	local val = Instance.new("IntValue")
	val.Name = "Wins"
	val.Value = data.Wins
	val.Parent = leaderstats

I recommend placing the data array table as a variable and not inside a function. That way, you can simply change the array and all variable references will point to the same table. Instead of having to change every table manually throughout the code. It’s prone for less error. e.g:

local GameData = {Wins = 0, Points = 0};

game.Players.PlayerAdded:Connect(function(plr)
    if (data == nil) then -- Check if the data exists.
        data = GameData; -- Default new data values
        dataStore:SetAsync(plr.UserId, data); -- Create their first save data, just in case you want to save data later in the game.
    end
end
2 Likes

Thank you for your help! Everything seems to be working now.

1 Like