using ADOFAIRenderer.Core; using HarmonyLib; namespace ADOFAIRenderer.Patch.Engine; /// /// Makes the async input clock offset follow the render clock immediately. /// /// Patched member: AsyncInputUtils.UpdateOffsetTime(long fixDivider = 1). The async input system maps wall-clock ticks /// (AsyncInputManager.currFrameTick = DateTime.Now.Ticks, 100 ns units) to conductor time with /// AsyncInputManager.offsetTick = currFrameTick - scrConductor.dspTime * 10^7. With fixDivider 1 the /// offset is set directly; otherwise it moves by 1 / fixDivider of the difference per call. scrConductor calls it every /// frame with divider 100 (damped); scrController calls it with divider 1. /// /// /// Active while (stage >= OnScene). From stage Render the conductor time follows the /// patched AudioSettings.dspTime (offline render clock, a fixed step per frame independent of wall-clock time), so the /// difference changes every frame; the damped update would lag behind it. The prefix always applies the divider-1 update. /// /// Reference: https://learn.microsoft.com/en-us/dotnet/api/system.datetime.ticks /// [HarmonyPatch(typeof(AsyncInputUtils), "UpdateOffsetTime")] // ReSharper disable once InconsistentNaming public static class AsyncInputPatches { /// /// Prefix of AsyncInputUtils.UpdateOffsetTime. While rendering, sets offsetTick to /// currFrameTick - dspTime * 10^7 (10^7 = 100 ns ticks per second) and offsetTickUpdated to true, for any divider. /// /// True to run the original method (not rendering); false to skip it (rendering). [HarmonyPrefix] // ReSharper disable once InconsistentNaming public static bool Prefix() { if (!RenderManager.IsActive) return true; // Force offsetTick to match instantly without 1/100 damping division when rendering AsyncInputManager.offsetTick = AsyncInputManager.currFrameTick - (long)(scrConductor.instance.dspTime * 10000000.0); AsyncInputManager.offsetTickUpdated = true; return false; // Skip the original damped offset calculation } }