How to detect when a player teleports?

Hello there. I’m currently working on a server-sided anti-cheat system, and so far it’s going pretty good. I was able to implement a very good NoClip detection system. I want to start working on detecting teleporters however, I’m not to sure on how I would go about doing it. Could someone give me some steps in the right direction on how to detect teleporters?

One idea that I had is, when the player moves more than 16 studs per second, we then see how long they were lagging. I then use pathfinding service, and some math to detect how long it would have taken the average player to get from it’s last position, to it’s current position, and if the player was lagging for longer than that amount of time, then there’s a pretty good chance that they were teleporting illegally.

My only worry is that, detecting ping relies on the client, and server (Server fires a remote, and relies on the client to fire a remote event in return). I don’t want to have anything to do with the client, because if it’s an exploiter, they could just disable the client sided part of the ping detection system, and they could pretend like they have infinite ping, even though they don’t.

3 Likes

You could implement an invisible trail system of attachments or stored Position values in a table that poll at a low hz or something and if those trail points are too far apart and the script tracking them wasn’t warned of an intentional teleport beforehard then have the automod take action. You could take it a step further and raycast between trail points to see if they went through walls too.

2 Likes

This is one decent way to do it and I have implemented an anti-teleport this way before (but without the ping part). It has flaws though.

That sounds like a cool way to calculate it, but it may not be ideal performance-wise, and PathfindingService is not perfect so it could introduce false positives.

It depends on what your game is and how the world is designed, but here are a few ideas I have:

  • If the world has a static layout, you can pre-calculate paths using a pathfinding algorithm and have the data in a table to use for fastest movement checks.
  • If the world does not have a lot of obstacles that take a long time to go around but quickly traversed by going through them, then you could use a naive but fast euclidian distance check (instead of pathfinding). You could take into account gravity for vertical euclidian movement. This can be combined with other validation checks specific to the game (player is in an area they don’t have unlocked, for example)

@TechnoTurbo’s solution with invisible trails also sounds interesting and could work.

This is correct, you should never rely on the client, especially for an anti-cheat system.

Also, kicking/punishing a player from an automatic cheat detection should be a last-resort choice. False positives happen and you don’t want innocent people kicked or auto-banned. For teleporting exploits, you can teleport them back to their last valid position; a lot of games outside Roblox do this. A false positive in this case will only result in what people usually call “rubberbanding”. You could log that this occurred for later investigation.

1 Like

Try doing it like how Soybeen, the creator of Booga Booga did it. He checks the studs moved and factors in walkspeed, velocity, jumping to calculate if the amount of studs they pass has logic. This also works as an anti-jump, anti-fly, and anti-speed detector

I’m sure both of these methods work. But how exactly do I get the ping of the player in a way that doesn’t rely on the client, so I can account for it?

There’s no way to get ping without client sided scripts. The best thing to do is to teleport them back to the last legal location, I believe this is what minecraft does.

You can’t rely on any ping calculations. As it is either not possible, and if it was, ping can fluctuate as different values for every packet which could throw things off and cause false positives.

An example of detecting teleporting using euclidian distance (not taking into account falling from gravity for simplicity) and not using ping, is the following:

local PlayerPositionHistory = {}
local ERROR_MARGIN = 2 --walkspeed error margin, in studs/sec, for potential laggy players
local CHECK_RATE = 1 --in cycles/sec (Hz)

local function OnPositionInvalid(player)
	--code that does something when the player's position is illegal
	--could teleport them to last legal position here
	print(("%s has moved illegally!"):format(player.Name))
end

local function ValidatePlayerPosition(player)
	local character = player.Character
	local data = PlayerPositionHistory[player]
	if (data and character) then
		local root = character:FindFirstChild("HumanoidRootPart")
		local currentPosition = root.Position
		local lastPosition = data.Position
		if (lastPosition) then
			local deltaTime = os.clock() - data.Time
			local studsTravelled = (currentPosition - lastPosition).Magnitude
			if (studsTravelled > character:FindFirstChildOfClass("Humanoid").WalkSpeed * deltaTime + ERROR_MARGIN) then
				return OnPositionInvalid(player)
			end
		end
		data.Position = currentPosition
		data.Time = os.clock()
	end
end

game:GetService("Players").PlayerAdded:Connect(function(player)
	while not player.Character do player:GetPropertyChangedSignal("Character"):Wait() end
	PlayerPositionHistory[player] = {
		Position = player.Character:WaitForChild("HumanoidRootPart").Position,
		Time = os.clock()
	}
end)

game:GetService("Players").PlayerRemoving:Connect(function(player)
	if (PlayerPositionHistory[player]) then
		PlayerPositionHistory[player] = nil
	end
end)

while true do
	for i, player in ipairs(game:GetService("Players"):GetPlayers()) do
		ValidatePlayerPosition(player)
	end
	wait(1/CHECK_RATE)
end
8 Likes