Data loss complaints

so people on my group wall keep complaining about data loss, i have no idea why since i have never lost data, maybe its my script?

giving data + auto save script

local dsm = ds:GetDataStore("Main")



	game.Players.PlayerAdded:Connect(function(plr)
		local z = 0 
		local char = plr.Character or plr.CharacterAdded:Wait()

		local newf = Instance.new("Folder", plr)
		newf.Name = "data"
		local newv = Instance.new("StringValue",newf)
		newv.Name = "Character"
		newv.Value = "None"
		local newp = Instance.new("NumberValue", newf)
		newp.Name = "Points"

		char:FindFirstChild("Humanoid").Died:Connect(function()
			print("yes")
			plr.data.Character.Value = "None"
		end)
		local news = Instance.new("NumberValue", plr)
		news.Name = "Stamina"
		news.Value = 3
		local rn = Instance.new("BoolValue", char)
		rn.Name = "Running"
		local sc = Instance.new("BoolValue", char)
		sc.Name = "screen"

		task.spawn(function()
			pcall(function()
				repeat
					task.wait(0.5)
					local running = plr.Character:FindFirstChild("Running")
					local scren = plr.Character:FindFirstChild("screen")
					if running.Value == false and scren.Value == false then
						if news.Value ~= 3 then
							news.Value += 0.5
						end
					end
				until  z == 2525
			end)
		end)
		local newls = Instance.new("Folder", plr)
		newls.Name = "leaderstats"
		local newc = Instance.new("NumberValue", newls)
		newc.Name = "Coins"
		local newcf = Instance.new("Folder", newf)
		newcf.Name = "Characters"
		local tsmowned = Instance.new("BoolValue", newcf)
		tsmowned.Name = "TSM"
		local smowned = Instance.new("BoolValue", newcf)
		smowned.Name = "SM"
		local lcowned = Instance.new("BoolValue", newcf)
		lcowned.Name = "LCM"
		local donated = Instance.new("NumberValue", plr)
		donated.Name = "Donated"
		local tcowned = Instance.new("BoolValue", newcf)
		tcowned.Name = "TCM"
		local tvowned = Instance.new("BoolValue", newcf)
		tvowned.Name = "TV"
		local scmowned = Instance.new("BoolValue", newcf)
		scmowned.Name = "SCM"
		local ttvowned = Instance.new("BoolValue", newcf)
		ttvowned.Name = "TTV"
		local ttvcowned = Instance.new("BoolValue", newcf)
		ttvcowned.Name = "TTVC"
		local utsmowned = Instance.new("BoolValue", newcf)
		utsmowned.Name = "UTSM"
		local ncmmowned = Instance.new("BoolValue", newcf)
		ncmmowned.Name = "NCM"
		local utcmowned = Instance.new("BoolValue", newcf)
		utcmowned.Name = "UTCM"

		local nv = Instance.new("BoolValue", plr)
		nv.Name = "Loaded"
		nv.Value = false
		
		local dse = ds:GetDataStore("Main")
	local ledata
		local succes, errored = pcall(function()
			ledata = dse:GetAsync(plr.UserId)
		end)
		if succes then
			pcall(function()
			
				newp.Value = ledata[1]
				tsmowned.Value = ledata[2]
				smowned.Value = ledata[3]
				lcowned.Value = ledata[4]
				donated.Value = ledata[5]
				tcowned.Value = ledata[6]

				tvowned.Value = ledata[7]
				scmowned.Value = ledata[8]
				ttvowned.Value = ledata[9]
				ttvcowned.Value = ledata[10]
				utsmowned.Value = ledata[11]
				ncmmowned.Value = ledata[12]
				utcmowned.Value = ledata[13]
			end)
		
			nv.Value = true
		end
		
		if errored then
			local try1 
		local succes, errored = pcall(function()
			try1 = dse:GetAsync(plr.UserId)
		end)
		
		if succes then

			newp.Value = try1[1]
			tsmowned.Value = try1[2]
			smowned.Value = try1[3]
			lcowned.Value = try1[4]
			donated.Value = try1[5]
			tcowned.Value = try1[6]

			tvowned.Value = try1[7]
			scmowned.Value = try1[8]
			ttvowned.Value = try1[9]
			ttvcowned.Value = try1[10]
			utsmowned.Value = try1[11]
			ncmmowned.Value = try1[12]
		end
		
		if errored then
			plr:Kick()
		end
				
		end
		
		

	while wait(60) do
		local stufftosave = {
			plr.data.Points.Value,
			plr.data.Characters.TSM.Value,
			plr.data.Characters.SM.Value,
			plr.data.Characters.LCM.Value,
			plr.Donated.Value,
			plr.data.Characters.TCM.Value,
			plr.data.Characters.TV.Value,
			plr.data.Characters.SCM.Value,
			plr.data.Characters.TTV.Value,
			plr.data.Characters.TTVC.Value,
			plr.data.Characters.UTSM.Value,
			plr.data.Characters.NCM.Value,
			plr.data.Characters.UTCM.Value
		}

		if plr.Loaded.Value == true then
			local succes, erromsg = pcall(function()
				local dse = ds:GetDataStore("Main")
				dse:UpdateAsync(plr.UserId, function(oldvalue)
					if stufftosave ~= oldvalue then
						return stufftosave
					end
				end)
			end)

			if succes then
				print("saved")
			else
				warn(erromsg)
			end

		end
	end

	end)

