Remaining data loss from revised Datastore System

Hi,

I recently got my datastore system upgraded to be more sophisticated. To me it personally seems that it is not possible for dataloss to occur, however it still very rarely does (one player has everything reset) however most of the time players keep their data.

Below is the ModuleScript that handles the general loading and saving of data, can anyone think why even after this datastore optimisation the issue still occurs?

Ive also not had much luck in finding a similar issue on the devforum (seemingly) which im then slightly confused as to how its happening, its quite a big problem for the game

Thanks

Code below:

--//This module is responsible for saving and loading data

local module = {}
local DataStoreService = game:GetService('DataStoreService')
local JSONExpert = require(script.JSONExpert)


function module:Load(datastore_key, i, initializer, tries)
	i = (typeof(i) == 'Instance' and i:IsA('Player') and tostring(i.UserId)) or i
	initializer = initializer or {}
	tries = tries or math.huge
	local datastore = DataStoreService:GetDataStore(datastore_key)
	local data do
		for _ = 1,tries do
			local success, error = pcall(function()
				data = datastore:GetAsync(i)
			end)

			if (success == true) then
				if (data == nil) then
					data = initializer
				else
					data = JSONExpert:JSONDecode(data)

					if (next(data) == nil) then
						data = initializer
					end
				end

				break
			else
				warn(error)
			end
		end
	end

	return data
end

function module:Save(datastore_key, i, v, tries)
	i = (typeof(i) == 'Instance' and i:IsA('Player') and tostring(i.UserId)) or i
	v = v or {}
	tries = tries or math.huge
	local datastore = DataStoreService:GetDataStore(datastore_key)
	local success, error

	v = JSONExpert:JSONEncode(v)

	for _ = 1,tries do 
		success, error = pcall(function()
			datastore:SetAsync(i, v)
		end)

		if (success == true) then
			return true;
		else
			warn(error);
		end
	end

	return false;
end

--//Encodes a table of values into a heiarchy of value objects
function module:EncodeValues(Stats, parent)
	for i,v in pairs(Stats) do
		if (typeof(v) == 'table') then
			local folder = Instance.new('Folder')
			folder.Name = i
			folder.Parent = parent

			self:EncodeValues(v, folder)
		else
			local type_i = typeof(v) do
				if (type_i == 'boolean') then
					type_i = 'bool'
				end

				type_i = type_i:sub(1,1):upper()..type_i:sub(2) --//Capitalizes first letter
			end

			local value = Instance.new(string.format('%sValue', type_i));
			value.Name = i
			value.Value = v
			value.Parent = parent
		end
	end
end

--//Decodes a heiarchy of value objects into a table of values
function module:DecodeValues(parent, table)
	table = table or {}

	for _,obj in ipairs(parent:GetChildren()) do
		if (obj:IsA('Folder')) then
			table[obj.Name] = self:DecodeValues(obj, {})
		elseif (obj:IsA('ValueBase')) then
			table[obj.Name] = obj.Value
		end
	end

	return table
end

return module

Your problem is :SetAsync because it doesn’t respect previous data and just overwrite it, try to use :UpdateAsync instead.

1 Like

Your issue is that you’re saving the data even if the data returns nil, which overwrites previous data. To fix this I’d recommend using UpdateAsync() GlobalDataStore | Roblox Creator Documentation, that way you can check if data exists before attempting to save it. The modified version should look like this (untested):

function module:Save(datastore_key, i, v, tries)
	i = (typeof(i) == 'Instance' and i:IsA('Player') and tostring(i.UserId)) or i
	v = v or {} -- Remove this line entirely, there is no point saving an empty table
	tries = tries or math.huge
	local datastore = DataStoreService:GetDataStore(datastore_key)
	local success, Error

	v = JSONExpert:JSONEncode(v)

	success, Error = pcall(function()
		datastore:UpdateAsync(i, function(CurrentData)
			if CurrentData and not v then
				return
			else
				return v
			end
		end)
	end)

	if (success == true) then
		return true;
	else
		warn(Error);
	end

	return false;
end

You can also remove the loop since UpdateAsync will automatically do that for you. Luckily now that Datastore v2.0 is a thing, it’s pretty easy to roll back a player’s save data if they were affected, even if you used SetAsync(). Hope this helps!

1 Like

Hi, sorry for the late reply, thanks to both for the suggestion. If I do recall correctly though, i had used UpdateAsync in the past and it actually didn’t seem to fix the problem, id still get a random wipe of a certain players data? Could you think of any other reasons for this? (Il still give the above suggestion a go)

Edit: I could actually post how I last utilised UpdateAsync a while ago, maybe it could be that I was using it incorrectly, here is how it was:

	local success,errorMsg = pcall(function()
			DataStore:UpdateAsync(player.UserId,function()
				return data
			end)
		end)

		if success then
			print("Save Successful!")
		else print(errorMsg)
		end


	

Thanks!

You may have been using UpdateAsync() but it seems you weren’t actually checking if the data was valid before saving it. it would look more like this:

local success,errorMsg = pcall(function()
	DataStore:UpdateAsync(player.UserId,function(CurrentData) --UpdateAysnc passes Current Data as a arguement
        if CurrentData and not data then return end --If CurrentData exists then don't overwrite it with emtpy data
		return data
	end)
end)

I’ve also noticed that this line:

v = v or {}

Is causing issues, since technically an empty table doesn’t count as empty data, so it would overwrite a player’s data without errors.

1 Like

Thanks alot! Il give it a try, and if it seems to work il mark as the solution!

1 Like