How to check ping and use it for WalkSpeed anti exploit?

I’m trying to develop an anti exploit but am very new at it. I’m aiming for a ServerSided Ant Exploit and need to ensure the player doesn’t go over a stud limit based on the players ping.

  1. how can I check the players ping?
  2. How can I ensure exploiters don’t alter the ping parameter to fool the server with false information?
  3. formula I can use with ping to find maximum distance that player can go?
4 Likes

You check the players ping by sending the client’s time whenever it sends a RemoteEvent (you can plug this into as many RemoteEvents as you want or just have one dedicated to it), then when the server receives it subtract the server’s time by the time given by the client. Start to average it out over time and make sure your averages aren’t global (otherwise outliers will destroy it). Instead, have ping average in the context of a short period of time (i.e. average out their pings in the last ten seconds).

You make sure they can’t alter the ping parameter by checking on the server if all the times are valid, not ahead of the server, etc.

The most a player can go with a latency of n seconds (assuming they’re frozen in place for all that time) is just that time multiplied by their walk speed. Be generous and have a margin of error, and don’t immediately start kicking people when they break this limit. Ping is variable.

5 Likes

So would the average out have to be 10 seconds?

No, it was just an example.

This is not how you get ping.

You get ping by getting the local time, asking the client to respond, then checking the difference between the new local time and the saved local time:

local start = tick()
script.PingRemoteFunction:InvokeClient()
local ping = tick() - start
-- you should actually use RemoteEvents instead, but that's too long for an example

There is no way to guarantee that the server and client’s clocks are synced up enough to tell differences in milliseconds. In fact, it’s very likely that the server, client, and all other clients have clock differences of multiple milliseconds, maybe tens of milliseconds, or multiple seconds! It’s not possible to 100% accurately measure the single-trip time.

This is also called round-trip time, because it’s the time it takes for a message to go to and back i.e. a round-trip.


You can’t very accurately change the allowed player velocity according to ping. The most you can do is say “the player could have been moving for as long as their ping time without the server knowing”.

Instead, you should have higher thresholds than the actual speed and up those thresholds some for high ping. For example, have a normal limit/threshold of 20 studs/per second, but with a 1 second ping you up it to 30 per second.

If it’s not clear, you should not have a threshold/limit of 16 studs per second/walkspeed. I noticed in one of your previous posts that you had your threshold set that low. That’s way too close! It’s expected that some exploiters can barely get through with a slight advantage. There will always be an overlap between network latency, player skill, and exploiters. To catch all exploiters, you would also have to catch all high-ping players and high-skill players, which would not be a good thing.


Edit: Example using RemoteEvents
-- server
local pings = {}

game.ReplicatedStorage.PingRemoteEvent.OnServerEvent:Connect(function(player)
	local pingInfo = pings[player]
	if pingInfo and pingInfo.sent then
		pingInfo.received = tick()
		pingInfo.ping = math.clamp(pingInfo.received - pingInfo.sent, 0, 5)
		pingInfo.sent = nil
	end
end)

local function playerAdded(player)
	pings[player] = {
		ping = 1
	}
end

game.Players.PlayerAdded:Connect(playerAdded)
for _, player in next, game.Players:GetPlayers() do
	playerAdded(player)
end

game.Players.PlayerRemoving:Connect(function(player)
	pings[player] = nil
end)

spawn(function()
	while true do
		for player, pingInfo in next, pings do
			pingInfo.sent = tick()
			game.ReplicatedStorage.PingRemoteEvent:FireClient(player)
		end
		wait(5)
	end
end)

local function getPing(player)
	return pings[player] and pings[player].ping or 1
end
-- client
game.ReplicatedStorage.PingRemoteEvent.OnClientEvent:Connect(function(player)
	game.ReplicatedStorage.PingRemoteEvent:FireServer()
end)
21 Likes

I completely blanked on the definition of ping. This post is correct.

However, using RemoteEvents instead of RemoteFunctions is NOT advisable here. You’re just adding extra overhead for the exact same effect. This is one of the few cases where RemoteFunctions are the correct solution.

1 Like

Any way to tell if the client is exploiting their ping/remote fire delay?

1 Like

If they’re sending lots of other events in the meantime.

2 Likes

It’s never safe to ask the client something i.e. use InvokeClient. The client can error, disconnect, or yield forever. Then what?

It’s always save to use InvokeServer, and in my opinion it’s rarely ever safe to use InvokeClient. Check out the example code I added to my original post.


No way that I’m aware of. You could have penalties for too high ping. It’s entirely possible for an exploiter to actually increase their ping for their whole network if they wanted, too.

If you use something like my example, you could check if the sent time was too long ago and give a penalty if it is. Any real player with a reasonable connection would respond within two seconds in the worst case scenario (e.g. 3G mobile network that’s switching connections, satellite internet that can barely play Roblox, etc.) Most players will respond within 0.3 seconds.

3 Likes

