Handling RemoteFunction Error When Player Leaves

Warning: If a client disconnects or leaves the game while it is being invoked from the server, the InvokeClient function will error. It is therefore recommended to wrap this function in a pcall so it does stop the execution of other code. From The Wiki

So what’s the best way to handle this?

My current script

local TotalVotes = 5

local Success , Votes = pcall(function() return RFs:InvokeClient(Player) end)

TotalVotes = Success and Votes >= 5 and TotalVotes + 5 or Success and TotalVotes + Votes or TotalVotes + 1

So what it does is if the player leaves then it will do

TotalVotes = TotalVotes + 1 

or

TotalVotes = TotalVotes + Votes

if something was returned

or

Votes >= 5 and TotalVotes + 5

Just making sure the Vote isn’t more than 5 (Exploiters can spoof my RF and return a number more than 5)


Well do you have a better or a simplified way to Handle this?

IMHO, there really shouldn’t be a reason to use InvokeClient. It’s a weird anti-pattern. But…your code is pretty much correct as far as I can tell–at least the pcall part. I’m not sure how your logic is supposed to look for your voting calculation, but the client invocation is done properly.

2 Likes

This script is for my Comedy game, when RFs:InvokeClient(Player) is called the player get’s to vote between 1 to 5 for the Current Comedian that is why I’m using a RemoteFunction.

And the Logic

Success and Votes >= 5 and TotalVotes + 5 or Success and TotalVotes + Votes or TotalVotes + 1

works just fine it’s like using an if statement but it’s shorter


This might be something a little bit more understandable

TextLable.TextColor3 = Bool and Color.new(0,1,0) or Color.new(1,0,0)

is the same as

if Bool then
   TextLable.TextColor3 = Color.new(0,1,0)
else
   TextLable.TextColor3 = Color.new(1,0,0)
end
1 Like

You can use remote events. Send an event to the clients when they should start voting, then the clients send events whenever they’re done voting. After you have received votes from everyone or a certain time threshold is passed, you close the vote. This is a little bit less straightforward to program maybe, but should be a lot cleaner than a solution that uses invokes and has to wait for the client for individual requests + has to handle errors.

1 Like

I have thought of Using RemoteEvents but

After a few seconds if the Player hasn’t decided to Vote, the RF will return 1 to the Server Automatically.

&

A small part of the script

for i , v  in pairs(Players) do
 spawn(function()
   RF:InvokeClient(Player)
 end)
end

My Comedy game isn’t active anymore I’m working on a New game now, but I will have to use RemoteFunctions so I’m just curious if there is a better or simplified way to Handle the Error when a Player leaves.

(The game never hit front page & got only 100 Players at most so I moved on because the game died after SponsoredGame was over)

Yes, I understand exactly what your code does, don’t worry. What is unclean about this is that the server isn’t in control of how long the interaction takes (imagine if an exploiter holds back the request for a while), and based on your example code you have no way to tell when the voting is finished or when someone is done voting since you need to wrap it. The wrapping in separate threads in itself is unclean.

What you are actually doing here is firing off an “event” to each player in parallel (since you wrap the invoke requests and do nothing with the return value), so you are trying to use a RemoteFunction as a RemoteEvent in that code. Why not just use RemoteEvents to begin with?

The better/simplified way is really just to not use RemoteFunction::InvokeClient and instead decouple this with a back-and-forth event as explained above. This will lead to a code structure where you don’t need to deal with this particular error.

I’m confused, I am using the Returned Value, that small part of the script doesn’t cover everything my bad, but I provided the full script down below which should clear things up.

&

Here’s the full code (In short, the only thing the Exploiter can do if He/She holds the Request is not vote so the Comedian will loose 1 vote but that’s it)

