I am having an issue in my game, where sometimes, when the last player leaves, it runs both the player removing and game:BindToClose functions. This is a problem as this causes datastore requests to max out, causing players to lose data. Here is the code:
game.Players.PlayerRemoving:Connect(function(plr)
SaveData(plr)
end)
game:BindToClose(function()
for i, plr in pairs(game.Players:GetPlayers()) do
SaveData(plr)
end
end)
If the last player leaves and there are no players left in the game, it’s going to shut down. So when the last player leaves it’s obviously going to fire the BindToClose event. You should check if there are any players left in the game before trying to save their data. Maybe add a variable to the player that sets to true once their data has been saved and then check in the next SaveData function if it has been saved or not, and only save again if it hasn’t.
to prevent the data loss from happening, you have to check if there’s no players left in the game, as @CZXPEK and @WoTrox already said.
try using this code instead.
check = function()
if #game:GetService("Players"):GetPlayers() == 1 then
return false
else
return true
end
end
game.Players.PlayerRemoving:Connect(function(plr)
if check() == true then
SaveData(plr)
end
end)
game:BindToClose(function()
for i, plr in pairs(game.Players:GetPlayers()) do
SaveData(plr)
end
end)
Perhaps an attribute debounce? The first thing that comes to mind is adding the player to a table or assigning an attribute to the player and removing it after several seconds to prevent multiple save attempts.
local Saving = {}
game.Players.PlayerRemoving:Connect(function(plr)
if table.find(Saving, plr) then return end
table.insert(Saving, plr)
task.delay(5, -- time before the player can save again
table.remove(Saving, table.find(Saving, plr))
)
SaveData(plr)
end)
game:BindToClose(function()
for i, plr in pairs(game.Players:GetPlayers()) do
if table.find(Saving, plr) then continue end
table.insert(Saving, plr)
task.delay(5, -- time before the player can save again
table.remove(Saving, table.find(Saving, plr))
)
SaveData(plr)
end
end)
Or I reccommend ProfileService, a great datastore module that has already ironed all of these issues, with autosave and saving on bindtoclose.
When a server is shut down, does the player removing function run for all players? As I am using the BindToClose to prevent dataloss on server shutdowns.
Yes, session locking would help. I recommend profileservice, but if you only want a fix to your issue:
Keep track of wether a save is currently in progress for a player. This can be a simple array of player indices, and boolean values. Then at the beginning of the ‘SaveData’ function you don’t save if this value is set to true. Before the save async call, you set that value to true, and after the async call, set it back to false.
If this still doesn’t fix it, you can use a debounce.
maybe something simple like would work
edit: i saw @CremaCoffeeOverseer ddid the same thing
local UpdatedInTheLast5Seconds = {}
game.Players.PlayerRemoving:Connect(function(plr)
if table.find(UpdatedInTheLast5Seconds, plr.Name) then return end
table.insert(UpdatedInTheLast5Seconds,plr)
SaveData(plr)
wait(5)
table.remove(table.find(UpdatedInTheLast5Seconds, plr.Name))
end)
game:BindToClose(function()
for i, plr in pairs(game.Players:GetPlayers()) do
if table.find(UpdatedInTheLast5Seconds, plr.Name) then return end
table.insert(UpdatedInTheLast5Seconds,plr)
SaveData(plr)
wait(5)
table.remove(table.find(UpdatedInTheLast5Seconds, plr.Name))
end
end)