From 75ad74e0b3c0ed133d05b78d5ec18038ed6a22b5 Mon Sep 17 00:00:00 2001 From: Erik Date: Fri, 24 Apr 2026 09:25:03 +0200 Subject: [PATCH] =?UTF-8?q?sky(phase-3d):=20fix=20time=20drift=20=E2=80=94?= =?UTF-8?q?=20WorldTime.TickSize=20is=20always=201.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit User reported the in-game sky time in acdream consistently trails retail's, even after Phases 3a-3c.1 aligned the DayGroup selection. Diagnosed it as a rate mismatch between our client-side extrapolation and the ACE server's tick advancement. ACE advances PortalYearTicks at 1.0 ticks per real-second: Timers.cs: PortalYearTicks += worldTickTimer.Elapsed.TotalSeconds Our WorldTime was using SkyDesc.TickSize (0.8 in the live Dereth dat) as the extrapolation rate: NowTicks = lastSync + elapsed * TickSize // with TickSize=0.8 Between the server's ~20s TimeSync gap, we fell 4 ticks behind. Every sync yanked us back, but in the window between syncs the sky interp was rendering at a stale (earlier) dayFraction — visible as "acdream is behind retail" when the user had retail running alongside. Root cause: we misread r12 §1.2's definition of SkyDesc.TickSize. Agent C's decompile (`docs/research/2026-04-23-sky-decompile-hunt-C.md` §5 and the 2026-04-23 sky-retail-verbatim synthesis §5) showed SkyDesc.TickSize is consulted at `FUN_005062e0:6241` as: _DAT_00842798 = SkyDesc.TickSize + _DAT_008379a8 // next deadline i.e. the per-frame sky-subsystem update PERIOD. It's a throttle, not a clock-rate multiplier. SkyDesc.LightTickSize=15 plays the same role for lighting interpolation (re-run every 15 real-seconds). Fix: remove the SkyDesc.TickSize → WorldTime.TickSize assignment. Keep WorldTime.TickSize at its default 1.0, matching the server's rate. SkyDesc.TickSize stays on the LoadedSkyDesc for a future Phase 4 port of the actual retail throttle logic. Build + 717 tests green. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/AcDream.App/Rendering/GameWindow.cs | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/src/AcDream.App/Rendering/GameWindow.cs b/src/AcDream.App/Rendering/GameWindow.cs index 2d35076..2d185c3 100644 --- a/src/AcDream.App/Rendering/GameWindow.cs +++ b/src/AcDream.App/Rendering/GameWindow.cs @@ -877,10 +877,26 @@ public sealed class GameWindow : IDisposable _loadedSkyDesc = AcDream.Core.World.SkyDescLoader.LoadFromRegion(region); if (_loadedSkyDesc is not null) { - WorldTime.TickSize = _loadedSkyDesc.TickSize > 0 ? _loadedSkyDesc.TickSize : 1.0; + // Phase 3d: do NOT assign WorldTime.TickSize from + // SkyDesc.TickSize. Agent C's decompile (chunk_00500000.c:6241 + // FUN_005062e0) shows SkyDesc.TickSize is the "next sky-tick + // deadline" period — a throttle — NOT a game-time + // advancement rate. ACE's server advances PortalYearTicks at + // 1.0 ticks per real-second (Timers.cs: `PortalYearTicks += + // worldTickTimer.Elapsed.TotalSeconds`). Our client + // extrapolation between TimeSyncs must match: 1.0. + // + // Previous behavior: WorldTime.TickSize = 0.8 (from the live + // SkyDesc.TickSize). Between ~20s TimeSync gaps we fell 4 + // ticks behind the server, producing a visible "acdream sky + // is behind retail" time-of-day mismatch (user-verified + // 2026-04-23). + WorldTime.TickSize = 1.0; + Console.WriteLine( $"sky: loaded Region 0x13000000 — {_loadedSkyDesc.DayGroups.Count} day groups, " + - $"TickSize={_loadedSkyDesc.TickSize}, LightTickSize={_loadedSkyDesc.LightTickSize}"); + $"SkyDesc.TickSize={_loadedSkyDesc.TickSize} (throttle, not rate), " + + $"LightTickSize={_loadedSkyDesc.LightTickSize}"); // Initial DayGroup roll using whatever WorldTime currently // has (either the hardcoded boot seed or a pre-arrived