-->> RoundComplete
local function RoundComplete(SelectedPlayer)
	local TotalVotes = 5
	if SelectedPlayer then
		REs:FireClient(SelectedPlayer,'OffStage')
	end
	workspace.Stage.Curtains.SFGui.Image:TweenSize(UDim2.new(1,0,1,0))
	if Comedian.Value then
		for i , v in pairs(Players:GetPlayers()) do
			spawn(function()
				if v ~= SelectedPlayer then
					local Success , Votes = pcall(function() return RFs:InvokeClient(v,'Vote',SelectedPlayer,PlrsData[SelectedPlayer].SaveData) end)
					TotalVotes = Success and Votes >= 5 and TotalVotes + 5 or Success and TotalVotes + Votes or TotalVotes + 1
				end
			end)
		end
		Text.Value = 'Time To Vote For '..SelectedPlayer.Name..'!'
		for i = 15 , 0 , -1 do
			Timer.Value = i
			wait(1)
		end
	end
	PlrsData:AddVotes(SelectedPlayer,PlrsData[SelectedPlayer].LeaderStatsClone,TotalVotes) -- TotalVotes
	REs:FireClient(SelectedPlayer,'UpdateData',PlrsData[SelectedPlayer])
	Text.Value = SelectedPlayer.Name..' Got '..TotalVotes..' Votes'
	Comedian.Value = nil
end

TotalVotes is = 5 because it’s the Votes from the Comedian, so you get at least 5 + how many votes you get


You are right tho, using a RemoteEvent would have been Cleaner & Easier to Handle.

but if this isn’t a case where I should use a RemoteFunction then when should I use it?

Here’s some pseudocode for that piece of code where it uses an event instead of a function:

-->> RoundComplete
local function RoundComplete(SelectedPlayer)
	local TotalVotes = 5
	if SelectedPlayer then
		REs:FireClient(SelectedPlayer,'OffStage')
	end
	workspace.Stage.Curtains.SFGui.Image:TweenSize(UDim2.new(1,0,1,0))
	if Comedian.Value then
		-- make all clients start voting:
		VotingEvent:FireAllClients(SelectedPlayer,PlrsData[SelectedPlayer].SaveData)
		local voted = {}
		-- clients fire event back when they're done:
		local conn = VotingEvent.OnServerEvent:connect(function(Player, Votes)
			if Player ~= selectedPlayer and not voted[Player] then
				TotalVotes = Votes >= 5 and TotalVotes + 5 or TotalVotes + Votes
				voted[Player] = true
			end
		end)
		Text.Value = 'Time To Vote For '..SelectedPlayer.Name..'!'
		for i = 15 , 0 , -1 do
			Timer.Value = i
			wait(1)
		end
		-- voting stopped:
		conn:disconnect()
	end
	PlrsData:AddVotes(SelectedPlayer,PlrsData[SelectedPlayer].LeaderStatsClone,TotalVotes) -- TotalVotes
	REs:FireClient(SelectedPlayer,'UpdateData',PlrsData[SelectedPlayer])
	Text.Value = SelectedPlayer.Name..' Got '..TotalVotes..' Votes'
	Comedian.Value = nil
end

As you can see, it didn’t have to deal with the error for when a player leaves. (Note it is pseudo, just to get the idea across, you’d have to define VotingEvent etc)

It is fine to use :InvokeServer because the server doesn’t suddenly die and even if so, the player would not be able to play the game anyway. The server is in control of returning the request. :InvokeClient is generally bad because of reasons described in a post above.

1 Like

Aren’t you assigning the return values of pcall to Success and Votes, respectively whether it executed without errors and what the error was?

Correct.

And then I use it here

TotalVotes = Success and Votes >= 5 and TotalVotes + 5 or Success and TotalVotes + Votes or TotalVotes + 1

Oh, I did not know it actually forwards the result of the successful call if it is. That’s something new learned today.

1 Like

The Error Message would be returned to Votes tho but in my case I don’t need to know what the Error is

just if it’s Success == true and then do TotalVotes = TotalVotes + Votes

Awesome!, I will learn from your Sample code and use it when appropriate.


So is this

local Success , Votes = pcall(function() return RFs:InvokeClient(v,'Vote',SelectedPlayer,PlrsData[SelectedPlayer].SaveData) end)

at least some what of a good way to handle this error if there is no other option but to use a RemoteFunction

2 Likes