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.
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
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.
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.