My data keeps getting deleted when I play test

Oh wow, this is a handful to read. Though I’ll make sure to go through this thoroughly. Thank you so much for taking the time to explain everything, I’m certain it will help not only me but also others in the same shoes as mine!

I’ll make sure to update you (and the rest) with the new code, altered with your suggestions! :slight_smile:

Okii so sorry for updating late, I just came on.

I’ve done what you have told me, it looks something like this:

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
			end
			FOV.Value = FOVData
			FOVREMOTE:FireClient(Player, FOVData)
		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
			end
			IGM.Value = IGMData
			IGMREMOTE:FireClient(Player, IGMData)
		end)
	end
	while true do
		AutoSave()
		task.wait(300)
	end
end)

FOVREMOTE.OnServerEvent:Connect(function(Player, newFOV)
	local leaderstats = Player:FindFirstChild("leaderstats")
	local FOV = leaderstats:FindFirstChild("FOV_VALUE")

	if leaderstats and FOV then
		FOV.Value = newFOV
	end
end)

IGMREMOTE.OnServerEvent:Connect(function(Player, newIGM)
	local leaderstats = Player:FindFirstChild("leaderstats")
	local IGM = leaderstats:FindFirstChild("IGM_VALUE")

	if leaderstats and IGM then
		IGM.Value = newIGM
	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)

Now this may be unrelated, but considering that in the PlayerAdded event, I fire the RemoteEvents, would that also be a cause of memory leaks or something like that? Or would it not matter?

Thanks!

1 Like

Hey, sorry for replying to you again. These questions just popped up.

You have mentioned ProfileService. What is the difference between the code I have integrated and ProfileService? How will this benefit me?

I’m all for efficiency if that means my game can be as memory leak-free as possible, that would be awesome since I want a smooth experience for everyone. I’ve tried researching it a bit, I really can’t seem to understand it a lot. I have never seen anyone use it…

Hi there,

I’ve looked through your code and have noticed a few issues. Although for the main part of handling the OnServerEvent() functions, what you’ve done should suffice without any noticeable issues surrounding memory.

  • Your AutoSave() function.

As I see you’re indefinitely repeating your AutoSave() function every 5 minutes, what’s important to notice here is that you’re also doing things such as creating instances, firing events, getting data, assigning data to certain properties, all of this happening inside the main AutoSave() function itself.

Now this is a problem because all of that will now happen over and over again in a loop repeating every 300 seconds. That means every 5 minutes, a new Instance of FOV and IGM NumberValue will be created, initialized, and parented to leaderstats. Considering the name of the function, it should only be using existing references of values and simply auto-saving them. You should consider looking through the function to see the segments contributing specifically to saving player-data, and leave Instance creation, DataStore GetAsync, parenting, etc. outside of the function. Basically the things that only need to happen once when the player has joined.

  • Imrpovement to pcall() handling in your PlayerAdded() setup.

Your current method of pcall() handling will sure help prevent your code from completely stopping and erroring if anything inside the pcall() function fails or errors, but I’ve cleaned it up slightly to include the informative success and err variables to allow for potential upcoming sanity checks and handling the data returned by your GetAsync() call.

  • FireClient() on PlayerAdded().

Although not as significant, I see you’re firing FOVREMOTE and IGMREMOTE twice before and after your DataStore GetAsync() call. May I ask why is that? You could simply fire up-to-date data once after you’ve retrieved it from DataStores, regardless of if the value is fetched from a store or default.

And for your question, no simply firing events inside PlayerAdded() will not cause contribute significantly to any memory issues as long as done in a controller manner. The issues that I was earlier mentioning about only start to stack and become problematic when you’re :Connect()ing events to their respective functions recursively. FireClient() can only be fired from a server and doesn’t need a connected function, hence shouldn’t be an issue.

  • Lastly, miscellaneous clean-up.

I noticed a minor redundancy in the revised OnServerEvent() functions that I provided yesterday with the 2 declared variables, where if leaderstats doesn’t exist by any chance, the next variable (FOV or IGM) holding their NumverValue will also error with an Attempt to index nil, since the root leaderstats itself isn’t there.

Keeping all of this in mind, below is a revised version of your code. Mostly containing the points mentioned above and a separated AutoSave() function alongside minor touch-ups here and there for you to potentially further amend to match your needs.

local Players = game:GetService("Players")
local DataStoreService = game:GetService("DataStoreService")
local ReplicatedStorage = game:GetService("ReplicatedStorage")

local RemoteEvents = ReplicatedStorage:WaitForChild("RemoteEvents")
local FOVREMOTE = RemoteEvents.Slider.FOV
local IGMREMOTE = RemoteEvents.Slider.IGM

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

