🎉 Celebrating 25 Years of GameDev.net! 🎉

Not many can claim 25 years on the Internet! Join us in celebrating this milestone. Learn more about our history, and thank you for being a part of our community!

Overwatch - Client Input Buffer + Dynamic FixedTimeStep

Started by
0 comments, last by RxMarcus 5 years, 3 months ago

Hello.
I have my netcode set up that my Server receives client inputs, and stores them in a buffer.

As seen in the Overwatch GDC talk, it is described how the client will 'speed up' and 'slow down' it's FixedTimeStep as to send more or less input frames to the Server as needed to compensate for latency and keep the Server on the "bleeding edge" of client inputs. (Meaning that we want our client input buffer to have as few inputs waiting in the buffer as possible, but never empty)

I've been struggling for a few weeks now trying to write some semi intelligent code that could help me achieve this in my game. I'm at a loss at this point and am hoping more sets of eyes can help me understand what I might be doing wrong. Below you will see the method 'ClientReceiveBufferState' which is called over the network from Server to Client, passing the last frame # the Server received from the client, and it's current server time TimeStamp. The 'timeChangedFrameCount' variable is incremented by 1 each FixedTimeStep. (I'm using Unity)

I'm attempting to keep my Server's client input buffer filled with 2 or 3 inputs waiting at any given time.

My thought was that I could more or less calculate the number of frames that the client has become behind or ahead of the Server and Speed Up / Slow Down my fixedTimeStep for that many frames.... but this is not working. Currently when I test with a higher latency, my client gets permanently stuck behind the Server's tick # and it's Frame # is always irrelevant (outdated) once it reaches the Server. Am I over complicating this?


void ClientReceiveBufferState(uint lastQueuedFrame, long serverTimeStamp)
{
  // Determine how far behind the server we are
  long serverTime = TNManager.serverTime;
  int mTick = (int)SimulationManager.MasterTick;
  int overTheWireTickCount = (int)(((Mathf.Abs((serverTimeStamp - serverTime))) * 0.001f) / 0.0167f); // Convert to milliseconds and divide by 'normal' FixedTimeStep
  uint serverClientTickDiff = (uint)Mathf.Abs(((int)lastQueuedFrame + overTheWireTickCount) - mTick);

  if (timeChangeActive)
  {
    // Check if we're back in a good state
    if (timeChangedFrameCount >= desiredChangedFrameNumber)
    {
      Time.fixedDeltaTime = 0.0167f; // 'normal' 60 ticks per second
      bufferStatus = InputBufferStatus.Normal;

      timeChangeActive = false;
    }
  }


  if (!timeChangeActive) {

    if (serverClientTickDiff <= 1) // Going to run out of client inputs in buffer, speed up!
    {
      if (bufferStatus != InputBufferStatus.Empty) {
        bufferStatus = InputBufferStatus.Empty;
        timeChangeActive = true;
        timeChangedFrameCount = 0;
        desiredChangedFrameNumber = (int)(((Mathf.Abs((serverTimeStamp - serverTime))) * 0.001f) / 0.0157f);

        // Speed Up
        Time.fixedDeltaTime = 0.0157f; // 'faster' 63 ticks per second
      }
    }
    else if (serverClientTickDiff > 3) // Too many client inputs waiting in buffer, slow down
    {
      if (bufferStatus != InputBufferStatus.Full) {
        bufferStatus = InputBufferStatus.Full;
        timeChangeActive = true;
        timeChangedFrameCount = 0;
        desiredChangedFrameNumber = (int)(((Mathf.Abs((serverTimeStamp - serverTime))) * 0.001f) / 0.0177f);

        // Slow Down
        Time.fixedDeltaTime = 0.0177f;// 'slower' 56 ticks per second
      }
    }
  }
}

Thanks for your time.

This topic is closed to new replies.

Advertisement