fix: complete retail parity stability pass
All checks were successful
CI / linux-portable (push) Successful in 3m41s
CI / windows-gate (push) Successful in 6m49s
CI / release (push) Successful in 3m22s

This commit is contained in:
Erik 2026-08-28 20:01:39 +02:00
parent d3df4cb20a
commit f7aa8e0eb7
131 changed files with 7765 additions and 1190 deletions

View file

@ -11,6 +11,7 @@ using AcDream.Runtime;
using AcDream.Runtime.Session;
using AcDream.Core.Combat;
using AcDream.Core.Lighting;
using AcDream.Core.Net.Messages;
using AcDream.Core.Physics;
using AcDream.Core.Selection;
using AcDream.Core.World;
@ -393,7 +394,10 @@ internal sealed class FrameRootCompositionPhase
live.EnvCellRenderer!,
foundation.SceneLighting!,
d.RenderRange,
skyPesFrame);
skyPesFrame,
persistentDaylight: () =>
d.Runtime.CharacterOwner.Options.GetOptionBit(
CharacterOptionId.PersistentAtDay));
var worldRenderFrameBuilder = new WorldRenderFrameBuilder(
new RuntimeWorldFrameCameraSource(
host.CameraController,

View file

@ -27,7 +27,6 @@ using AcDream.UI.Abstractions.Input;
using AcDream.UI.Abstractions.Panels.Chat;
using AcDream.UI.Abstractions.Panels.Vitals;
using DatReaderWriter;
using DatReaderWriter.DBObjs;
using Silk.NET.Input;
using Silk.NET.Windowing;
@ -716,19 +715,12 @@ internal sealed class RetailInteractionRetainedUiCompositionFactory
// outside the lock matches this file's existing pattern
// elsewhere (construct once, lock only around Resolve calls).
var characterCreationStrings = new DatStringResolver(d.Dats);
// CC5 review fix round F3 (2026-08-16): read the global
// SkillTable (portal.dat 0x0E000004 — the SAME file
// ChargenOptions.GlobalSkillCostsBySkillId's own doc comment and
// LiveSessionRuntimeFactory.CreateCharacterBindings already read)
// ONCE at composition time, under the DatLock DatCollection's
// thread-safety contract requires — mirrors LiveSkillCreditResolver's
// own constructor-time load. The resolver itself does no further
// DAT access per call (pure SkillFormula arithmetic), so the
// Summary page's GetSkillScore binding below needs no lock.
SkillTable? chargenSkillTable;
lock (d.DatLock)
chargenSkillTable = d.Dats.Get<SkillTable>(0x0E000004u);
var chargenSkillScoreResolver = new ChargenSkillScoreResolver(chargenSkillTable);
// #404: ChargenTableReader already loads the global SkillTable and
// projects its formulas into this immutable options model. Reuse
// that single source of truth; summary score reads are pure and
// need neither another DAT read nor another DatLock acquisition.
var chargenSkillScoreResolver = new ChargenSkillScoreResolver(
d.Runtime.CharacterCreation.Options);
// Campaign QT slice QT5: lazily loaded on first open (the panel
// is hidden at mount), then held for the session.
AcDream.Core.Quests.ContractCatalog? contractCatalog = null;
@ -1122,7 +1114,9 @@ internal sealed class RetailInteractionRetainedUiCompositionFactory
MapHouse: new MapHouseRuntimeBindings(
CurrentCalendar: d.CurrentCalendar,
PlayerCellId: () => d.PlayerController.Controller?.CellId ?? 0u,
HouseLines: () => d.Runtime.HouseOwner.Lines),
HousePosition: () => d.Runtime.HouseOwner.Position,
HouseLines: () => d.Runtime.HouseOwner.Lines,
HousePanelLines: () => d.Runtime.HouseOwner.PanelLines),
// Campaign QT slice QT5. The catalog is read from the dats
// ONCE and cached: it is immutable installed content, and the
// panel would otherwise re-read a 322-entry table on every

View file

@ -742,6 +742,7 @@ internal sealed class SessionPlayerCompositionPhase
d.EntityObjects.Objects,
live.LiveEntities,
hydration,
d.Actions.Selection,
() => d.UpdateClock.SimulationTimeSeconds);
bindings.Adopt(
"inventory world-drop projection",