Game breaks after the SetAsync pcall

I’ve done a lot of debugging and for some reason in my module script it only gets to my “got through this” print. This means that my SetAsync pcall is causing something. I just don’t know what. It actually also prints the savedData after that print but thats basically just saying that this line is causing the problem:

Problem:

Saver:SetAsync(tostring(plr.UserId),savedData)

Module Script:

local DDS = game:GetService("DataStoreService")
local Saver = DDS:GetDataStore("SaveLeaderstatsV2")
local rp = game:GetService("ReplicatedStorage")

local module = {}

local Devs = {
	[3653923696] = true,
	[134989192] = true
}

local Ranks = {
	[3] = {
		["[Honored One]"] = 50, 
		["[Special Grade]"] = 20, 
		["[1st Grade]"] = 15
	},
	[2] = {
		["[Semi-1st Grade]"] = 12, 
		["[2nd Grade]"] = 9
	},
	[1] = {
		["[3rd Grade]"] = 6,
		["[4th Grade]"] = 4 
	},
	[0] = {
		["[5th Grade]"] = 2,
		["[Homeless]"] = 0
	}
}

function module.SaveRank(plr)
	print("got to the start")
	local savedData = {}
	
	for _,v in pairs(plr.leaderstats:GetChildren()) do
		savedData[v.Name] = v.Value
	end
	print("got through this")
	local Yes, errorMessage = pcall(function()
		print(savedData)
		Saver:SetAsync(tostring(plr.UserId),savedData)
	end)
	print("got to this part")
	if not Yes then
		error(errorMessage) -- womp womp if this happens to you
	end
	print("got to the end")
end

Server Script:

local rankModule = require(script.InfoModule)

local players = game:GetService("Players")

players.PlayerAdded:Connect(function(plr)
	rankModule.checkRank(plr)
end)

players.PlayerRemoving:Connect(function(plr)
	warn("hi")
	
	local success, errorMsg = pcall(function()
		rankModule.SaveRank(plr)
	end)
	
	if not success then
		print("Error occurred: " .. errorMsg)
	end
	
	warn("after")
end)

--game:BindToClose(function() -- happens when the game shuts down
--	for _,v in pairs(players:GetPlayers()) do
--		rankModule.SaveRank(v)
--	end
--end)

There is more code to the module script but I just thought it was too overwhelming and it’s not actually useful in fixing this problem so why would I share it and give you guys a harder time. Also it doesn’t print any errors in the output.
image

plz someone help!!! :pray: :pray: :pray:
‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎ ‎

If you remove this line, does the rest of the code execute?

yes, the rest of the code runs if I comment out or delete that line of code.

The only explanation is that SetAsync is yielding on the pcall, but it shouldn’t be doing that.

Try removing the pcall block and letting the SetAsync run outside.

looks like it worked! But then, how will I know if a player gets an error? And why exactly is it doing this?

:SetAsync will yield because it has to make a request to an external server to save the data which won’t happen instantly. Because it’s yielding, the server then shuts down before the request can complete. SetAsync yielding is expected behaviour and it is what you want to happen.

The solution here isn’t to move your :SetAsync call outside of a pcall (pcall doesn’t actually create a new thread or anything, it just catches errors, so it will yield). It’s to call game:BindToClose and then save the data for each player inside of the function you pass to it, so the server shutdown doesn’t happen until you finish saving data.

If you un-comment this part, you should get your expected result and should also be able to see all of the prints.

1 Like

Thank you so much, now I finally understand.

1 Like

Thank you for the explanation, but I believe the problem still persists.

SetAsync is known to yield. That part is fine.

However, the behavior of SetAsync suddenly changes when it’s moved outside of the pcall() block. Originally, it yields indefinitely, but outside of the pcall statement it does not yield.

@MonsterTrident have you confirmed that :BindToClose() actually fixes the problem?

I’ve actually copied your code in a test place now and seems to work fine both with :BindToClose enabled and disabled. Are you sure there’s no other moving parts that is messing up the script?
image

Your results can vary depending on things like Roblox’s server speed and (I’d assume) server stress, but regardless, the problem that op is facing is that the server is being shut down prematurely, thus all of the game’s threads will be terminated (including the thread created by players.PlayerRemoving) and the server will be shut down before all data can save.

The only way to delay the server’s shutdown is to call game:BindToClose and in the function passed to it, yield until all data is saved.

You can try this yourself, a very artificial example:

local players = game:GetService('Players')

players.PlayerRemoving:Connect(function(player)
	print('PlayerRemoving called') -- will run
	
	task.wait(3)
	
	print('This part was reached') -- will not run
end)

however if you yield inside of game:BindToClose, for, say, 4 seconds, it will run:

local players = game:GetService('Players')

players.PlayerRemoving:Connect(function(player)
	print('PlayerRemoving called') -- will run
	
	task.wait(3)
	
	print('This part was reached') -- now, because we called game:BindToClose, will run
end)

game:BindToClose(function()
	task.wait(4)
end)

2 Likes

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.