StringValue not saving?

hi, i am making a game with a stringvalue that needs to be saved. whenever i change the value of the stringvalue, and I leave, the value sets back to the staring stringvalue’s value. any help?

script:

local currencyName = "tool"
local DataStore = game:GetService("DataStoreService"):GetDataStore("TestDataStore")
game.Players.PlayerAdded:Connect(function(player)
local currency = Instance.new("StringValue")
currency.Name = currencyName
currency.Parent = player

local ID = currencyName.."-"..player.UserId
local savedData = nil	

pcall(function()
	savedData = DataStore:GetAsync(ID)
end)
if savedData ~= nil then
	currency.Value = savedData
	print("Data loaded")
else
	currency.Value = "M4A1"
	print("New player to the game")
end
end)

game.Players.PlayerRemoving:Connect(function(player)
local ID = currencyName.."-"..player.UserId
DataStore:SetAsync(ID,player[currencyName].Value)
end)

game:BindToClose(function()
for i, player in pairs(game.Players:GetPlayers()) do
	if player then
		player:Kick("This game is shutting down")
	end
end

wait(5)	

end)

hi, its tough to figure out the issue without an error code so I recommend using the fields the pcall return which are success, and error. you should print the second parameter so we can get the error code.

So this block of code is the part that manages loading data, but also manages setting it to a default if the data is equal to ‘nil’.

Not sure what exactly is the value that it sets back to, but if you mean the default string value that you have set it for a new user, which is “M4A1”, then your pcall() that handles retrieving the player’s data is probably returning an error, its simply not caught in the call since it is an isolated pcall() with no variable it can return to.

If you are able to retrieve the 2 returned values that include the success boolean, you can catch it in your code and make it automatically attempt a recall or at least print out its error.

Example of a more reliable pcall() in a function:

function retrieveData()
    local success, response = pcall(function()
        savedData = DataStore:GetAsync(ID)
    end)

    if success == false then
        print("Data call success returned " .. success .. " retrying call ...")
        wait(.05)
        retrieveData()
    end
end

retrieveData()

You can get all the details of the pcall() method in Lua here: Lua Manual | pcall() Documentation

Hope this helps :+1: Make sure to mark this post as a solution if it does solve your problem, so other readers know that this topic has been resolved :slight_smile:

1 Like

Make sure you have API Services enabled as well as the fact that you’re not changing the player’s tool value from the client command bar. I tested the code and it works fine in-game.

I also recommend going with G8Max’s approach with how he handles the pcall.

1 Like

Just a edit to your function i would add a retries counter to it so just in case you encounter a particularly bad data store you dont exceed the limits
Heres an Edited Version

local Retires = 0
local MaxRetries = 5 -- anything you want

function retrieveData()
    local success, response = pcall(function()
        savedData = DataStore:GetAsync(ID)
    end)

    if success == false then
        Retries = Retries + 1
        print("Data call success returned " .. success .. " retrying call ...")
        wait(.05)
        if Retires < MaxRetries then
            retrieveData()
        elseif Retries >= MaxRetries then
            Retries = 0
        end
    end
end

retrieveData()
1 Like

Your issue is that you are not saving when the game shuts down, you only kick them.

And, I would also set up a loop that saves everyone’s data every X minutes.

local Players = game:GetService('Players')
local DataStoreService = game:GetService('DataStoreService')
local Key = DataStoreService:GetDataStore('DataStore')

local MaxTries = 3 -- Roblox allows you to attempt saving 3 times before the request gets rejected.
local Update_Time = 120 -- Every 120 seconds (every 2 minutes)

--[[ Create a thread that runs alone without affecting the rest of the script --]]

coroutine.create(coroutine.resume(function()
	-- Save their data with a loop. Example:
	
	local GetPlayers = Players:GetPlayers()
	for i = 1, #GetPlayers do
		local Target = GetPlayers[i]
		
		-- Usage: Target.UserId, Target is the player instance.
		
		-- Using numerical loops is actually faster in the long-run,
		-- assigning local variables is also faster when accessing them the 2nd time and beyond.
	end
end))


--[[ LOAD DATA --]]

Players.PlayerAdded:Connect(function(Player)
	local Ok, Result = pcall(function()
		return Key:GetAsync(Player.UserId)
	end)
	if Ok then
		if Result then
			print('Currently saved:', Result)
			
		else
			print('No data has been saved for this player yet!')
		end
		
	else
		print('Error while retrieving data as DataStores are currently down:', Result)
	end
end)


--[[ SAVE DATA--]]

Players.PlayerRemoving:Connect(function(Player)
	local Ok, Result
	local Tries = 0
	
	repeat
		Ok, Result = pcall(function()
			Key:UpdateAsync(Player.UserId, function(CurrentValue)
				return 123
				-- Whatever value you need to store. CurrentValue returns their current saved data (if any, otherwise it's nil).
			end)
		end)
		if not Ok then
			wait(1)
		end
	until Tries == MaxTries or Ok
	
	-- The reason I use a repeat loop is because Roblox sometimes rejects the request, this is not an issue when getting or removing data.
	-- You are allowed a "retry" 3 times before Roblox rejects the request.
	
	if not Ok then
		print('Error while saving data:', Result)
	end
end)

Disclaimer: I am giving you pseudocode, which means a notation resembling a simplified programming language, used in program design.

You may get a better understanding with the given code.

Useful links
DataStore limits: Documentation - Roblox Creator Hub
Roblox’s example of DataStore: Data Stores | Documentation - Roblox Creator Hub

1 Like

it would probably be better to use game:BindToClose() instead of saving in intervals

You can do both, I do recommend both.
This is just pseudocode.

i found out the problem I guess. IDK i changed the script and changed the value from the script and it loads now, thanks to everyone for helping.

script:

local DataStoreService = game:GetService("DataStoreService")
local PlayerCash = DataStoreService:GetDataStore("PlayerCash")

function OnPlayerAdded(player)
local stats = Instance.new("Folder", player)
stats.Name = 'leaderstats'
local Cash = Instance.new("StringValue", stats)
Cash.Name = "$"
Cash.Value = "yo"
local data
local success, err = pcall(function()
	data = PlayerCash:GetAsync(player.UserId)
end)
if success then
	print("Data loaded!")
	if data then
		Cash.Value = data
	end
else
	print("There was an error while getting data of player " .. player.Name)
	warn(err)
end
while wait(10) do
	Cash.Value = "LOL"
end
end

function OnPlayerRemoving(player)
local success, err = pcall(function()
	PlayerCash:SetAsync(player.UserId, player.leaderstats:FindFirstChild("$").Value)
end)
if success then
	print("Data saved!")
else
	print("There was an error while saving data of player " .. player.Name)
	warn(err)
end
end

game.Players.PlayerAdded:Connect(OnPlayerAdded)
game.Players.PlayerRemoving:Connect(OnPlayerRemoving)
1 Like