Using BindToClose() to compensate players for lost money

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)
1 Like

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)

Just warn players when it will shut down so they don’t start a match.

if i restart while they’re mid game a warning wouldnt make a difference. Plus i need to do restarts multiple times a day.

Hmmm. why do you need to restart the game? maybe there is a work around.

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?

Are you sure they have a compensation attribute on server shutdown? Can you show us what dataModule.Save looks like?

1 Like

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?

1 Like

Worded it wrong mb. Im setting the compensation attribute at the start and removing it at the end of the match.

Oh gotcha, no worries.

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

print('something')

game:BindToClose(function...
1 Like

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.

image

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