player removing and bindtoclose handler script:

	pcall(function()
		local stufftosave = {
			plr.data.Points.Value,
			plr.data.Characters.TSM.Value,
			plr.data.Characters.SM.Value,
			plr.data.Characters.LCM.Value,
			plr.Donated.Value,
			plr.data.Characters.TCM.Value,
			plr.data.Characters.TV.Value,
			plr.data.Characters.SCM.Value,
			plr.data.Characters.TTV.Value,
			plr.data.Characters.TTVC.Value,
			plr.data.Characters.UTSM.Value,
			plr.data.Characters.NCM.Value,
			plr.data.Characters.UTCM.Value
		}

		if plr.Loaded.Value == true then
			local succes, erromsg = pcall(function()
				local dse = ds:GetDataStore("Main")
				dse:UpdateAsync(plr.UserId, function(oldvalue)
					if stufftosave ~= oldvalue then
						return stufftosave
					end
				end)
			end)

			if succes then
				print("saved")
			else
				local succes, erromsg = pcall(function()
					local dse = ds:GetDataStore("Main")
					dse:UpdateAsync(plr.UserId, function(oldvalue)
						if stufftosave ~= oldvalue then
							return stufftosave
						end
					end)
				end)

				if succes then
					print("saved")

				else
					local succes, erromsg = pcall(function()
						local dse = ds:GetDataStore("Main")
						dse:UpdateAsync(plr.UserId, function(oldvalue)
							if stufftosave ~= oldvalue then
								return stufftosave
							end
						end)
					end)
					if succes then
						print("good")
					end
				end
			end

		end
	end)

end)

	





game:BindToClose(function()
	pcall(function()
		for i,v in game.Players:GetChildren() do
		local stufftosave = {
			v.data.Points.Value,
			v.data.Characters.TSM.Value,
			v.data.Characters.SM.Value,
			v.data.Characters.LCM.Value,
			v.Donated.Value,
			v.data.Characters.TCM.Value,
			v.data.Characters.TV.Value,
			v.data.Characters.SCM.Value,
			v.data.Characters.TTV.Value,
			v.data.Characters.TTVC.Value,
			v.data.Characters.UTSM.Value,
			v.data.Characters.NCM.Value,
			v.data.Characters.UTCM.Value
		}


		if v.Loaded == true then
				local succes, erromsg = pcall(function()
					local dse = ds:GetDataStore("Main")
					dse:UpdateAsync(v.UserId, function(oldvalue)
						if stufftosave ~= oldvalue then
							return stufftosave
						end
					end)
				end)
				if succes then
					print("saved")
				else
					warn(erromsg)
				end
		end

		
	end
	end)
end)
1 Like

you can use modules like profileservice to get more reliable data store

