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.