Victor Zhou
Victor Zhou

Reputation: 433

Inconsistent Date.now() values

I built an HTML5 multiplayer game that depends on having a reasonably accurate time sync between server and client. For the most part, the algorithm I use is very accurate -- all it does is estimate what the client-server time delta is, i.e. the difference between the current time on the server and the current time on client. For example, if the server time is exactly 5 seconds ahead of the client time, the time delta is 5000 ms.

The client and server (node.js) are both written in Javascript. The algorithm works as follows:

  1. Record time on the client: var clientTime = Date.now();

  2. Ping the server. When the server receives the message, it immediately sends a response containing just one thing: the time on the server when the message was received.

    var serverTime = Date.now();
    // Send serverTime to the client
    
  3. When the client receives the server response, immediately record the time: var clientTime2 = Date.now();

Now, we know that when the server received the message, the client time must have been somewhere between clientTime and clientTime2.

If the server received the message when client time was clientTime (i.e. client->server request took 0ms somehow), then the time delta is var delta1 = (serverTime - clientTime);

If the server received the message when client time was clientTime (i.e. server->client response took 0ms somehow), then the time delta is var delta2 = (serverTime - clientTime2).

Thus we can safely say that the time delta is somewhere between delta1 and delta2. Now, repeat this process a bunch of times, each time narrowing the range based on whatever results you got, and you can get a pretty good estimate of the time delta.

I've tested this hundreds of times on 7 different browsers and multiple machines and have never had any issue with it. It's never been inconsistent.


The issue, though, is that my server logs show that, every now and then, a few people will get wildly inconsistent time sync results. Here is an actual example of one player's time sync:

The client went through 74 cycles of the above algorithm and successfully narrow the range of possible time deltas to: [-186460, -186431] without a single inconsistency. 29ms accuracy.

On the 75th cycle, possibly a few seconds after the 74th cycle, the client calculated the range of possible time deltas to be: [-601, -596]. 5ms accuracy, except for it's extremely inconsistent with the past 74 cycles: it's 3 minutes off!

I would blame this on crazy edge cases, except it happens almost 100 times a day... how could this happen? Is there any possible error when using Date.now()?

Upvotes: 3

Views: 823

Answers (2)

Victor Zhou
Victor Zhou

Reputation: 433

performance.now() instead of Date.now(), because performance.now() is monotonically increasing and not subject to clock drift. See the comments, thanks to everyone for their help!

Upvotes: 2

user2832874
user2832874

Reputation:

Your difficulty is that you depend on estimating round-trip times to the server, over an Internet that has variance in round-trip times. Sometimes that variance will be unexpectedly substantial, as in cases where temporary congestion and large router buffers delay a single message far longer than normal. (Cf "bufferbloat".)

Your second difficulty is that you are using self-reported client times, and that means that if a client has a clock that's weird it will look to you like a round-trip estimation gone wrong. As another poster noted, internet time protocols will sometimes slew a clock rapidly to correct from local timekeeping anomalies.

What it sounds like is that you need some filtering in your code that takes into account previous results so that when you get an anomalous result you don't immediately accept it as true.

Upvotes: 1

Related Questions