csharp/1ZouLTReX1/FP-MOG/top%20down%20shooter/Assets/Scripts/ClientReceiveBuffer.cs

ClientReceiveBuffer.cs
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;

public clast ClientReceiveBuffer : MyStopWatch
{
    // The desierd gap between the last received snapshot to the frame that we interpolate towards 
    // that means the if the gap is 2 we try to be 2 ticks back from current tick (at least).

    // Example:
    // desierd gap = 2;
    // last tick received = 16;
    // prev tick = 13;   // var prevState = snapshotBuffer[snapshotBuffer.Count - 2 - gap]; 
    // next tick = 14;   // var prevState = snapshotBuffer[snapshotBuffer.Count - 1 - gap]; 

    const int bufferLength = 10;

    const int snapshotDesiredGap = 5;
    int snapshotGap = -2;

    float time = 0;
    float lerpTimeFactor;

    long now;
    long lastTimeCallTime;

    long lastReceiveTime; // for jitter measurement

    int lastTickUsed = 0;


    FloatRollingAverage jitter;

    List playerStates;
    CircularList snapshotBuffer;

    public ClientReceiveBuffer(float ticksPerSecond) : base()
    {
        lerpTimeFactor = (1f / ticksPerSecond) * 1000f;  // In ms.

        jitter = new FloatRollingAverage((int) ticksPerSecond * 3);  // the jitter in 3 second
        lastReceiveTime = NowInTicks;

        snapshotBuffer = new CircularList(bufferLength);
        playerStates = new List();
    }

    public WorldState GetLatestWorldState()
    {
        if (snapshotBuffer.Count == 0)
            return null;

        return snapshotBuffer[snapshotBuffer.Count - 1];
    }

    public void AppendNewSnapshot(WorldState snapshot)
    {
        lock (snapshotBuffer)
        {
            snapshotBuffer.Add(snapshot);
            
            float deltaTime = (NowInTicks - lastReceiveTime) / (float) this.m_FrequencyMS;
            jitter.Update(deltaTime);
            lastReceiveTime = NowInTicks;
            //Debug.Log("AVG: " + jitter.average + " stdDeviation: " + jitter.stdDeviation);

            if (snapshotGap >= snapshotBuffer.Count - 2)
            {
                Reset();
            }
            else
            {
                snapshotGap++;
            }
        }
    }

    public Tuple Interpolate()
    {
        WorldState prevState;
        WorldState nextState;

        lock (snapshotBuffer)
        {
            // If we don't have enough snapshots to interpolate in between we simply set the state to the oldest received frame.
            if (snapshotBuffer.Count - 2 - snapshotDesiredGap < 0)
            {
                time = 0;
                lastTimeCallTime = NowInTicks;

                if (snapshotBuffer.Count > 0)
                {
                    lastTickUsed = snapshotBuffer[snapshotBuffer.HeadIndex].serverTickSeq;
                }
                
                return GetFirst();
            }

            // When we first capture a packet we save the received time and from then on we playout the snapshots from the server
            // in constant intervals these intervals are equal the server tick rate, i.e, the lerpTimeFactor.
            if (time >= lerpTimeFactor)
            {
                time %= lerpTimeFactor;

                if (snapshotGap