So, I was testing my game and I was in the Server Log and I noticed, people who had left the game, were still getting data autosaves. I’m really confused as this is near enough the same method as I’ve used in the past, and this has never happened. Anyone know the issue?
-- // Main Data Saving
game:GetService("Players").PlayerAdded:Connect(function(Plr)
-- // Leaderstats
local Stats = Instance.new("Folder", Plr)
Stats.Name = "leaderstats"
local Strength = Instance.new("IntValue", Stats)
Strength.Name = "Strength"
local Rebirths = Instance.new("IntValue", Stats)
Rebirths.Name = "Rebirths"
-- // Alt values
local RebirthStat = Instance.new("IntValue", Plr)
RebirthStat.Name = "RebirthPrice"
RebirthStat.Value = 500
local Scales = Instance.new("Folder", Plr)
Scales.Name = "Scales"
local BWS = Instance.new("NumberValue", Scales)
BWS.Name = "BWS"
local BHS = Instance.new("NumberValue", Scales)
BHS.Name = "BHS"
local BDS = Instance.new("NumberValue", Scales)
BDS.Name = "BDS"
-- // Loading stuff
local Data1 = Data:GetAsync(Plr.UserId)
if Data1 then
for i, v in pairs(Stats:GetChildren()) do
v.Value = Data1[v.Name]
end
for i, v in pairs(Scales:GetChildren()) do
v.Value = Data1[v.Name]
end
print(Plr.Name .. "'s data loaded successfully!")
end
-- // Saving stuff
spawn(function()
coroutine.resume(coroutine.create(function()
while true do
wait(60)
if Stats then
local SaveData = {}
for i, v in pairs(Stats:GetChildren()) do
SaveData[v.Name] = v.Value
end
for i, v in pairs(Scales:GetChildren()) do
SaveData[v.Name] = v.Value
end
Data:SetAsync(Plr.UserId, SaveData)
print(Plr.Name .. "'s data autosaved successfully!")
end
end
end))
end)
Plr.CharacterAdded:Connect(function(Char)
wait(1)
repeat wait() until Char:FindFirstChild("Humanoid")
local Humanoid = Char:WaitForChild("Humanoid")
local BDS = Humanoid:WaitForChild("BodyDepthScale")
local BHS = Humanoid:WaitForChild("BodyHeightScale")
local BWS = Humanoid:WaitForChild("BodyWidthScale")
end)
end)
game:GetService("Players").PlayerRemoving:Connect(function(Plr)
local Stats = Plr:FindFirstChild("leaderstats")
local WeightStats = Plr:FindFirstChild("Scales")
if Stats then
local SaveData = {}
for i, v in pairs(Stats:GetChildren()) do
SaveData[v.Name] = v.Value
end
for i, v in pairs(WeightStats:GetChildren()) do
if v.ClassName == "NumberValue" then
SaveData[v.Name] = v.Value
end
end
Data:SetAsync(Plr.UserId, SaveData)
print(Plr.Name .. "'s data saved successfully!")
end
end)
What I can gather from your script dump, is that you have a loop that checks if Stats then, inside of the following
spawn(function()
coroutine.resume(coroutine.create(function()
while true do
wait(60)
if Stats then
When the loop runs, it checks if the Stats variable has a value that is not nil or false - but this will always be the case, since the variable is never cleaned up, making that loop keep running even after the player leaves your game.
A possible fix is to break out of the loop whenever the Stats variable’s Parent property no longer refers to something.
On a separate note, it seems a bit overkill to use both spawn and a coroutine within each other.
Stats will never be empty, so what you need to do is add this line
if not Plr then break end this will stop the loop and reduces lagg if loads of players joined the server
Another way of doing it is putting the autosave outside the PlayerAdd connection in a loop that gets all current players in the game like so:
-- Simple Autosave loop:
while true do
-- Get current players playing:
for i, player in pairs(game.Players:GetPlayers()) do
wait(3) -- to not spam datastore for each player.
-- Check if players are still in the game:
if game.Players:FindFirstChild(tostring(player)) then
saveData(player)
end
end
wait(150) -- 3 minute cooldown.
end
Destroying an object clears its event listeners and recursively removes descendants while removing the object from its parent object.
However, it does not clear variable references to the object. As long as there’s a reference to the object that could possibly be used in the future, Lua cannot garbage collect the referenced object either, keeping it in memory, as far as I know of.
So instead of doing what the OP did, creating a Table for the Player’s Stat and using the PlayerObj as a key would solve the problem?
Because the Key is the Player and if the Player leaves the Key would be nil
All in all just checking if plr and blabla then code end is an easier approach I’m just curious.
-- Auto Saving
coroutine.resume(coroutine.create(function()
while plr and wait(300) do
if plr and plr.UserId > 0 and (PlrsData[plr]) and (PlrsData[plr].SaveData) then
local Success, Message = pcall(function() DSS:SetAsync(plr.UserId,PlrsData[plr].SaveData) end)
if Success then
print(plr.Name..' Auto-Saved')
else
print(plr.Name..' Auto-Saved Error = '..Message)
end
else
break
end
end
end))
That would make the player instance be kept in memory, even after they leave. Instead, you can listen to Players.PlayerRemoving, and set PlrsData[plr] to nil.
Just to sum up, destroying objects doesn’t clear their variable references.
Setting the variables pointing to the object in your code to something else will make it lose the references to that object, which will clear it from memory when there’s no chance that the object will be used in the code anymore.
Almost! Lua GC will only collect the player if you tell it to do so. With the __mode metamethod, you can tell it to collect keys, values or both (not supported on this version)! See weak tables (Wiki has been terrible these days, I didn’t find the tutorial on the new website ).
To do so:
Local PlayerData = {}
PlayerData.__mode = "k" --if nobody knows about my keys, they don't exist.
setmetatable(PlayerData, PlayerData)
Players.PlayerAdded:Connect(function(plr)
PlayerData[plr].Stats = "Stats"
-- Etc
end)