sometimes the data store api can fail or error and cause the player to lose all their data

also you should put your data in a dictionary so you dont have to make new code for each value

This code is very hard to read…

anyway, from what I can tell, you aren’t yielding to allow BindToClose to save player data. You should add a task.wait(5) statement to allow for this.

A few things:

  • You are getting the data store every time, idk why u need to do that
  • You are doing pcalls inside of pcalls (why?)
  • You have no data versioning or any way to tell which data entry to the store is the most recent
  • You seem to be spamming pcalls with no variables to receive the results. This appears to be for stopping errors in the output, but these errors will still impact your game and just not show up in the output.
  • I can’t seem to find anywhere you are assigning default data for new players
  • Your retry logic for saving looks odd… 3 seperate UpdateAsync code blocks? Why not:
--I am aware I do not have to initialise variables here, I just decided to because I think it makes the code look nicer
local success: nil, result: nil
local attempt: number= 0

repeat
    success, result = pcall(dataStore.UpdateAsync, dataStore, saveKey, function(oldData)
        --do stuff
    end) :: boolean, string?
    attempt += 1
until
    success or attempt == 3





this is incorrect, a data key cannot lose its associated data from an error. It’s likely what made you think this happens is failing to fetch data from the store and then saving blank values to the store afterwards, which can be avoided with things like failsafe values.

ProfileService still uses the Roblox data store system. However, it does account for things like budgeting, caching, data versioning, etc. that a lot of people don’t know how to do.

i tried improving it, is this better?

local dsm = ds:GetDataStore("Main")



game.Players.PlayerAdded:Connect(function(plr)
	local z = 0 
	local char = plr.Character or plr.CharacterAdded:Wait()

	local newf = Instance.new("Folder", plr)
	newf.Name = "data"
	local newv = Instance.new("StringValue",newf)
	newv.Name = "Character"
	newv.Value = "None"
	local newp = Instance.new("NumberValue", newf)
	newp.Name = "Points"

	char:FindFirstChild("Humanoid").Died:Connect(function()
		print("yes")
		plr.data.Character.Value = "None"
	end)
	local news = Instance.new("NumberValue", plr)
	news.Name = "Stamina"
	news.Value = 3
	local rn = Instance.new("BoolValue", char)
	rn.Name = "Running"
	local sc = Instance.new("BoolValue", char)
	sc.Name = "screen"

	task.spawn(function()
		pcall(function()
			repeat
				task.wait(0.5)
				local running = plr.Character:FindFirstChild("Running")
				local scren = plr.Character:FindFirstChild("screen")
				if running.Value == false and scren.Value == false then
					if news.Value ~= 3 then
						news.Value += 0.5
					end
				end
			until  z == 2525
		end)
	end)
	local newls = Instance.new("Folder", plr)
	newls.Name = "leaderstats"
	local newc = Instance.new("NumberValue", newls)
	newc.Name = "Coins"
	local newcf = Instance.new("Folder", newf)
	newcf.Name = "Characters"
	local tsmowned = Instance.new("BoolValue", newcf)
	tsmowned.Name = "TSM"
	local smowned = Instance.new("BoolValue", newcf)
	smowned.Name = "SM"
	local lcowned = Instance.new("BoolValue", newcf)
	lcowned.Name = "LCM"
	local donated = Instance.new("NumberValue", plr)
	donated.Name = "Donated"
	local tcowned = Instance.new("BoolValue", newcf)
	tcowned.Name = "TCM"
	local tvowned = Instance.new("BoolValue", newcf)
	tvowned.Name = "TV"
	local scmowned = Instance.new("BoolValue", newcf)
	scmowned.Name = "SCM"
	local ttvowned = Instance.new("BoolValue", newcf)
	ttvowned.Name = "TTV"
	local ttvcowned = Instance.new("BoolValue", newcf)
	ttvcowned.Name = "TTVC"
	local utsmowned = Instance.new("BoolValue", newcf)
	utsmowned.Name = "UTSM"
	local ncmmowned = Instance.new("BoolValue", newcf)
	ncmmowned.Name = "NCM"
	local utcmowned = Instance.new("BoolValue", newcf)
	utcmowned.Name = "UTCM"

	local nv = Instance.new("BoolValue", plr)
	nv.Name = "Loaded"
	nv.Value = false
	local success, result
	local attempt = 0
	local ledata

	local success, result = pcall(function()
		repeat
			local success, result = pcall(function()
				ledata = dsm:GetAsync(plr.UserId)
			end)
			attempt += 1
		until
		success or attempt == 3
	end)


	if success then

		newp.Value = ledata[1]
		tsmowned.Value = ledata[2]
		smowned.Value = ledata[3]
		lcowned.Value = ledata[4]
		donated.Value = ledata[5]
		tcowned.Value = ledata[6]

		tvowned.Value = ledata[7]
		scmowned.Value = ledata[8]
		ttvowned.Value = ledata[9]
		ttvcowned.Value = ledata[10]
		utsmowned.Value = ledata[11]
		ncmmowned.Value = ledata[12]
		utcmowned.Value = ledata[13]
	else
		plr:Kick("failed to load data.")
	end



	while wait(60) do
		local stufftosave = {
			plr.data.Points.Value,
			plr.data.Characters.TSM.Value,
			plr.data.Characters.SM.Value,
			plr.data.Characters.LCM.Value,
			plr.Donated.Value,
			plr.data.Characters.TCM.Value,
			plr.data.Characters.TV.Value,
			plr.data.Characters.SCM.Value,
			plr.data.Characters.TTV.Value,
			plr.data.Characters.TTVC.Value,
			plr.data.Characters.UTSM.Value,
			plr.data.Characters.NCM.Value,
			plr.data.Characters.UTCM.Value
		}

		local success, result


		local attempt = 0

		repeat
			local success, result = pcall(function()
				dsm:UpdateAsync(plr.UserId, function(oldvalue)
					if stufftosave ~= oldvalue then
						return stufftosave
					end
				end)
			end)
			attempt += 1
		until
		success or attempt == 3
	end

end)




