using AcDream.Content; using AcDream.Core.Physics; using DatReaderWriter.DBObjs; namespace AcDream.App.Physics; /// /// Publishes the graph and prepared collision views for DAT objects discovered /// through the live-object path. Streamed statics carry their prepared closure /// on LandblockBuild; live CreateObject/appearance data arrives outside /// that transaction and therefore uses this matching strict publisher. /// internal sealed class LiveCollisionAssetPublisher { private readonly PhysicsDataCache _cache; private readonly IPreparedCollisionSource _source; public LiveCollisionAssetPublisher( PhysicsDataCache cache, IPreparedCollisionSource source) { _cache = cache ?? throw new ArgumentNullException(nameof(cache)); _source = source ?? throw new ArgumentNullException(nameof(source)); } public void CacheGfxObj(uint id, GfxObj gfxObj) { ArgumentNullException.ThrowIfNull(gfxObj); FlatGfxObjCollisionAsset? prepared = _cache.GetFlatGfxObj(id) is not null ? null : Require( _source.ReadGfxObjCollision(id), "GfxObj collision", id); _cache.CacheGfxObj(id, gfxObj, prepared); } public void CacheSetup(uint id, Setup setup) { ArgumentNullException.ThrowIfNull(setup); FlatSetupCollision? prepared = _cache.GetFlatSetup(id) is not null ? null : Require( _source.ReadSetupCollision(id), "Setup collision", id); _cache.CacheSetup(id, setup, prepared); } private static T Require( PreparedCollisionReadResult result, string kind, uint id) where T : class { if (result.Status == PreparedAssetReadStatus.Loaded && result.Data is not null) { return result.Data; } throw new InvalidDataException( $"{kind} 0x{id:X8} is {result.Status}. " + "Live collision cannot publish only one representation."); } }