In my game you can wager money when playing a match. Once the match starts your money is deducted and when the match ends the winner gets all the earnings. Problem is whenever i shut down servers the players lose their money.
Im using a BindToClose function to compensate anyone that wagered money during the time of a shutdown but it doesn’t seem to work or even run.
Code:
local shuttingDown = false
Players.PlayerRemoving:Connect(function(plr)
if shuttingDown then return end
dataModule.Save(plr)
end)
game:BindToClose(function()
shuttingDown = true
for _, plr in Players:GetChildren() do
local compensation = plr:GetAttribute("Compensation")
local cash = plr:FindFirstChild("leaderstats") and plr.leaderstats:FindFirstChild("Cash")
if cash and compensation then
cash.Value = cash.Value + compensation
dataModule.Save(plr)
end
end
end)
I don’t see an obvious reason as to why it wouldn’t be working, so all I have is questions.
Do some players get a refund while some don’t?
Do you get refunded in a studio environment?
Does the dataModule.Save function use task.wait()?
When the server shuts down a random player gets the win and the cash. Which means the match end/reward function is running aswell as the save function. Also The save function doesn’t use task.wait().
I also have an autosave function, maybe that has something to do with it?
--- Leaderboard
local waitTime = 5*60
task.spawn(function()
while wait(waitTime) do
for _, plr in Players:GetPlayers() do
dataModule.Save(plr)
dataModule.UpdateCash(plr)
dataModule.UpdateWins(plr)
dataModule.UpdateDonations(plr)
end
dataModule.GetTopPlayers()
end
end)
Autosaving would not have an affect on :BindToClose() because it’s the last thing to be done by a Roblox Server. So your auto save loop would stop running before :BindToClose() is fired.
The problem with your :BindToClose() is not very apparent with the code provided.
Could I see the match end and reward functions?
Compensation attribute gets set in the game end/reward function, I think its being removed before BindToClose gets the chance to get the attribute. Gonna remove it and see if that changes anything.
This is DataModule.Save:
function module.Save(plr) -- Save Player Data
if plr:GetAttribute("DataLoaded") ~= true then
warn("Data Not Loaded: "..plr.Name.." | "..plr.UserId)
return
end
local data = module.SaveData(plr)
local tries = 0
local success = false
local err
repeat
success, err = pcall(function()
ProgressStore:SetAsync(plr.UserId..dataStoreKey, data)
end)
if success then
print("Data Saved Successfully: "..plr.Name)
module.UpdateCash(plr)
module.UpdateWins(plr)
module.UpdateDonations(plr)
else
warn("Save Failure: ".."Progress | "..plr.Name.." | "..plr.UserId)
warn(err)
tries += 1
task.wait(5)
end
until success or tries == 3
end
Ok, yeah, I asked for the save function to see if there was any new thread being spawned, which would keep your BindToClose call from yielding, shutting down the server. Looks like this isn’t the case.
So the attributes happen after the round ends? Wouldn’t you want to set the attribute before the round starts, then remove it on round end, so game:BindToClose would catch it in the case of the server abruptly shutting down?
You mention in your original post that it doesn’t look like your game:BindToClose function is running, did you add any prints or anything for that? Could you try seeing if there are any players in game when the function is called (print(players:GetChildren()))? That’s the only thing I can see here that could make it seem like it isn’t running.
I also wonder if players:GetPlayers would maybe work better here? For example if there’s some edge case where the player isn’t a descendant of the DataModel but still somehow needs to be accounted for. I doubt this is the case, I don’t know the implementation details of getplayers but who knows
Could you also try seeing if the game.BindToClose is even being run in the first place? Maybe there could be something before it that’s keeping it from being run. Just add a print right before the game.BindToClose call and see
It’s possible that it is yielding too long, given that it has to wait for each player to be saved. Maybe if you use task.spawn on each of the player objects, and have the script wait until all players are saved (but have the actual saving being done in parallel), it might resolve your issue.
just ran some prints in on the server and shut it down, looks like BindToClose runs both after match end & playerremoving. Doubt there would even be a way to stop that. Would it be a bad idea to just add a wait to the player removing or match end function?
I believe problem is located in how you store data, see you should store it inside replicated storage, not under player, player is quickly removed soo it may cause the problem
EDIT: Also you shouldn’t use SetAsync as it’s unsafe and unstable, you should always use update async and have some sort of Id value that will tell if your data can be overwritten or not