feat(headless): hydrate isolated collision worlds

This commit is contained in:
Erik 2026-07-27 09:25:58 +02:00
parent 9569dadb57
commit b6547ff38c
20 changed files with 1960 additions and 101 deletions

View file

@ -1,5 +1,7 @@
using System.Collections.Immutable;
using AcDream.Content;
using AcDream.Headless.Configuration;
using DatReaderWriter.DBObjs;
namespace AcDream.Headless.Hosting;
@ -25,7 +27,8 @@ internal interface IHeadlessProcessContentFactory
internal sealed record HeadlessOpenedProcessContent(
IDatReaderWriter Dats,
IPreparedAssetSource Prepared,
MagicCatalog Magic);
MagicCatalog Magic,
ImmutableArray<float> HeightTable);
internal sealed class ProductionHeadlessProcessContentFactory
: IHeadlessProcessContentFactory
@ -50,10 +53,24 @@ internal sealed class ProductionHeadlessProcessContentFactory
dats,
diagnostic);
MagicCatalog magic = MagicCatalog.Load(dats);
Region region = dats.Get<Region>(0x13000000u)
?? throw new InvalidOperationException(
"Region dat id 0x13000000 is missing.");
float[]? sourceHeightTable =
region.LandDefs.LandHeightTable;
if (sourceHeightTable is null
|| sourceHeightTable.Length < 256)
{
throw new InvalidOperationException(
"Region.LandDefs.LandHeightTable is missing or truncated.");
}
ImmutableArray<float> heightTable =
ImmutableArray.CreateRange(sourceHeightTable);
return new HeadlessOpenedProcessContent(
dats,
prepared,
magic);
magic,
heightTable);
}
catch
{
@ -80,6 +97,7 @@ internal sealed class HeadlessProcessContentOwner : IDisposable
{
private readonly object _gate = new();
private readonly MagicCatalog _magic;
private readonly ImmutableArray<float> _heightTable;
private IDatReaderWriter? _dats;
private IPreparedAssetSource? _prepared;
private SharedPreparedCollisionCache? _collision;
@ -105,8 +123,13 @@ internal sealed class HeadlessProcessContentOwner : IDisposable
IDatReaderWriter? dats = opened.Dats;
IPreparedAssetSource? prepared = opened.Prepared;
MagicCatalog? magic = opened.Magic;
ImmutableArray<float> heightTable = opened.HeightTable;
bool incomplete =
dats is null || prepared is null || magic is null;
dats is null
|| prepared is null
|| magic is null
|| heightTable.IsDefaultOrEmpty
|| heightTable.Length < 256;
if (incomplete || prepared is not IPreparedCollisionSource)
{
try
@ -127,6 +150,7 @@ internal sealed class HeadlessProcessContentOwner : IDisposable
_dats = dats!;
_prepared = prepared!;
_magic = magic!;
_heightTable = heightTable;
_collision = new SharedPreparedCollisionCache(
(IPreparedCollisionSource)prepared!);
}
@ -149,7 +173,8 @@ internal sealed class HeadlessProcessContentOwner : IDisposable
_dats!,
_prepared!,
_collision!,
_magic);
_magic,
_heightTable);
}
}
@ -220,6 +245,7 @@ internal sealed class HeadlessProcessContentOwner : IDisposable
private readonly IPreparedAssetSource _prepared;
private readonly IPreparedCollisionSource _collision;
private readonly MagicCatalog _magic;
private readonly ImmutableArray<float> _heightTable;
internal HeadlessProcessContentLease(
HeadlessProcessContentOwner owner,
@ -227,7 +253,8 @@ internal sealed class HeadlessProcessContentOwner : IDisposable
IDatReaderWriter dats,
IPreparedAssetSource prepared,
IPreparedCollisionSource collision,
MagicCatalog magic)
MagicCatalog magic,
ImmutableArray<float> heightTable)
{
_owner = owner;
SessionId = sessionId;
@ -235,6 +262,7 @@ internal sealed class HeadlessProcessContentOwner : IDisposable
_prepared = prepared;
_collision = collision;
_magic = magic;
_heightTable = heightTable;
}
internal string SessionId { get; }
@ -275,6 +303,15 @@ internal sealed class HeadlessProcessContentOwner : IDisposable
}
}
internal ImmutableArray<float> HeightTable
{
get
{
ObjectDisposedException.ThrowIf(_owner is null, this);
return _heightTable;
}
}
public void Dispose()
{
HeadlessProcessContentOwner? owner =