In your example, you are using RemoteEvents in the same vein as RemoteFunctions. If the client errors, disconnects, or yields, then you don’t use the same thread. They’ll be failing your ping checks, and you can sufficiently say that they’re either horribly laggy (which nobody enjoys) or are a hacker.

1 Like

It’s impossible to differentiate between someone using a network lagswitch and someone actually pinging bad. I think this solution I proposed earlier today may be a tad bit more reliable.

PS: These threads are closely related. If you have any more questions in regards to your anti-exploit, making a new thread may not be the best idea if it’s closely related to a previous one.

2 Likes

I think RemoteEvents are a much cleaner way to do this. If you implement it using RemoteFunctions then you have to create a new coroutine every time you call it and do error checking. I’ve rewritten the example using RemoteFunctions and it looks messier and harder to understand at a glance for me.

Additionally, it’s easy to forget to use a new coroutine and implement error checking, and even easier to forget or forgo for any beginners that might be reading this and don’t know how to do those things. Using RemoteEvents prevents even the chance of leaving those things out, as they’re not necessary when using RemoteEvents. RemoteEvents do not add significant overhead.

Example
-- server
local pings = {}

local function playerAdded(player)
	pings[player] = {
		ping = 1
	}
end

game.Players.PlayerAdded:Connect(playerAdded)
for _, player in next, game.Players:GetPlayers() do
	playerAdded(player)
end

game.Players.PlayerRemoving:Connect(function(player)
	pings[player] = nil
end)

spawn(function()
	while true do
		for player, pingInfo in next, pings do
			coroutine.wrap(function()  -- we use coroutine.wrap because spawn does not start instantly
				local success, err = pcall(function()
					pingInfo.sent = tick()
					game.ReplicatedStorage.PingRemoteFunction:InvokeClient(player)
				end)
				if not success then
					warn("Failed to get ping: "..tostirng(err))
				else
					pingInfo.received = tick()
					pingInfo.ping = pingInfo.received - pingInfo.sent
				end
			end)()
		end
		wait(5)
	end
end)

-- technically you could take out the pcall and let it error, but that just sounds bad to me

local function getPing(player)
	return pings[player] and pings[player].ping
end
-- client
game.ReplicatedStorage.PingRemoteEvent.OnServerInvoke = function(player)

end

There is the upside that you can catch if the client errors for any reason, such as injected code.

There’s a few posts in this thread that share my opinion: “RemoteFunctions are unsafe.” Additionally, the very last post states that it’s actually faster to use RemoteEvents than RemoteFunctions i.e. one RemoteFunction call has more overhead than two RemoteEvent fires.

I’m not going to tell you to never use RemoteFunctions for stuff like this. If you like it better then feel free to use them. I think there are many good reasons to use RemoteEvents instead, though, and I’ve attempted to bring some up for consideration.

1 Like

A thread which I don’t have access to.

For future reference;
D = (R / 2) * WS
Where D is the (rough) max distance (in studs) a client should be able to travel under R seconds of round-ping at WS walkspeed.

Value Type: float
Description: Used to set how quickly a Humanoid can walk in studs per second. The default value is 16.

Even then this may not be very accurate. WalkSpeed is one of the harder things to stop, since distance checks are only really effective while taking into account all sorts of things, which may include but are not limited to:

  • Network latency
  • Hardware framerate
  • Synchronization Client->Server

The fact is, if you have the math down to the last decimal, it’ll work perfectly on a holy and perfect connection, but would instantly die the second a network spike is hit.
Give it too much leniency and suddenly each little bit of that can be taken advantage.
Make it too strict and now your normal players on the lower end hardware (or even just environmental or geographical factors) are hit hard.

You have to compromise on this, and it’s probably not worth much of the effort.

1 Like

That’s one reason we need a Upcoming Developer tag as a reminder to only link to public topics or instead use quotes. You’re already aware of that, but I just thought I’d mention it for anyone else reading who hasn’t seen the thread.

Here are some relevant bits from that thread
  • 72% of voters use only RemoteEvents
  • 25% of voters use a combination of RemoteEvents and RemoteFunctions
  • 3% of voters use only RemoteFunctions

It’s “New Member” now. I don’t blame you if you’re losing track lol.

The problem with the possibility of players being flung around due to players colliding or a player jumping in a confined space could always throw some false-positives when working out the magnitude between two vectors. The same would apply if you were to teleport players.

I have made an experimental anti-speed exploit file. I noticed that quite a lot of false positives were thrown when including the Y vector in the calculation due to falling. Feel free to take a look at it, knowing me, I’ve probably done quite a few things wrong but it works as far as I can tell. I just haven’t really had the opportunity to test it with larger servers than 2-3 players.

1 Like

Or you can just ignore players with humanoid states like jumping, falling, free falling, and ragdoll.

So they’ll just set their states to those.

2 Likes

Does it replicate over FE?

EDIT: I’d test it myself if I could immediately.
I’m at school from 7-3 and I go on DevForum during passing periods and break.

1 Like