views:

213

answers:

3

When you have a multiplayer game where the server is receiving movement (location) information from the client, you want to verify this information as an anti-cheating measure.

This can be done like this:

maxPlayerSpeed = 300; // = 300 pixels every 1 second
if ((1000 / (getTime() - oldTimestamp) * (newPosX - oldPosX)) > maxPlayerSpeed)
{
   disconnect(player); //this is illegal!
}

This is a simple example, only taking the X coords into consideration. The problem here is that the oldTimestamp is stored as soon as the last location update was received by the server. This means that if there was a lag spike at that time, the old timestamp will be received much later relatively than the new location update by the server. This means that the time difference will not be accurate.

Example:

  1. Client says: I am now at position 5x10
  2. Lag spike: server receives this message at timestamp 500 (it should normally arrive at like 30)
  3. ....1 second movement...
  4. Client says: I am now at position 20x15
  5. No lag spike: server receives message at timestamp 1530

The server will now think that the time difference between these two locations is 1030. However, the real time difference is 1500. This could cause the anti-cheating detection to think that 1030 is not long enough, thus kicking the client.

Possible solution: let the client send a timestamp while sending, so that the server can use these timestamps instead

Problem: the problem with that solution is that the player could manipulate the client to send a timestamp that is not legal, so the anti-cheating system won't kick in. This is not a good solution.

It is also possible to simply allow maxPlayerSpeed * 2 speed (for example), however this basically allows speed hacking up to twice as fast as normal. This is not a good solution either.

So: do you have any suggestions on how to fix this "server timestamp & latency" issue in order to make my anti-cheating measures worthwhile?

+1  A: 

Smooth out lag spikes by filtering. Or to put this another way, instead of always comparing their new position to the previous position, compare it to the position of several updates ago. That way any short-term jitter is averaged out. In your example the server could look at the position before the lag spike and see that overall the player is moving at a reasonable speed.

For each player, you could simply hold the last X positions, or you might hold a lot of recent positions plus some older positions (eg 2, 3, 5, 10 seconds ago).

Generally you'd be performing interpolation/extrapolation on the server anyway within the normal movement speed bounds to hide the jitter from other players - all you're doing is extending this to your cheat checking mechanism as well. All legitimate speed-ups are going to come after an apparent slow-down, and interpolation helps cover that sort of error up.

Kylotan
The position of several updates ago will still exceed the maximum allowed distance by a bit because of one or two lag spikes in between. What treshhold would you think is good to add into consideration? maxSpeed * 1.1, aka 10%?
Tom
A potential flaw in your system could exist in the beginning of the game. If you only have two position updates so far, you'd have to use the previous one. If that is a lag spike, it would kick you. If you ignore the first few updates at the server side, that would mean that players can always teleport by restarting the game and then teleport once each restart. Any ideas on that?
Tom
I think the threshold would have to be something you pick through experimentation - you might want something different for an FPS than you would for an MMO, for example. But remember your server is the authority here - if someone is consistently 10% too fast then you can just move them back.As for the flaw, that is inherent to the problem - to estimate the derivative of a value you need at least 2 samples of that value first. Again, your server should be authoritative - push the player back into a reasonable place along their direction of travel. Just don't assume they're cheating (yet).
Kylotan
I don't think that answers what to do when there's less than 3 recordings. Sorry about the confusion but I'm not sure how to implement it otherwise.
Tom
If your measurement system requires 3 recordings then you wait until you have 3 recordings before you start checking the measurements for validity. Just like if you were measuring speed naïvely you would need to wait for the 2nd recording.
Kylotan
That would mean that they can 1) teleport far away and then 2) teleport back and the anti-cheating system would not kick in. That's not acceptable.
Tom
You'd stop that by snapping them back on the second position they send you since your server knows there is no way they could possibly be there. But you wouldn't assume they were necessarily cheating until you had a few more samples.
Kylotan
Interesting, so I would have to combine several methods. Yours and mine. I am not completely convinced that having multiple samples is going to solve the problem though, as there will also be multiple lag occasions which could imbalance the time differences.
Tom
It's not so much several methods, as several problems. One is smoothing out jitter to get accurate positions. Another is checking for potential cheating. They share the same data but should handle it in different ways. It's quite common to snap someone backwards into a less distant position without assuming they were cheating to reach the more distant position.
Kylotan
A: 

No no no.. with all due respect this is all wrong, and how NOT to do it.

