Sometimes players lose some of their data

Hello everyone,

Sometimes I get reports of players saying they have lost some of their data (like an item they bought in game). I can’t figure out why this happens and it frustrates me a lot.

I have to mention that I never shutdown any server before doing an update to the game. Could this be the cause of dataloss? Or is the way I am saving data the problem?

Below here is a brief example of how I save data. In this example I save Energy and the inventory of a player (this inventory gets filled by purchasing something in game, like a pet)

local dataStores = game:GetService("DataStoreService"):GetDataStore("BuckDataStore")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local totalplayers = 0 


game.Players.PlayerAdded:Connect(function(player)
	totalplayers = totalplayers + 1
	
	local leaderstats = Instance.new("Folder")
	leaderstats.Name = "leaderstats"
	leaderstats.Parent = player
	
	local energy = Instance.new("IntValue")
	energy.Name = "Energy"
	energy.Parent = leaderstats	

	local playerData = Instance.new("Folder")
	playerData.Name = player.Name
	playerData.Parent = game.ServerStorage.PlayerData
	
	local inventory = Instance.new("Folder")
	inventory.Name = "Inventory"
	inventory.Parent = playerData	


	local energydata
        local InventoryData

	pcall(function()
		energydata = dataStores:GetAsync(player.UserId.."-Energy")
	end)

	pcall(function()
		InventoryData = dataStores:GetAsync(player.UserId.."-Inventory")
	end)	

	if energydata then
		energy.Value = energydata
	end

	if InventoryData then
		for _, item in pairs(InventoryData) do
			if game.ServerStorage.Items:FindFirstChild(item) then
				local ItemClone = game.ServerStorage.Items[item]:Clone()
				ItemClone.Parent = inventory
			end
		end
	end	
end)

local bindableEvent = Instance.new("BindableEvent")

game.Players.PlayerRemoving:Connect(function(player)
	
	pcall(function()
		dataStores:SetAsync(player.UserId.."-Energy", player.leaderstats.Energy.Value)
	end)

	pcall (function()
		local items = game.ServerStorage.PlayerData[player.Name].Inventory:GetChildren()
		local itemstable = {}
		
		for _, v in pairs(items) do
			table.insert(itemstable,v.Name)
		end
		dataStores:SetAsync(player.UserId.."-Inventory",itemstable)
	end)		
		
	
	totalplayers = totalplayers - 1
	bindableEvent:Fire()
	
end)

game:BindToClose(function()
	while totalplayers > 0 do
		bindableEvent.Event:Wait()
	end
end)

Does anyone know what the cause of people losing data could be?

A lot of people around here have been saying that DataStore:SetAsync loses data very easily, and it is often better to use DataStore:UpdateAsync, and they experienced no problems. You should try just replacing your SetAsync calls with UpdateAsync, and seeing if that solves your problem.

Some articles have already talked about data loss from SetAsync here:

Datastore2 is also a good resource for backing up or fully replacing your datastores:

2 Likes

Read this as I was reading more on datastore:save every whatever amount of seconds, save after they purchased an important item, save when they leave the game.

also
https://devforum.roblox.com/t/stop-using-setasync-to-save-player-data/276457

1 Like

Okay, I will see how it works after I replace SetAsync commands for UpdateAsync commands. Thank you for helping!

Yeah, I can very clearly see why data loss is happening. In addition to poor coding structure, you also are exhausting your budget by making too many DataStore calls unnecessarily and incorrectly.

First thing you’re going to want to do is fix up your pcalls and how you’re saving in one fell swoop. The use of pcalls are important to DataStores so do not throw away the values they return.

local success, result = pcall(dataStore.GetAsync, dataStore, player.UserId)

if success then
    if result then
        -- Player has data
    else
        -- Player does not have data or nil was returned
    end
else
    -- Success is not true, something went wrong
    warn(result)
end

As for DataStores, save in tables instead of one DataStore per stat. Convert your data into a table instead of pecking each individual stat. You can save something like

local DataSave = {
    ["Energy"] = energy.Value
}

Of course, you should make it more dynamic, but this gives you an idea of what to look for. You don’t really need leaderstats to use data - you could even get rid of it, since all it does is display data and that’s all it should do. Values can be displayed on Guis to the client.

Finally, I personally would incorporate UpdateAsync as @goldenstein64 recommended, but I don’t quite agree with the rationale he used fully. It’s possible to avoid data loss with SetAsync and receive data loss from UpdateAsync, just the latter mitigates the risk in a number of cases when done right.

Here’s a better article explaining Set/UpdateAsync which also includes some noteworthy discussions in the responses:

This one is dedicated to the practices of saving to DataStores whereas gold’s article is an isolated and similar case. Better to tackle the root, though the information there is nonetheless helpful to make note of.

5 Likes

Thank you for helping me!

I have to say I’m not very experienced yet with programming and some things are still hard for me to do. I will try to change the way I save data starting off with changing the pcalls!

1 Like