alap
This commit is contained in:
86
Assets/Mirror/Core/Tools/AccurateInterval.cs
Normal file
86
Assets/Mirror/Core/Tools/AccurateInterval.cs
Normal file
@ -0,0 +1,86 @@
|
||||
// accurate interval from Mirror II.
|
||||
// for sync / send intervals where it matters.
|
||||
// does not(!) do catch-up.
|
||||
//
|
||||
// first, let's understand the problem.
|
||||
// say we need an interval of 10 Hz, so every 100ms in Update we do:
|
||||
// if (Time.time >= lastTime + interval)
|
||||
// {
|
||||
// lastTime = Time.time;
|
||||
// ...
|
||||
// }
|
||||
//
|
||||
// this seems fine, but actually Time.time will always be a few ms beyond
|
||||
// the interval. but since lastTime is reset to Time.time, the remainder
|
||||
// is always ignored away.
|
||||
// with fixed tickRate servers (say 30 Hz), the remainder is significant!
|
||||
//
|
||||
// in practice if we have a 30 Hz tickRate server with a 30 Hz sendRate,
|
||||
// the above way to measure the interval would result in a 18-19 Hz sendRate!
|
||||
// => this is not just a little off. this is _way_ off, by almost half.
|
||||
// => displaying actual + target tick/send rate will show this very easily.
|
||||
//
|
||||
// we need an accurate way to measure intervals for where it matters.
|
||||
// and it needs to be testable to guarantee results.
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Mirror
|
||||
{
|
||||
public static class AccurateInterval
|
||||
{
|
||||
// static func instead of storing interval + lastTime struct.
|
||||
// + don't need to initialize struct ctor with interval in Awake
|
||||
// + allows for interval changes at runtime too
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool Elapsed(double time, double interval, ref double lastTime)
|
||||
{
|
||||
// enough time elapsed?
|
||||
if (time < lastTime + interval)
|
||||
return false;
|
||||
|
||||
// naive implementation:
|
||||
//lastTime = time;
|
||||
|
||||
// accurate but doesn't handle heavy load situations:
|
||||
//lastTime += interval;
|
||||
|
||||
// heavy load edge case:
|
||||
// * interval is 100ms
|
||||
// * server is under heavy load, Updates slow down to 1/s
|
||||
// * Elapsed(1.000) returns true.
|
||||
// technically 10 intervals have elapsed.
|
||||
// * server recovers to normal, Updates are every 10ms again
|
||||
// * Elapsed(1.010) should return false again until 1.100.
|
||||
//
|
||||
// increasing lastTime by interval would require 10 more calls
|
||||
// to ever catch up again:
|
||||
// lastTime += interval
|
||||
//
|
||||
// as result, the next 10 calls to Elapsed would return true.
|
||||
// Elapsed(1.001) => true
|
||||
// Elapsed(1.002) => true
|
||||
// Elapsed(1.003) => true
|
||||
// ...
|
||||
// even though technically the delta was not >= interval.
|
||||
//
|
||||
// this would keep the server under heavy load, and it may never
|
||||
// catch-up. this is not ideal for large virtual worlds.
|
||||
//
|
||||
// instead, we want to skip multiples of 'interval' and only
|
||||
// keep the remainder.
|
||||
//
|
||||
// see also: AccurateIntervalTests.Slowdown()
|
||||
|
||||
// easy to understand:
|
||||
//double elapsed = time - lastTime;
|
||||
//double remainder = elapsed % interval;
|
||||
//lastTime = time - remainder;
|
||||
|
||||
// easier: set to rounded multiples of interval (fholm).
|
||||
// long to match double time.
|
||||
long multiplier = (long)(time / interval);
|
||||
lastTime = multiplier * interval;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user