local function AutoSave(plr)
	if plr then
		local leaderstats = plr:FindFirstChild("leaderstats")
		
		if leaderstats then
			local FOV, IGM = leaderstats:FindFirstChild("FOV_VALUE"), leaderstats:FindFirstChild("IGM_VALUE")
			
			if FOV and IGM then
				local success, errorMessage = pcall(function()
					FOVslider:SetAsync(plr.UserId, FOV.Value)
					IGMslider:SetAsync(plr.UserId, IGM.Value)
				end)

				if not success then
					warn("Error saving data for "..plr.Name.." ("..errorMessage..")")
				end
			end
		end
	end
end

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
	local success, err = pcall(function()
		FOVData = FOVslider:GetAsync(Player.UserId)
	end)
	
	if success and FOVData then
		FOV.Value = FOVData
	elseif FOVData == nil then
		FOV.Value = FOVdefaultValue
		print("Oh! You must be new! We're setting you up...")
		print("Welcome to Miles by the way. :)")
		print(" ")
	end
	FOVREMOTE:FireClient(Player, FOVData) -- Why is this being fired twice?

	-- 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
	local success, err = pcall(function()
		IGMData = IGMslider:GetAsync(Player.UserId)
	end)
	
	if success and IGMData then
		IGM.Value = IGMData
	elseif IGMData == nil then
		IGM.Value = IGMdefaultValue
	end
	IGMREMOTE:FireClient(Player, IGMData) -- Why is this being fired twice?
	
	while true do
		task.wait(300)
		AutoSave(Player)
	end
end)

FOVREMOTE.OnServerEvent:Connect(function(Player, newFOV)
	local leaderstats = Player:FindFirstChild("leaderstats")
	
	if leaderstats then
		local FOV = leaderstats:FindFirstChild("FOV_VALUE")
		
		if FOV then
			FOV.Value = newFOV
		end
	end
end)

IGMREMOTE.OnServerEvent:Connect(function(Player, newIGM)
	local leaderstats = Player:FindFirstChild("leaderstats")
	
	if leaderstats then
		local IGM = leaderstats:FindFirstChild("IGM_VALUE")
		
		if IGM then
			IGM.Value = newIGM
		end
	end
end)

Players.PlayerRemoving:Connect(function(Player)
	AutoSave(Player)
end)

My apologies if I misunderstood or missed something. Cheers.

EDITS (Suggestive):

  1. I would also advise you to consider storing FOV and IGM data in a single DataStore using dictionaries. Something along the lines of below would be what your data structure would then look like, which is also what you can store directly in a single DataStore.
local data = {
	["FOV"] = 69,
	["IGM"] = 69
}

print(data["FOV"]) -- Access a certain element of the dictionary, prints 69
DataStore:SetAsync(Player.UserId, data) -- Storing the entire 'data' dictionary itself using a singular datastore
  1. Combine the functionality of the 2 separate remote events into one. As someone mentioned earlier on, they’re practically doing the exact same thing, but simply to different NumberValues. You can then parse a separate identifier for the server to identify what execution needs to be performed for each separate incoming call based on the identifier.

Below is an example of that:

REMOTE.OnServerEvent:Connect(function(Player, valueType, newValue) -- OnServerEvent Argument change to incorporate an 'identifier' parameter
	if valueType == "FOV" then
		-- Set FOV Value
	elseif valueType == "IGM" then
		-- Set IGM Value
	end
end)

REMOTE:FireServer("FOV", 69) -- What your FireServer() call on the client would look like

However, both of these suggestions would require you to modify your client-side a bit, why is why I’ve simply listed them as “Suggestions” for reference.

Really sorry that this message has been this long but I hope you find something of value in it. Cheers.

2 Likes

Hi there!

Before I get into your revision and your points, I want to say thank you so much for taking so much time to explain to me how certain things work and even revising my code, really I appreciate it a lot. :heart:

To answer this:

I first fire FOVRemote and IGMRemote before Async to communicate with the client script with the default values if a person doesn’t have data stored in the data store. In this case, the FOV would be 50 and IGM would be 80 (0.8 since Sound.Volume works with decimals)

Now, if I’m honest with you, reading my code again, you may be right about not having to fire it twice. I just made it for precaution because I thought that (for some reason) it wouldn’t work since it’s the first time a user joins.

Agh, shoot. I must have rushed it yesterday. Thank you for bringing it to my attention! I swear that went through my mind last night.

As for the suggestions, I would be really happy to do them! Honestly, if that means improving my skills in DataStores whilst optimizing a pretty heavy game would be great!

1 Like

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