game.Players.PlayerRemoving:Connect(function(plr)

	local stufftosave = {
		plr.data.Points.Value,
		plr.data.Characters.TSM.Value,
		plr.data.Characters.SM.Value,
		plr.data.Characters.LCM.Value,
		plr.Donated.Value,
		plr.data.Characters.TCM.Value,
		plr.data.Characters.TV.Value,
		plr.data.Characters.SCM.Value,
		plr.data.Characters.TTV.Value,
		plr.data.Characters.TTVC.Value,
		plr.data.Characters.UTSM.Value,
		plr.data.Characters.NCM.Value,
		plr.data.Characters.UTCM.Value
	}

	local success, result
	local attempt = 0

	repeat
		local success, result = pcall(function()
			dsm:UpdateAsync(plr.UserId, function(oldvalue)
				if stufftosave ~= oldvalue then
					return stufftosave
				end
			end)
		end)
		attempt += 1
	until
	success or attempt == 3


end)







game:BindToClose(function()

	task.wait(5)
	for i,v in game.Players:GetChildren() do
		local stufftosave = {
			v.data.Points.Value,
			v.data.Characters.TSM.Value,
			v.data.Characters.SM.Value,
			v.data.Characters.LCM.Value,
			v.Donated.Value,
			v.data.Characters.TCM.Value,
			v.data.Characters.TV.Value,
			v.data.Characters.SCM.Value,
			v.data.Characters.TTV.Value,
			v.data.Characters.TTVC.Value,
			v.data.Characters.UTSM.Value,
			v.data.Characters.NCM.Value,
			v.data.Characters.UTCM.Value
		}

		local success, result
		local attempt = 0

		repeat
			local success, result = pcall(function()
				dsm:UpdateAsync(v.UserId, function(oldvalue)
					if stufftosave ~= oldvalue then
						return stufftosave
					end
				end)
			end)
			attempt += 1
		until
		success or attempt == 3
	end




end)

Not by much. I can still see one of the pcalls I described above, I also see you are creating a lot of instance values without ever setting the parent, deeming them useless to scripts outside of that one. (didn’t see the parent parameter sorry, i was on mobile and it cut off)

