DataStore won't save

So, I had a DataStore system in my game, which was working fine. Until today, when I tested to see if the in-game currency that I made would save, it didn’t. I looked at a few posts regarding this same issue, but none of the answers worked for me. Here’s the Data Store script im using.

(P.S. yes, api services are enabled, I double checked that)

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("NumberValue", stats)
	Cash.Name = "Credits"
	Cash.Value = 0
	local data
	local success, err = pcall(function()
		data = PlayerCash:GetAsync(tostring(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
end

function OnPlayerRemoving(player)
	local success, err = pcall(function()
		PlayerCash:UpdateAsync(player.UserId, player.leaderstats:FindFirstChild("Credits").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)

It might be because you are using tostring() in your GetAsync() but not in your UpdateAsync() to index the key.

If not, what does it print? Is there any error messages?

No error messages or anything in console, other than the data loading, both with or without tostring() (in both GetAsync() and UpdateAsync())

Oo… I looked up UpdateAsync() and you need to provide it with a function, not a value :neutral_face:, so you should use SetAsync() instead, or make it a function. Using UpdateAsync() is only really needed if more than one server will be changing the value at a time, which probably won’t be the case here. Here is an article explaining how to use UpdateAsync().

Using SetAsync() seems to have worked. Thanks!

Although kosava solved the main problem, there are still flaws in your code.

  1. You don’t check for already loaded players (which means there is a chance the player’s data will not load)
  2. You don’t manage BindToClose, which is fired whenever your game shuts down.
  3. You don’t retry any of the DataStore calls (GetAsync, UpdateAsync) whenever failed.
  4. In onPlayerRemoving, FindFirstChild for the leaderstats folder seems redundant

5. MAJOR MAJOR PROBLEM!! Your DataStore should be saved as a table so that you can add multiple values to it, not only cash. If players have already had dataStores, then you’re going to have to migrate your current data stores.

The code below solves problems 1 and 2:

local function onPlayerAdded(player)
  -- dataStore loading code
end

local function onPlayerRemoving(player)
  -- dataStore saving code
end

for _, player in ipairs(Players:GetPlayers()) do
  coroutine.wrap(onPlayerAdded)(player)
end

Players.PlayerAdded:Connect(onPlayerAdded)
Players.PlayerRemoving:Connect(onPlayerRemoving)

game:BindToClose(function()
  for _, player in ipairs(Players:GetPlayers()) do
    coroutine.wrap(onPlayerRemoving)(player)
  end
end)

Ah, okay. Using the code you gave for problems 1 and 2, should I make Players into game.Players, because Players isn’t a variable yet?

1 Like

Yeah, I forgot to add a variable for players lol

1 Like

Alright. For problem 4, I’ll remove the FindFirstChild, giving me

	PlayerCash:SetAsync(tostring(player.UserId), player.leaderstats.Credits.Value)

For problem 3, would I just add the above line of code into the error part of my if statement?

I’m not sure what you mean, but this is how roblox does their retrying code (on the wiki):

local tries = 0	
local success
repeat
	tries = tries + 1
	success = pcall(function()
		playerData:SetAsync(playerUserId, DATA_HERE)
	end)
	
    if not success then wait(1) end
until tries == 3 or success
	if not success then
		warn("Cannot save data for player!")
    end

Alright, tried my best to implement it into my code. I ended up with:

local tries = 0

local function onPlayerRemoving(player)
	repeat
		tries = tries + 1
		local success, err = pcall(function()
			PlayerCash:SetAsync(tostring(player.UserId), player.leaderstats.Credits.Value)
		end)

		if success then
			print("Data saved!")
		else
			print("There was an error while saving data of player " .. player.Name)
			warn(err)
		end
	until tries == 3 or success
end

Make sure to move the tries inside the onPlayerRemoving or else it will count retries for all players.

1 Like

Late reply to this thread, but how exactly do I implement a table into a DataSave? I don’t use tables often nor do I script DataSaves often, so I’m just wondering.

Fun fact, don’t use SetAsync() to save data as it can override any existing entries. Instead, use UpdateAsync().