acdream/src/AcDream.App/Physics/LiveCollisionAssetPublisher.cs
Erik d9446030e6 feat(streaming): shadow-publish flat collision assets
Carry one immutable prepared collision closure with each accepted near-tier generation and install graph plus flat views through the same retained publication receipt. Apply the same strict package-only rule to live entities, add exact sampled graph-authoritative comparison artifacts and lifecycle counters, and prove cancellation, demotion, rehydrate, revisit, teardown, reconnect, and the nine-stop route with 14,064 zero-mismatch samples.

Co-authored-by: OpenAI Codex <codex@openai.com>
2026-07-25 16:38:54 +02:00

68 lines
2.2 KiB
C#

using AcDream.Content;
using AcDream.Core.Physics;
using DatReaderWriter.DBObjs;
namespace AcDream.App.Physics;
/// <summary>
/// Publishes the graph and prepared collision views for DAT objects discovered
/// through the live-object path. Streamed statics carry their prepared closure
/// on <c>LandblockBuild</c>; live CreateObject/appearance data arrives outside
/// that transaction and therefore uses this matching strict publisher.
/// </summary>
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<T>(
PreparedCollisionReadResult<T> 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.");
}
}