I wouldn’t recommend retry logic for loading data, it’s more for saving data, especially as for loading you can tell the player their data didn’t load and ask them to rejoin.

You should also add the wait at the end of the BindToClose and data versioning, like I suggested.

what do you mean without parent, the parent is set right after the value

local ncmowned = Instance.new(“BoolValue”, newcf ← parent)

thank you for the tips tho hopefully when i release the update no more data loss complaints.

Apart from not handling errors correctly(by handling I mean retrying etc, not just printing the error) the data structure itself is faulty.

You’re saving data as an array of values instead of a dictionary with direct key-value pairs. This means that if you decide to update said array, say for example add a new value, or change the order in any way, it may cause data loss by mismatching values saved using the old way with the new format.

Hi @NyrionDev you do not seem very experienced with coding or may be a beginner. However, the Profile Service won’t help you fix this issue. The Profile Service isn’t typically used for data handling as much as the normal data services. Please consider using Reading Services before stating random facts. Additionally, data errors can occur due to how it is structured in the code. Being close to binding is beneficial, but the Profile Service will likely make it worse, similar to using memory services for data handling.

Wrong person i ping sorry! I meant ping @happya_x

Adding onto my previous suggestions, I noticed you are autosaving every 60 seconds, and you do not appear to have put in any kind of budgeting system. This means once your requests run out, further requests are subject to throttling, where they will be added to queue and it will take more time for them to go through. You are not giving them time to do this, as seen in the BindToClose.

You should implement wait times and a budgeting system to allow requests time to process as well as preserve the budget. You should probably lengthen the wait time for autosaving, and I’d recommend making it dependant on the data budget. You can get the budget for a request like so:

--Getting the budget for UpdateAsync (example)
local budget: number = DataStoreService:GetRequestBudgetForRequestType(Enum.DataStoreRequestType.UpdateAsync)

A key thing to remember is accessing the data store is an asynchronous network request; it needs time to process.


I’m sorry if this sounds like I’m insulting your code, I’m not trying to be offensive, I just want to help you make this the best it can be. I’m trying to criticise constructively, please let me know if I need to explain anything or I’m being too picky! :sweat_smile:

Lol its not offensive all im trying to do is fix the data loss im not that good at data stores but i got a friend to recode it for me now :smiley:

1 Like

profileservice is built for saving player data using the datastore2 module which uses roblox’s datastore service

ProfileService and DataStore2 are two separate modules, and they both use the DataStoreService. ProfileService does not use DataStore2.




ProfileService is often used to simplify data saving. It’s a very powerful module that hides all the ins and outs of data saving and handles them automatically. This means developers do not need to worry about things like caching, versioning, budgeting and many more.

Therefore, implementing ProfileService will not be worse than a normal data system, but it may also not be any better if a custom system that includes these things is implemented. Things like caching, budgeting, versioning, binding to close, etc. are crucial to preventing data loss.

Saving with ProfileService is nothing like saving with things like MemoryStoreService, etc. Roblox’s Data Stores are the best method to save data, despite all the criticism they get.


I think both of you might need to read some more about data stores and community-made modules :sweat_smile:

Am I the only one who doesn’t use datastore modules? I don’t know if I’m weird but I think regular datastores are easy enough for me.

I use a data store module, but I made it myself. I personally think making my own module is better, I can tailor it more to my game. The modular structure also makes it easier to read.

It’s just important to put in the data loss prevention tactics, including ones I have previously stated.

so… no ur not weird :smiley:

It is absolutely more than fine to use if your data is simple, as long as you are not going overboard on your requests.

However, it becomes an issue the more features you introduce to your game, like trading, where features like session locking becomes vital to prevent duplicates, or purchase handling, where you need more assurance that data will save upon purchasing something. In this case, external DataStore modules will handle these things for you so you don’t have to.

Oh yeah I do have ways to prevent data loss, for example if the server shuts down I’ll use game:BindToclose() to save everyone’s data just in case.

You can just modify on your DATA STORE Code to make it on serverstorage or whatever!

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.