My data keeps getting deleted when I play test

Heya!

I hope you’re well!

So, recently I’ve started to use DataStoreService for the first time. Everything works well and all, right?

This all comes to an end when for some reason when I’m playtesting, all my data is lost

I’m unsure if this occurs when a user plays the game on the public client. This happens on all my data stores. Am I leaving and joining playtest too quickly? Is this normal?

Thanks,
Akridiki

3 Likes

Hi there,

This mostly sounds like an issue with your implementation of DataStoring than DataStores themselves. Providing the code with a brief description of what you’re attempting to achieve alongside any potential known issues such as errors would be a good starting point for anyone here to direct you into the right direction with correcting this behavior.

2 Likes

Hello!

Yes sure! Here’s one of my datastores (not going to send all for the sake of the message being too long)

local DataStoreService = game:GetService("DataStoreService")
local FOVREMOTE = game:GetService("ReplicatedStorage").RemoteEvents.Slider.FOV
local IGMREMOTE = game:GetService("ReplicatedStorage").RemoteEvents.Slider.IGM

local FOVslider = DataStoreService:GetDataStore("SliderValues", "FOV")
local IGMslider = DataStoreService:GetDataStore("SliderValues", "IGM")

game.Players.PlayerAdded:Connect(function(Player)
	-- FIELD OF VIEW
	local FOV = Instance.new("IntValue")
	local FOVdefaultValue = 50
	FOV.Name = "FOV_VALUE"
	FOV.Parent = Player:WaitForChild("leaderstats")	
	FOV.Value = FOVdefaultValue -- Default Value
	FOVREMOTE:FireClient(Player, FOV.Value) -- Default Value
	local FOVData = FOVslider:GetAsync(Player.UserId)
	if FOVData == nil then
		print("Oh! You must be new! We're setting you up...")
		print("Welcome to Miles by the way. :)")
		print(" ")
		FOVData = FOVdefaultValue
		warn("FOV set!")
	else if FOV ~= nil then
			print("Welcome back to Miles! :)")
		end
	end
	FOV.Value = FOVData
	FOVREMOTE:FireClient(Player, FOVData)
	FOVREMOTE.OnServerEvent:Connect(function(Player, newFOV)
		FOV.Value = newFOV
	end)
	
	-- IN GAME MUSIC
	local IGM = Instance.new("NumberValue")
	local IGMdefaultValue = 0.8
	IGM.Name = "IGM_VALUE"
	IGM.Parent = Player:WaitForChild("leaderstats")	
	IGM.Value = IGMdefaultValue -- Default Value
	IGMREMOTE:FireClient(Player, IGM.Value) -- Default Value
	local IGMData = IGMslider:GetAsync(Player.UserId)
	if IGMData == nil then
		IGMData = IGMdefaultValue
		warn("IGM set!")
	end
	IGM.Value = IGMData
	IGMREMOTE:FireClient(Player, IGMData)
	IGMREMOTE.OnServerEvent:Connect(function(Player, newIGM)
		IGM.Value = newIGM
	end)
end)

game.Players.PlayerRemoving:Connect(function(Player)
	local success, errorMessage = pcall(function()
		local FOVData = FOVslider:GetAsync(Player.UserId)
		FOVslider:SetAsync(Player.UserId, Player.leaderstats.FOV_VALUE.Value)
		
		local IGMData = IGMslider:GetAsync(Player.UserId)
		IGMslider:SetAsync(Player.UserId, Player.leaderstats.IGM_VALUE.Value)
	end)
	if not success then
		print(errorMessage)
	end
end)

This is my newest data store.

Its purpose is to save the settings’ slider values, depending on how the user has set them.


(Here’s a reference for you to understand)

This uses someone else’s API:

The script communicates by RemoteEvents with 2 LocalScripts. One for the FOV and the other for music. Since the sliders are in a local script in a UI, and the data store is a script saved in ServerScriptService, the only solution I could think of was RemoteEvents.

The whole thing works really well, it’s just that weird problem I’ve mentioned before

Edit:

This happens in all of my data stores as I mentioned before. It’s nothing specific. I tried my best to use the Roblox documentation to construct all my data stores.

Why do you get the data when the player is removed in the first place? It seems like its never used. Also, try implementing autosave, that might fix the issue. Maybe try testing in a team test server as in studio, PlayerRemoving will sometimes fail to fire.

1 Like

Additionally, you can also use BindToClose to extend the wait time before the server shuts down.

1 Like

Thanks for the suggestions!

As seen here, I do request the data when the player is added, not when they are removed. At least, I’m hoping that GetAsync is to retrieve data. As for autosave, that would be a good idea! As I said before, I’m a complete rookie in the whole DataStoreService space, so I’m still really green about the whole thing. Though, I will try to implement it anyways!

Is it okay if you tell me how this will differ in results? I may have forgotten to mention that this game is single-player as of now, I am planning to add a multi-player mode sometime.

(I had to repost, for some reason DevForum made me reply to myself)

Edit:

I just realized that I’m requesting data when the person left aswell. My bad! :frowning:

1 Like

First, I just want to mention that whenever you get and save data async, you should write it inside of a pcall. Also, since you’re saving the values of the slider from leaderstats, you could make sure the leaderstats folder is actually created. An easy way to know is to make sure the leaderboard actually says the values. If not, you’d have to manually create it and I’d recommend creating it inside of this script.

