namespace Engine.Kernel.Diagnostics;
///
/// Read-only to plugins (ITime); advanced by whoever owns the frame loop —
/// Engine.Host today, headless or windowed — via and
/// , neither on the interface. Same split as
/// Schedule/ISchedule: the host gets the extra, plugin-facing code doesn't.
///
public sealed class Time : ITime
{
// Capped so a real stall — a debugger pause, a slow disk load — can't
// turn into a catch-up burst of hundreds of physics steps executing
// back to back, each one still assuming a normal frame budget around
// it. Five steps behind (0.1s at the default rate) already means
// something else is wrong; better to visibly fall behind real time
// than to freeze the process trying to simulate through it.
private const int MaxStepsPerFrame = 5;
private float _accumulator;
public float DeltaTime { get; private set; }
public double ElapsedTime { get; private set; }
public int FrameCount { get; private set; }
public float FixedDeltaTime { get; } = 1f / 50f;
public void Tick(float deltaTime)
{
DeltaTime = deltaTime;
ElapsedTime += deltaTime;
FrameCount++;
}
///
/// Adds this frame's DeltaTime to the accumulator and returns how many
/// FixedDeltaTime-sized steps it can pay for, having already deducted
/// that much. Call once per frame, then run Stage.FixedUpdate that many
/// times (zero is normal — most frames are shorter than FixedDeltaTime
/// at 50Hz against a 60+fps display).
///
public int ConsumeFixedSteps()
{
_accumulator += DeltaTime;
var steps = 0;
while (_accumulator >= FixedDeltaTime && steps < MaxStepsPerFrame)
{
_accumulator -= FixedDeltaTime;
steps++;
}
return steps;
}
}