Safe use of InvokeClient?

Through my testing, this has seemed to work pretty consistently, and I haven’t had any problems with stuff not being returned. Basically the client has 30 seconds to make a move, if they dont then I’ll get a random move generated. However, I don’t know how exploits work, so I’m unsure if this system could be ‘rigged’

I am aware of exploiters being able never return the InvokeClient, but the delay function seems to remedy that problem. I just wanna make sure this code is as efficient as can be :grimacing: as this seems a lot more linear than using remote events and waiting for an event to be fired back.

local Result

coroutine.wrap(function()
	Result = PlacePiece:InvokeClient(PlacingPlayer) -- Get player to place piece
end)()

WaitTurn:FireClient(WaitingPlayer) -- Make player wait turn

delay(Timer, function() -- Prevents nothing being returned from client
	if not Result then
		Result = true -- Get randomized default result
	end
end)

-- Wait for a result
repeat
	wait()
until Result
								
if Result then
	print("WE GOT SOMETHING FROM", PlacingPlayer)

	Board.Turn = Board.Turn == 1 and 2 or 1
end
1 Like

If InvokeClient has no timeout of its own it’s likely that you will end up leaking memory from this. It’s also possible that an error could show part of the server stack trace if those still behave like that, which could expose information for reverse engineers.

Relying on callbacks in this case seems messier than with events. Have you considered a pattern where only 1 connection is made like this?

local active, inactive = player1, player2
local timeout = os.clock() + 30

local function on_player_place(player, result)
    if player ~= active then return end

    active, inactive = inactive, active
    timeout = os.clock() + 30

    -- do something with the result
end

PlacePiece.OnServerEvent:Connect(on_player_place)

while game_running do
    if os.clock() > timeout then
        -- handle inactivity
        timeout = os.clock() + 30
    end

    wait(1)
end
4 Likes

I was worried about using a remove event due to my games being set up in a for loop, with while loop inside as well, and possibility of other games affecting the wrong game

for _, board in pairs(CollectionService:GetTagged("Connect4")) do
	if board.PrimaryPart then
		local Board = GenerateGame(board) -- Generate board
		-- Go through all seats
		for _, seat in pairs(board:GetDescendants()) do
			if seat:IsA("Seat") then
				seat.Changed:Connect(function()
					if seat.Occupant then -- Player has sat down
						local Character = seat.Occupant.Parent
						local Player = Players:GetPlayerFromCharacter(Character)
						if not Player then return end
						
						if not Board then return end -- Game doesn't exist
						
						Board[seat.Parent.Name] = Player
						
						if Board.Seat1 and Board.Seat2 then
							Board.InProgress = true -- Start game
							
							StartMinigame:FireClient(Board.Seat1, "Connect4", seat, Board.Seat2)
							StartMinigame:FireClient(Board.Seat2, "Connect4", seat, Board.Seat1)
							
							board.InProgress.Value = true
							board.Billboard.Enabled = false
						else -- Only 1 player
							board.Billboard.Enabled = true
						end
						
						if not Board.InProgress then return end -- No game
						
						while Board.InProgress do
							local PlacingPlayer = Board["Seat" .. Board.Turn]
							local WaitingPlayer = Board["Seat" .. (Board.Turn == 1 and 2 or 1)]
							-- Etc....
						end
					end
				end						
			end
		end
	end
end

And if whether or not I should be do the listen for OnServerEvent within the while loop, or outside everything, memory leaks being created due to multiple listens of OnServerEvent, etc.

InvokeClient basically like a Remote thing but with result you may use the teleportservice version.