The remedy is not trusting your clients. Don't make the clients send their positions, make them send their button states! View the button states as requests where the clients say "I'm moving forwards, unless you object". If the client sends a "moving forward" message and can't move forward, the server can ignore that or do whatever it likes to ensure consistency. In that case, the client only fools itself.

As for speed-hacks made possible by packet flooding, keep a packet counter. Eject clients who send more packets within a certain timeframe than the allowed settings. Clients should send one packet per tick/frame/world timestep. It's handy to name the packets based on time in whole timestep increments. Excessive packets of the same timestep can then be identified and ignored. Note that sending the same packet several times is a good idea when using UDP, to prevent package loss.

Again, never trust the client. This can't be emphasized enough.

Mads Elvheim
1) I am using button states, the location updates are only there for corrections which are needed because of lag 2) I do not trust the client, because if I would I would not be verifying it - just sending button states will not keep players synchronized.
Tom
Your client data is button states? That's not what your summary says:_When you have a multiplayer game where the server is receiving movement (location) information from the client, you want to verify this information as an anti-cheating measure._What I mean with "trusting the clients", I mean giving them any chance to be authoritative. A global world position for the client is authoritative. A "move forwards" state is not. There is a vital difference.It would be nice if you could clarify whether this is lag/latency circumvention, or an anti-cheat measure. I'm confused (see above).
Mads Elvheim
@Mads, sending button states is fine but most games are going to have to run a separate predicted simulation on the client as well anyway. So the problem reverts to keeping that client simulation in sync with the server either way, and you get fewer problems with jitter if you send positions. World of Warcraft is one of many games that has the client report their own position.
Kylotan
Mads, all my summary says is that the game sends the server movement (location) information. This is true, even with my button state system. Like I said earlier, these locatin information updates are there to synchronize the clients in case there has been lag. A client could send an invalid location update which would lead to invalid correction, so he could teleport. This is where the anti-cheating comes in at the server's side. I disagree that all movement should be calculated by the server, professional multiplayer games haven't chosen this path either.
Tom
Could depend on the game type, but integrating the world state on the server is very much the standard de-facto way to do it in FPS games. So on the contrary, I'll claim the majority of games choose this scheme.Unless you give some extremely compelling reason to send positions to the server, my answer still stands. That is, I think it's a generally bad idea. Nothing good can come out of it. Your timestamp problem should not be an issue though. Enforce the rule that one worldframe means one client packet. Ignore packets from the future and packets from the (ancient) past.
Mads Elvheim
It might be that small-scale multiplayer games do choose such method, I am not very experienced there. I'm thinking about a massive multiplayer game, and so far they all seem to let the client do the resource expensive calculations while letting the server verify it (less resource intensive). Could you elaborate the "Enforce the rule that one worldframe means one client packet. Ignore packets from the future and packets from the (ancient) past."? When exactly would you ignore certain messages and why? It sounds interesting but I'm lost here. Thanks in advance.
Tom
Perhaps it was a bad assumption, but I assumed you used integration to advance the server and client state(s), and a fixed timestep for the simulation. Say, if you want the simulation to run in 50 fps, you get a time delta of 0.02. The packets the clients and the server send to each other must lie exactly on one such time delta. You don't think in seconds then, but rather in "number of simulation frames". Since the server time is authoritative, any packet with a "future" timestamp is not legit; clients can not catch up with the server. And "too old" timestamps are simply too late.
Mads Elvheim
For MMO's, the expenses aren't calculations. They're rendering the scene (large number of meshes with a large number of effects) and data delivery. The more authoritative the client is, the more data it needs to communicate to the server. If the server is authoritative, the client sends what it wants to do and the server broadcasts the effect to everything in range. Additionally, if the client takes a long time processing something, synchronizing the result still requires server interaction and you are more likely to see something that didn't happen on the client.
Matt
That was my line of thinking as well. You need to sane-check the client data and step the simulation anyway, so in my head such a scheme just means the same work done server-side, but with less security and less stability. Unless someone has solved the halting problem lately.
Mads Elvheim
+1  A: 

Regardless of opinions on the approach, what you are looking for is the speed threshold that is considered "cheating". Given a a distance and a time increment, you can trivially see if they moved "too far" based on your cheat threshold.

time = thisTime - lastTime;
speed = distance / time;
If (speed > threshold) dudeIsCheating();