2 Likes

Yes, the autosave feature is quite simple, firstly you should separate your saving into a function, and call it every 5 minutes or so, as for the studio results. The game shuts down before it can fire the PlayerRemoved event. This is because doing a studio test actually runs it on your computer, and because of this differing code, it causes the server to shut down too early. This could also have been implemented to save time while you are unloading your tests.

2 Likes

Hi! Thanks for the reply!

I can confirm that the leaderstats folder is created by another script before the values are inside of it (not mentioned in this post). As for the pcall, I did use it in the PlayerRemoving function, unless you mean that I should do it for PlayerAdded as well.

Mostly anywhere there is an Async function called, you should wrap in it a pcall just to be safe.

Okay, I’ll try doing that aswell!

So, if I’m understanding correctly, what I should do is separate:

	local success, errorMessage = pcall(function()
		local FOVData = FOVslider:GetAsync(Player.UserId)
		FOVslider:SetAsync(Player.UserId, Player.leaderstats.FOV_VALUE.Value)
		
		local IGMData = IGMslider:GetAsync(Player.UserId)
		IGMslider:SetAsync(Player.UserId, Player.leaderstats.IGM_VALUE.Value)
	end)
	if not success then
		print(errorMessage)
	end
end)

into 2, one for IGMData and the other for FOVData inside functions (per say, SaveFOV() and SaveIGM(), then I should create a loop so that every 5 seconds, it executes those functions. Correct?

1 Like

No, every 5 minutes. Doing an auto save every 5 seconds would exhaust the Data store API. Also, just put the entirety of the code you run on Player Added inside of a function, then just run that on startup and save all players data every 5 minutes.

1 Like

Agh, sorry I misread it. Sorry, it’s getting late.

Apart from that, hmm I see. I think I understood. I’ll update you once I reconstruct it then!

1 Like
local DataStoreService = game:GetService("DataStoreService")
local FOVREMOTE = game:GetService("ReplicatedStorage").RemoteEvents.Slider.FOV
local IGMREMOTE = game:GetService("ReplicatedStorage").RemoteEvents.Slider.IGM

local FOVslider = DataStoreService:GetDataStore("SliderValues", "FOV")
local IGMslider = DataStoreService:GetDataStore("SliderValues", "IGM")

game.Players.PlayerAdded:Connect(function(Player)
	local function AutoSave()
		-- FIELD OF VIEW
		local FOV = Instance.new("IntValue")
		local FOVdefaultValue = 50
		FOV.Name = "FOV_VALUE"
		FOV.Parent = Player:WaitForChild("leaderstats")	
		FOV.Value = FOVdefaultValue -- Default Value
		FOVREMOTE:FireClient(Player, FOV.Value) -- Default Value
		pcall(function()
			local FOVData = FOVslider:GetAsync(Player.UserId)
			if FOVData == nil then
				print("Oh! You must be new! We're setting you up...")
				print("Welcome to Miles by the way. :)")
				print(" ")
				FOVData = FOVdefaultValue
				warn("FOV set!")
			else if FOV ~= nil then
					print("Welcome back to Miles! :)")
				end
			end
			FOV.Value = FOVData
			FOVREMOTE:FireClient(Player, FOVData)
			FOVREMOTE.OnServerEvent:Connect(function(Player, newFOV)
				FOV.Value = newFOV
			end)
		end)

		-- IN GAME MUSIC
		local IGM = Instance.new("NumberValue")
		local IGMdefaultValue = 0.8
		IGM.Name = "IGM_VALUE"
		IGM.Parent = Player:WaitForChild("leaderstats")	
		IGM.Value = IGMdefaultValue -- Default Value
		IGMREMOTE:FireClient(Player, IGM.Value) -- Default Value
		pcall(function()
			local IGMData = IGMslider:GetAsync(Player.UserId)
			if IGMData == nil then
				IGMData = IGMdefaultValue
				warn("IGM set!")
			end
			IGM.Value = IGMData
			IGMREMOTE:FireClient(Player, IGMData)
			IGMREMOTE.OnServerEvent:Connect(function(Player, newIGM)
				IGM.Value = newIGM
			end)
		end)
	end
	while true do
		AutoSave()
		task.wait(300)
	end
end)

game.Players.PlayerRemoving:Connect(function(Player)
	local success, errorMessage = pcall(function()
		FOVslider:SetAsync(Player.UserId, Player.leaderstats.FOV_VALUE.Value)

		IGMslider:SetAsync(Player.UserId, Player.leaderstats.IGM_VALUE.Value)
	end)
	if not success then
		print(errorMessage)
	end
end)

Something like this?

Yeah, that should work. You could also put a central function to handle this, but this code works perfectly fine.

1 Like

Awesome! I’ll consider it as the solution. I’m hoping it doesn’t delete my data again so :crossed_fingers:

Thanks again for the time! :slight_smile:

(oh my goodness I’m so tired I wrote fingers instead of data.)

1 Like

Well, you should probably test if it works before marking it as solved.

I did, it works pretty much the same. The deletion happens infrequently. So I can’t be so sure.

Though judging its logic, it makes sense for it not to happen again.

Oh wait, I know why. You are using the exact same datastorekey for both values, Consider using a table