protected bool DecodePrediction( DeltaBuffer deltaBuffer, ushort currentSequence, ushort resetSequence, int numCubes, ref int[] cubeIds, ref bool[] perfectPrediction, ref bool[] hasPredictionDelta, ref ushort[] baselineSequence, ref CubeState[] cubeState, ref CubeDelta[] predictionDelta )
    {
        Profiler.BeginSample( "DecodePrediction" );

        CubeState baselineCubeState = CubeState.defaults;

        bool result = true;

#if !DISABLE_DELTA_ENCODING

        for ( int i = 0; i < numCubes; ++i )
        {
            if ( perfectPrediction[i] || hasPredictionDelta[i] )
            {
                if ( deltaBuffer.GetCubeState( baselineSequence[i], resetSequence, cubeIds[i], ref baselineCubeState ) )
                {
                    int baseline_sequence = baselineSequence[i];
                    int current_sequence = currentSequence;

                    if ( current_sequence < baseline_sequence )
                        current_sequence += 65536;

                    int baseline_position_x = baselineCubeState.position_x;
                    int baseline_position_y = baselineCubeState.position_y;
                    int baseline_position_z = baselineCubeState.position_z;

                    int baseline_linear_velocity_x = baselineCubeState.linear_velocity_x;
                    int baseline_linear_velocity_y = baselineCubeState.linear_velocity_y;
                    int baseline_linear_velocity_z = baselineCubeState.linear_velocity_z;

                    int baseline_angular_velocity_x = baselineCubeState.angular_velocity_x;
                    int baseline_angular_velocity_y = baselineCubeState.angular_velocity_y;
                    int baseline_angular_velocity_z = baselineCubeState.angular_velocity_z;

                    if ( current_sequence < baseline_sequence )
                        current_sequence += 65536;

                    int numFrames = current_sequence - baseline_sequence;

                    int predicted_position_x;
                    int predicted_position_y;
                    int predicted_position_z;

                    int predicted_linear_velocity_x;
                    int predicted_linear_velocity_y;
                    int predicted_linear_velocity_z;

                    int predicted_angular_velocity_x;
                    int predicted_angular_velocity_y;
                    int predicted_angular_velocity_z;

                    Prediction.PredictBallistic( numFrames,
                                                 baseline_position_x, baseline_position_y, baseline_position_z,
                                                 baseline_linear_velocity_x, baseline_linear_velocity_y, baseline_linear_velocity_z,
                                                 baseline_angular_velocity_x, baseline_angular_velocity_y, baseline_angular_velocity_z,
                                                 out predicted_position_x, out predicted_position_y, out predicted_position_z,
                                                 out predicted_linear_velocity_x, out predicted_linear_velocity_y, out predicted_linear_velocity_z,
                                                 out predicted_angular_velocity_x, out predicted_angular_velocity_y, out predicted_angular_velocity_z );

                    if ( perfectPrediction[i] )
                    {
#if DEBUG_DELTA_COMPRESSION
                        Assert.IsTrue( predicted_position_x == cubeDelta[i].absolute_position_x );
                        Assert.IsTrue( predicted_position_y == cubeDelta[i].absolute_position_y );
                        Assert.IsTrue( predicted_position_z == cubeDelta[i].absolute_position_z );
#endif // #if DEBUG_DELTA_COMPRESSION

                        cubeState[i].position_x = predicted_position_x;
                        cubeState[i].position_y = predicted_position_y;
                        cubeState[i].position_z = predicted_position_z;

                        cubeState[i].linear_velocity_x = predicted_linear_velocity_x;
                        cubeState[i].linear_velocity_y = predicted_linear_velocity_y;
                        cubeState[i].linear_velocity_z = predicted_linear_velocity_z;

                        cubeState[i].angular_velocity_x = predicted_angular_velocity_x;
                        cubeState[i].angular_velocity_y = predicted_angular_velocity_y;
                        cubeState[i].angular_velocity_z = predicted_angular_velocity_z;
                    }
                    else
                    {
                        cubeState[i].position_x = predicted_position_x + predictionDelta[i].position_delta_x;
                        cubeState[i].position_y = predicted_position_y + predictionDelta[i].position_delta_y;
                        cubeState[i].position_z = predicted_position_z + predictionDelta[i].position_delta_z;

#if DEBUG_DELTA_COMPRESSION
                        Assert.IsTrue( cubeState[i].position_x == cubeDelta[i].absolute_position_x );
                        Assert.IsTrue( cubeState[i].position_y == cubeDelta[i].absolute_position_y );
                        Assert.IsTrue( cubeState[i].position_z == cubeDelta[i].absolute_position_z );
#endif // #if DEBUG_DELTA_COMPRESSION

                        cubeState[i].linear_velocity_x = predicted_linear_velocity_x + predictionDelta[i].linear_velocity_delta_x;
                        cubeState[i].linear_velocity_y = predicted_linear_velocity_y + predictionDelta[i].linear_velocity_delta_y;
                        cubeState[i].linear_velocity_z = predicted_linear_velocity_z + predictionDelta[i].linear_velocity_delta_z;

                        cubeState[i].angular_velocity_x = predicted_angular_velocity_x + predictionDelta[i].angular_velocity_delta_x;
                        cubeState[i].angular_velocity_y = predicted_angular_velocity_y + predictionDelta[i].angular_velocity_delta_y;
                        cubeState[i].angular_velocity_z = predicted_angular_velocity_z + predictionDelta[i].angular_velocity_delta_z;
                    }
                }
                else
                {
                    Debug.Log( "error: missing baseline for cube " + cubeIds[i] + " at sequence " + baselineSequence[i] + " (perfect prediction and prediction delta)" );
                    result = false;
                    break;
                }
            }
        }

#endif // #if !DISABLE_DELTA_COMPRESSION

        Profiler.EndSample();

        return result;
    }
    bool DecodePrediction(DeltaBuffer buffer, ushort currentId, ushort resetId, int cubeCount, ref int[] cubeIds, ref bool[] hasPerfectPrediction, ref bool[] hasPrediction, ref ushort[] baselineIds, ref CubeState[] states, ref CubeDelta[] predictions
                          )
    {
        Profiler.BeginSample("DecodePrediction");
        var baseline = CubeState.defaults;
        var result   = true;

#if !DISABLE_DELTA_ENCODING
        for (int i = 0; i < cubeCount; ++i)
        {
            if (!hasPerfectPrediction[i] && !hasPrediction[i])
            {
                continue;
            }

            if (!buffer.GetPacketCube(baselineIds[i], resetId, cubeIds[i], ref baseline))
            {
                Debug.Log("error: missing baseline for cube " + cubeIds[i] + " at sequence " + baselineIds[i] + " (perfect prediction and prediction delta)");
                result = false;
                break;
            }

            int id = currentId;

            if (id < baselineIds[i])
            {
                id += 65536;
            }

            if (id < baselineIds[i])
            {
                id += 65536;
            }

            int positionX;
            int positionY;
            int positionZ;
            int linearVelocityX;
            int linearVelocityY;
            int linearVelocityZ;
            int angularVelocityX;
            int angularVelocityY;
            int angularVelocityZ;

            Prediction.PredictBallistic(id - baselineIds[i],
                                        baseline.positionX, baseline.positionY, baseline.positionZ,
                                        baseline.linearVelocityX, baseline.linearVelocityY, baseline.linearVelocityZ,
                                        baseline.angularVelocityX, baseline.angularVelocityY, baseline.angularVelocityZ,
                                        out positionX, out positionY, out positionZ,
                                        out linearVelocityX, out linearVelocityY, out linearVelocityZ,
                                        out angularVelocityX, out angularVelocityY, out angularVelocityZ);

            if (hasPerfectPrediction[i])
            {
#if DEBUG_DELTA_COMPRESSION
                Assert.IsTrue(predicted_position_x == cubeDelta[i].absolute_position_x);
                Assert.IsTrue(predicted_position_y == cubeDelta[i].absolute_position_y);
                Assert.IsTrue(predicted_position_z == cubeDelta[i].absolute_position_z);
#endif // #if DEBUG_DELTA_COMPRESSION
                states[i].positionY        = positionY;
                states[i].positionZ        = positionZ;
                states[i].linearVelocityX  = linearVelocityX;
                states[i].linearVelocityY  = linearVelocityY;
                states[i].linearVelocityZ  = linearVelocityZ;
                states[i].angularVelocityX = angularVelocityX;
                states[i].angularVelocityY = angularVelocityY;
                states[i].angularVelocityZ = angularVelocityZ;
                continue;
            }

            states[i].positionX = positionX + predictions[i].positionX;
            states[i].positionY = positionY + predictions[i].positionY;
            states[i].positionZ = positionZ + predictions[i].positionZ;
#if DEBUG_DELTA_COMPRESSION
            Assert.IsTrue(cubeState[i].position_x == cubeDelta[i].absolute_position_x);
            Assert.IsTrue(cubeState[i].position_y == cubeDelta[i].absolute_position_y);
            Assert.IsTrue(cubeState[i].position_z == cubeDelta[i].absolute_position_z);
#endif // #if DEBUG_DELTA_COMPRESSION
            states[i].linearVelocityX  = linearVelocityX + predictions[i].linearVelocityX;
            states[i].linearVelocityY  = linearVelocityY + predictions[i].linearVelocityY;
            states[i].linearVelocityZ  = linearVelocityZ + predictions[i].linearVelocityZ;
            states[i].angularVelocityX = angularVelocityX + predictions[i].angularVelocityX;
            states[i].angularVelocityY = angularVelocityY + predictions[i].angularVelocityY;
            states[i].angularVelocityZ = angularVelocityZ + predictions[i].angularVelocityZ;
        }
#endif // #if !DISABLE_DELTA_COMPRESSION
        Profiler.EndSample();

        return(result);
    }
    protected void DeterminePrediction( Context context, Context.ConnectionData connectionData, ushort currentSequence, int numCubes, ref int[] cubeIds, ref bool[] notChanged, ref bool[] hasDelta, ref bool[] perfectPrediction, ref bool[] hasPredictionDelta, ref ushort[] baselineSequence, ref CubeState[] cubeState, ref CubeDelta[] predictionDeltas )
    {
        Profiler.BeginSample( "DeterminePrediction" );

        CubeState baselineCubeState = CubeState.defaults;

        for ( int i = 0; i < numCubes; ++i )
        {
            perfectPrediction[i] = false;
            hasPredictionDelta[i] = false;

#if !DISABLE_DELTA_ENCODING

            if ( notChanged[i] )
                continue;

            if ( !hasDelta[i] )
                continue;

            if ( !cubeState[i].active )
                continue;

            if ( context.GetMostRecentAckedState( connectionData, cubeIds[i], ref baselineSequence[i], context.GetResetSequence(), ref baselineCubeState ) )
            {
                if ( Network.Util.BaselineDifference( currentSequence, baselineSequence[i] ) <= Constants.MaxBaselineDifference )
                {
                    // baseline is too far behind. send the cube state absolute
                    continue;
                }

                if ( !baselineCubeState.active )
                {
                    // no point predicting if the cube is at rest.
                    continue;
                }

                int baseline_sequence = baselineSequence[i];
                int current_sequence = currentSequence;

                if ( current_sequence < baseline_sequence )
                    current_sequence += 65536;

                int baseline_position_x = baselineCubeState.position_x;
                int baseline_position_y = baselineCubeState.position_y;
                int baseline_position_z = baselineCubeState.position_z;

                int baseline_linear_velocity_x = baselineCubeState.linear_velocity_x;
                int baseline_linear_velocity_y = baselineCubeState.linear_velocity_y;
                int baseline_linear_velocity_z = baselineCubeState.linear_velocity_z;

                int baseline_angular_velocity_x = baselineCubeState.angular_velocity_x;
                int baseline_angular_velocity_y = baselineCubeState.angular_velocity_y;
                int baseline_angular_velocity_z = baselineCubeState.angular_velocity_z;

                if ( current_sequence < baseline_sequence )
                    current_sequence += 65536;

                int numFrames = current_sequence - baseline_sequence;

                int predicted_position_x;
                int predicted_position_y;
                int predicted_position_z;

                int predicted_linear_velocity_x;
                int predicted_linear_velocity_y;
                int predicted_linear_velocity_z;

                int predicted_angular_velocity_x;
                int predicted_angular_velocity_y;
                int predicted_angular_velocity_z;

                Prediction.PredictBallistic( numFrames,
                                             baseline_position_x, baseline_position_y, baseline_position_z,
                                             baseline_linear_velocity_x, baseline_linear_velocity_y, baseline_linear_velocity_z,
                                             baseline_angular_velocity_x, baseline_angular_velocity_y, baseline_angular_velocity_z,
                                             out predicted_position_x, out predicted_position_y, out predicted_position_z,
                                             out predicted_linear_velocity_x, out predicted_linear_velocity_y, out predicted_linear_velocity_z,
                                             out predicted_angular_velocity_x, out predicted_angular_velocity_y, out predicted_angular_velocity_z );

                int current_position_x = cubeState[i].position_x;
                int current_position_y = cubeState[i].position_y;
                int current_position_z = cubeState[i].position_z;

                int current_linear_velocity_x = cubeState[i].linear_velocity_x;
                int current_linear_velocity_y = cubeState[i].linear_velocity_y;
                int current_linear_velocity_z = cubeState[i].linear_velocity_z;

                int current_angular_velocity_x = cubeState[i].angular_velocity_x;
                int current_angular_velocity_y = cubeState[i].angular_velocity_y;
                int current_angular_velocity_z = cubeState[i].angular_velocity_z;

                int position_error_x = current_position_x - predicted_position_x;
                int position_error_y = current_position_y - predicted_position_y;
                int position_error_z = current_position_z - predicted_position_z;

                int linear_velocity_error_x = current_linear_velocity_x - predicted_linear_velocity_x;
                int linear_velocity_error_y = current_linear_velocity_y - predicted_linear_velocity_y;
                int linear_velocity_error_z = current_linear_velocity_z - predicted_linear_velocity_z;

                int angular_velocity_error_x = current_angular_velocity_x - predicted_angular_velocity_x;
                int angular_velocity_error_y = current_angular_velocity_y - predicted_angular_velocity_y;
                int angular_velocity_error_z = current_angular_velocity_z - predicted_angular_velocity_z;

                if ( position_error_x == 0 &&
                     position_error_y == 0 &&
                     position_error_z == 0 &&
                     linear_velocity_error_x == 0 &&
                     linear_velocity_error_y == 0 &&
                     linear_velocity_error_z == 0 &&
                     angular_velocity_error_x == 0 &&
                     angular_velocity_error_y == 0 &&
                     angular_velocity_error_z == 0 )
                {
                    perfectPrediction[i] = true;
                }
                else
                {
                    int abs_position_error_x = Math.Abs( position_error_x );
                    int abs_position_error_y = Math.Abs( position_error_y );
                    int abs_position_error_z = Math.Abs( position_error_z );

                    int abs_linear_velocity_error_x = Math.Abs( linear_velocity_error_x );
                    int abs_linear_velocity_error_y = Math.Abs( linear_velocity_error_y );
                    int abs_linear_velocity_error_z = Math.Abs( linear_velocity_error_z );

                    int abs_angular_velocity_error_x = Math.Abs( angular_velocity_error_x );
                    int abs_angular_velocity_error_y = Math.Abs( angular_velocity_error_y );
                    int abs_angular_velocity_error_z = Math.Abs( angular_velocity_error_z );

                    int total_prediction_error = abs_position_error_x +
                                                 abs_position_error_y +
                                                 abs_position_error_z +
                                                 linear_velocity_error_x +
                                                 linear_velocity_error_y +
                                                 linear_velocity_error_z +
                                                 angular_velocity_error_x +
                                                 angular_velocity_error_y +
                                                 angular_velocity_error_z;

                    int total_absolute_error = Math.Abs( cubeState[i].position_x - baselineCubeState.position_x ) +
                                               Math.Abs( cubeState[i].position_y - baselineCubeState.position_y ) +
                                               Math.Abs( cubeState[i].position_z - baselineCubeState.position_z ) +
                                               Math.Abs( cubeState[i].linear_velocity_x - baselineCubeState.linear_velocity_x ) +
                                               Math.Abs( cubeState[i].linear_velocity_y - baselineCubeState.linear_velocity_y ) +
                                               Math.Abs( cubeState[i].linear_velocity_z - baselineCubeState.linear_velocity_z ) +
                                               Math.Abs( cubeState[i].angular_velocity_x - baselineCubeState.angular_velocity_x ) +
                                               Math.Abs( cubeState[i].angular_velocity_y - baselineCubeState.angular_velocity_y ) +
                                               Math.Abs( cubeState[i].angular_velocity_z - baselineCubeState.angular_velocity_z );

                    if ( total_prediction_error < total_absolute_error )
                    {
                        int max_position_error = abs_position_error_x;

                        if ( abs_position_error_y > max_position_error )
                            max_position_error = abs_position_error_y;

                        if ( abs_position_error_z > max_position_error )
                            max_position_error = abs_position_error_z;

                        int max_linear_velocity_error = abs_linear_velocity_error_x;

                        if ( abs_linear_velocity_error_y > max_linear_velocity_error )
                            max_linear_velocity_error = abs_linear_velocity_error_y;

                        if ( abs_linear_velocity_error_z > max_linear_velocity_error )
                            max_linear_velocity_error = abs_linear_velocity_error_z;

                        int max_angular_velocity_error = abs_angular_velocity_error_x;

                        if ( abs_angular_velocity_error_y > max_angular_velocity_error )
                            max_angular_velocity_error = abs_angular_velocity_error_y;

                        if ( abs_angular_velocity_error_z > max_angular_velocity_error )
                            max_angular_velocity_error = abs_angular_velocity_error_z;

                        if ( max_position_error <= Constants.PositionDeltaMax &&
                             max_linear_velocity_error <= Constants.LinearVelocityDeltaMax &&
                             max_angular_velocity_error <= Constants.AngularVelocityDeltaMax )
                        {
                            hasPredictionDelta[i] = true;

                            predictionDelta[i].position_delta_x = position_error_x;
                            predictionDelta[i].position_delta_y = position_error_y;
                            predictionDelta[i].position_delta_z = position_error_z;

                            predictionDelta[i].linear_velocity_delta_x = linear_velocity_error_x;
                            predictionDelta[i].linear_velocity_delta_y = linear_velocity_error_y;
                            predictionDelta[i].linear_velocity_delta_z = linear_velocity_error_z;

                            predictionDelta[i].angular_velocity_delta_x = angular_velocity_error_x;
                            predictionDelta[i].angular_velocity_delta_y = angular_velocity_error_y;
                            predictionDelta[i].angular_velocity_delta_z = angular_velocity_error_z;
                        }
                    }
                }
            }
        }

#endif // #if !DISABLE_DELTA_ENCODING

        Profiler.EndSample();
    }
  protected void DeterminePrediction(Context context, Context.NetworkData data, ushort currentId, int cubeCount, ref int[] cubeIds, ref bool[] notChanged, ref bool[] hasDelta, ref bool[] hasPerfectPrediction, ref bool[] hasPrediction, ref ushort[] baselineIds, ref CubeState[] cubes, ref CubeDelta[] predictions
  ) {
    BeginSample("DeterminePrediction");
    var baseline = CubeState.defaults;

    for (int i = 0; i < cubeCount; ++i) {
      hasPerfectPrediction[i] = false;
      hasPrediction[i] = false;
#if !DISABLE_DELTA_ENCODING
      if (notChanged[i] 
        || !hasDelta[i] 
        || !cubes[i].isActive
        || !context.GetAckedCube(data, cubeIds[i], ref baselineIds[i], context.resetId, ref baseline)
        || Util.BaselineDifference(currentId, baselineIds[i]) <= MaxBaselineDifference //baseline is too far behind. send the cube state absolute
        || !baseline.isActive //no point predicting if the cube is at rest.
      ) continue;

      int id = currentId;

      if (id < baselineIds[i])
        id += 65536;

      if (id < baselineIds[i])
        id += 65536;

      Prediction.PredictBallistic(id - baselineIds[i],
        baseline.positionX, baseline.positionY, baseline.positionZ,
        baseline.linearVelocityX, baseline.linearVelocityY, baseline.linearVelocityZ,
        baseline.angularVelocityX, baseline.angularVelocityY, baseline.angularVelocityZ,
        out int positionX, out int positionY, out int positionZ,
        out int linearVelocityX, out int linearVelocityY, out int linearVelocityZ,
        out int angularVelocityX, out int angularVelocityY, out int angularVelocityZ);

      int positionLagX = cubes[i].positionX - positionX;
      int positionLagY = cubes[i].positionY - positionY;
      int positionLagZ = cubes[i].positionZ - positionZ;
      int linearVelocityLagX = cubes[i].linearVelocityX - linearVelocityX;
      int linearVelocityLagY = cubes[i].linearVelocityY - linearVelocityY;
      int linearVelocityLagZ = cubes[i].linearVelocityZ - linearVelocityZ;
      int angularVelocityLagX = cubes[i].angularVelocityX - angularVelocityX;
      int angularVelocityLagY = cubes[i].angularVelocityY - angularVelocityY;
      int angularVelocityLagZ = cubes[i].angularVelocityZ - angularVelocityZ;

      if (positionLagX == 0 && positionLagY == 0 && positionLagZ == 0
        && linearVelocityLagX == 0 && linearVelocityLagY == 0 && linearVelocityLagZ == 0
        && angularVelocityLagX == 0 && angularVelocityLagY == 0 && angularVelocityLagZ == 0
      ) {
        hasPerfectPrediction[i] = true;
        continue;
      }

      int absPositionX = Math.Abs(positionLagX);
      int absPositionY = Math.Abs(positionLagY);
      int absPositionZ = Math.Abs(positionLagZ);
      int absLinearVelocityX = Math.Abs(linearVelocityLagX);
      int absLinearVelocityY = Math.Abs(linearVelocityLagY);
      int absLinearVelocityZ = Math.Abs(linearVelocityLagZ);
      int absAngularVelocityX = Math.Abs(angularVelocityLagX);
      int absAngularVelocityY = Math.Abs(angularVelocityLagY);
      int absAngularVelocityZ = Math.Abs(angularVelocityLagZ);

      int predictedLag = absPositionX + absPositionY + absPositionZ
        + linearVelocityLagX + linearVelocityLagY + linearVelocityLagZ
        + angularVelocityLagX + angularVelocityLagY + angularVelocityLagZ;

      int absoluteLag = Math.Abs(cubes[i].positionX - baseline.positionX)
        + Math.Abs(cubes[i].positionY - baseline.positionY)
        + Math.Abs(cubes[i].positionZ - baseline.positionZ)
        + Math.Abs(cubes[i].linearVelocityX - baseline.linearVelocityX)
        + Math.Abs(cubes[i].linearVelocityY - baseline.linearVelocityY)
        + Math.Abs(cubes[i].linearVelocityZ - baseline.linearVelocityZ)
        + Math.Abs(cubes[i].angularVelocityX - baseline.angularVelocityX)
        + Math.Abs(cubes[i].angularVelocityY - baseline.angularVelocityY)
        + Math.Abs(cubes[i].angularVelocityZ - baseline.angularVelocityZ);

      if (predictedLag < absoluteLag) {
        int maxPositionLag = absPositionX;

        if (absPositionY > maxPositionLag)
          maxPositionLag = absPositionY;

        if (absPositionZ > maxPositionLag)
          maxPositionLag = absPositionZ;

        int maxLinearVelocityLag = absLinearVelocityX;

        if (absLinearVelocityY > maxLinearVelocityLag)
          maxLinearVelocityLag = absLinearVelocityY;

        if (absLinearVelocityZ > maxLinearVelocityLag)
          maxLinearVelocityLag = absLinearVelocityZ;

        int maxAngularVelocityLag = absAngularVelocityX;

        if (absAngularVelocityY > maxAngularVelocityLag)
          maxAngularVelocityLag = absAngularVelocityY;

        if (absAngularVelocityZ > maxAngularVelocityLag)
          maxAngularVelocityLag = absAngularVelocityZ;

        if (maxPositionLag > PositionDeltaMax
          || maxLinearVelocityLag > LinearVelocityDeltaMax
          || maxAngularVelocityLag > AngularVelocityDeltaMax
        ) continue;

        hasPrediction[i] = true;
        cubePredictions[i].positionX = positionLagX;
        cubePredictions[i].positionY = positionLagY;
        cubePredictions[i].positionZ = positionLagZ;
        cubePredictions[i].linearVelocityX = linearVelocityLagX;
        cubePredictions[i].linearVelocityY = linearVelocityLagY;
        cubePredictions[i].linearVelocityZ = linearVelocityLagZ;
        cubePredictions[i].angularVelocityX = angularVelocityLagX;
        cubePredictions[i].angularVelocityY = angularVelocityLagY;
        cubePredictions[i].angularVelocityZ = angularVelocityLagZ;
      }
    }
#endif // #if !DISABLE_DELTA_ENCODING
    EndSample();
  }