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