The times used for measurement are server received packet times. While it seems trivial, it is calculating distance for every character movement, which can end up very expensive. The best route is server calculate position based on velocity and that is the character's position. The client never communicates a position or absolute velocity, instead, the client sends a "percent of max" velocity.


To clarify: This was just for the cheating check. Your code has the possibility of lag or long processing on the server affect your outcome. The formula should be:

maxPlayerSpeed = 300; // = 300 pixels every 1 second
if (maxPlayerSpeed < 
    (distanceTraveled(oldPos, newPos) / (receiveNewest() - receiveLast()))
{
   disconnect(player); //this is illegal!
}

This compares the players rate of travel against the maximum rate of travel. The timestamps are determined by when you receive the packet, not when you process the data. You can use whichever method you care to to determine the updates to send to the clients, but for the threshold method you want for determining cheating, the above will not be impacted by lag.

Receive packet 1 at second 1: Character at position 1

Receive packet 2 at second 100: Character at position 3000

distance traveled = 2999

time = 99

rate = 30

No cheating occurred.

Receive packet 3 at second 101: Character at position 3301

distance traveled = 301

time = 1

rate = 301

Cheating detected.

What you are calling a "lag spike" is really high latency in packet delivery. But it doesn't matter since you aren't going by when the data is processed, you go by when each packet was received. If you keep the time calculations independent of your game tick processing (as they should be as stuff happened during that "tick") high and low latency only affect how sure the server is of the character position, which you use interpolation + extrapolation to resolve.

If the client is out of sync enough to where they haven't received any corrections to their position and are wildly out of sync with the server, there is significant packet loss and high latency which your cheating check will not be able to account for. You need to account for that at a lower layer with the handling of actual network communications.

For any game data, the ideal method is for all systems except the server to run behind by 100-200ms. Say you have an intended update every 50ms. The client receives the first and second. The client doesn't have any data to display until it receives the second update. Over the next 50 ms, it shows the progression of changes as it has already occurred (ie, it's on a very slight delayed playback). The client sends its button states to the server. The local client also predicts the movement, effects, etc. based on those button presses but only sends the server the "button state" (since there are a finite number of buttons, there are a finite number of bits necessary to represent each state, which allows for a more compact packet format).

The server is the authoritative simulation, determining the actual outcomes. The server sends updates every, say, 50ms to the clients. Rather than interpolating between two known frames, the server instead extrapolates positions, etc. for any missing data. The server knows what the last real position was. When it receives an update, the next packet sent to each of the clients includes the updated information. The client should then receive this information prior to reaching that point in time and the players react to it as it occurs, not seeing any odd jumping around because it never displayed an incorrect position.

It's possible to have the client be authoritative for some things, or to have a client act as the authoritative server. The key is determining how much impact trust in the client is there.


The client should be sending updates regularly, say, every 50 ms. That means that a 500 ms "lag spike" (delay in packet reception), either all packets sent within the delay period will be delayed by a similar amount or the packets will be received out of order. The underlying networking should handle these delays gracefully (by discarding packets that have an overly large delay, enforcing in order packet delivery, etc.). The end result is that with proper packet handling, the issues anticipated should not occur. Additionally, not receiving explicit character locations from the client and instead having the server explicitly correct the client and only receive control states from the client would prevent this issue.

Matt
I don't see how your system can take care of synchronisation correction, as the other clients do not know the exact position of the corresponding client. If you want to let the server calculate the position and then send it to the client, see the responses I made to Mads.
Tom
Thanks for the clarification but it doesn't make much sense to me as I am already using the time when the server receives the message, not when it is being processed. In your example everything works fine sure, but when the first package is being delayed it will show position 1 at time 50 instead of 1, for example. Then when the second package has no delay, it will have position 3000 at time 100. That's 50 time difference, which could make it detect it as cheating. I don't see how your system is solving this as you are using the time when the package is received by the server.
Tom
This method requires a "baseline" position and a baseline "timestamp". The way to resolve that is in the connection negotiation. Server sends client start position. Client responds with the exact same position, any deviation = cheating. You have the time difference and position difference. A delay in client processing is covered. Network latency is covered. This isn't round trip times, only when the server receives it. In your example, you weren't using server received time, you were using current time. Even if it took 1/10 of a second, it could tick to the next and violate the time value.
Matt
I still don't get it, I never said that I thought you were talking about round trip times. It's indeed the time where the server receives it, and like I said before, this can vary. So when the first message is received in 100ms and the second in 25ms that will cause trouble. I still don't see where your system is solving that issue.
Tom