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):
- 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
- 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.