feat(headless): share immutable gameplay content
This commit is contained in:
parent
12b500d383
commit
9569dadb57
25 changed files with 1031 additions and 429 deletions
|
|
@ -4,6 +4,7 @@ using AcDream.App.Composition;
|
|||
using AcDream.App.Diagnostics;
|
||||
using AcDream.App.Rendering;
|
||||
using AcDream.App.Spells;
|
||||
using AcDream.Content;
|
||||
using AcDream.App.UI;
|
||||
using AcDream.App.UI.Layout;
|
||||
using AcDream.App.World;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
using AcDream.App.Rendering;
|
||||
using AcDream.App.UI;
|
||||
using AcDream.Content;
|
||||
using AcDream.Content.Vfx;
|
||||
using AcDream.Core.World;
|
||||
using DatReaderWriter;
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ using System.Numerics;
|
|||
using AcDream.App.Rendering;
|
||||
using AcDream.App.Rendering.Selection;
|
||||
using AcDream.App.UI;
|
||||
using AcDream.Content;
|
||||
using AcDream.Core.Textures;
|
||||
using AcDream.Core.Selection;
|
||||
using DatReaderWriter;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
using System.Numerics;
|
||||
using AcDream.App.Spells;
|
||||
using AcDream.Content;
|
||||
using AcDream.App.UI;
|
||||
using AcDream.App.UI.Layout;
|
||||
using AcDream.Core.Combat;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
using AcDream.App.Spells;
|
||||
using AcDream.Content;
|
||||
using AcDream.App.UI;
|
||||
using AcDream.App.UI.Layout;
|
||||
using AcDream.Core.Items;
|
||||
|
|
|
|||
143
tests/AcDream.Content.Tests/SharedPreparedCollisionCacheTests.cs
Normal file
143
tests/AcDream.Content.Tests/SharedPreparedCollisionCacheTests.cs
Normal file
|
|
@ -0,0 +1,143 @@
|
|||
using System.Collections.Immutable;
|
||||
using AcDream.Content.Pak;
|
||||
using AcDream.Core.Physics;
|
||||
|
||||
namespace AcDream.Content.Tests;
|
||||
|
||||
public sealed class SharedPreparedCollisionCacheTests
|
||||
{
|
||||
[Fact]
|
||||
public void LoadedPayloadIsSharedByReferenceAndLeastRecentlyUsedIsBounded()
|
||||
{
|
||||
var source = new FixtureSource();
|
||||
using var cache = new SharedPreparedCollisionCache(
|
||||
source,
|
||||
capacity: 2);
|
||||
|
||||
FlatSetupCollision first =
|
||||
Assert.IsType<FlatSetupCollision>(
|
||||
cache.ReadSetupCollision(1u).Data);
|
||||
Assert.Same(
|
||||
first,
|
||||
cache.ReadSetupCollision(1u).Data);
|
||||
_ = cache.ReadSetupCollision(2u);
|
||||
Assert.Same(
|
||||
first,
|
||||
cache.ReadSetupCollision(1u).Data);
|
||||
_ = cache.ReadSetupCollision(3u);
|
||||
|
||||
SharedPreparedCollisionCacheSnapshot snapshot =
|
||||
cache.CaptureSnapshot();
|
||||
Assert.Equal(2, snapshot.Count);
|
||||
Assert.True(snapshot.IsBounded);
|
||||
Assert.Equal(2, snapshot.Hits);
|
||||
Assert.Equal(3, snapshot.Misses);
|
||||
Assert.Equal(1, source.SetupReads[1u]);
|
||||
Assert.Equal(1, source.SetupReads[2u]);
|
||||
|
||||
_ = cache.ReadSetupCollision(2u);
|
||||
|
||||
Assert.Equal(2, source.SetupReads[2u]);
|
||||
Assert.True(cache.CaptureSnapshot().IsBounded);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TypeParticipatesInIdentityAndDisposeDoesNotOwnPackage()
|
||||
{
|
||||
var source = new FixtureSource();
|
||||
var cache = new SharedPreparedCollisionCache(source, capacity: 4);
|
||||
|
||||
FlatSetupCollision setup =
|
||||
Assert.IsType<FlatSetupCollision>(
|
||||
cache.ReadSetupCollision(7u).Data);
|
||||
FlatGfxObjCollisionAsset gfx =
|
||||
Assert.IsType<FlatGfxObjCollisionAsset>(
|
||||
cache.ReadGfxObjCollision(7u).Data);
|
||||
|
||||
Assert.NotSame(setup, gfx);
|
||||
Assert.Equal(1, source.SetupReads[7u]);
|
||||
Assert.Equal(1, source.GfxReads[7u]);
|
||||
|
||||
cache.Dispose();
|
||||
|
||||
Assert.False(source.IsDisposed);
|
||||
Assert.True(cache.CaptureSnapshot().IsDisposed);
|
||||
Assert.Throws<ObjectDisposedException>(
|
||||
() => cache.ReadSetupCollision(7u));
|
||||
}
|
||||
|
||||
private sealed class FixtureSource : IPreparedCollisionSource
|
||||
{
|
||||
internal Dictionary<uint, int> SetupReads { get; } = new();
|
||||
internal Dictionary<uint, int> GfxReads { get; } = new();
|
||||
internal bool IsDisposed { get; private set; }
|
||||
|
||||
public PreparedCollisionSourceStats CollisionStats => default;
|
||||
|
||||
public PreparedAssetPresence ProbeCollision(
|
||||
PakAssetType type,
|
||||
uint sourceFileId) =>
|
||||
PreparedAssetPresence.Available;
|
||||
|
||||
public PreparedCollisionReadResult<FlatGfxObjCollisionAsset>
|
||||
ReadGfxObjCollision(
|
||||
uint sourceFileId,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
Increment(GfxReads, sourceFileId);
|
||||
return PreparedCollisionReadResult<
|
||||
FlatGfxObjCollisionAsset>.Loaded(
|
||||
new FlatGfxObjCollisionAsset(
|
||||
EmptyPhysicsBsp(),
|
||||
null,
|
||||
null));
|
||||
}
|
||||
|
||||
public PreparedCollisionReadResult<FlatSetupCollision>
|
||||
ReadSetupCollision(
|
||||
uint sourceFileId,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
Increment(SetupReads, sourceFileId);
|
||||
return PreparedCollisionReadResult<FlatSetupCollision>.Loaded(
|
||||
new FlatSetupCollision(
|
||||
ImmutableArray<FlatCollisionCylinder>.Empty,
|
||||
ImmutableArray<FlatCollisionSphere>.Empty,
|
||||
0f,
|
||||
0f,
|
||||
0f,
|
||||
0f));
|
||||
}
|
||||
|
||||
public PreparedCollisionReadResult<
|
||||
FlatCellStructureCollisionAsset>
|
||||
ReadCellStructureCollision(
|
||||
uint sourceFileId,
|
||||
CancellationToken cancellationToken = default) =>
|
||||
PreparedCollisionReadResult<
|
||||
FlatCellStructureCollisionAsset>.Missing;
|
||||
|
||||
public PreparedCollisionReadResult<FlatEnvCellTopology>
|
||||
ReadEnvCellTopology(
|
||||
uint sourceFileId,
|
||||
CancellationToken cancellationToken = default) =>
|
||||
PreparedCollisionReadResult<FlatEnvCellTopology>.Missing;
|
||||
|
||||
public void Dispose() => IsDisposed = true;
|
||||
|
||||
private static FlatPhysicsBsp EmptyPhysicsBsp() => new(
|
||||
-1,
|
||||
ImmutableArray<FlatPhysicsBspNode>.Empty,
|
||||
ImmutableArray<int>.Empty,
|
||||
FlatPolygonTable.Empty);
|
||||
|
||||
private static void Increment(
|
||||
Dictionary<uint, int> counts,
|
||||
uint id) =>
|
||||
counts[id] = counts.TryGetValue(id, out int count)
|
||||
? count + 1
|
||||
: 1;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
using AcDream.App.Spells;
|
||||
using AcDream.Content;
|
||||
using AcDream.Core.Spells;
|
||||
using DatReaderWriter;
|
||||
using DatReaderWriter.DBObjs;
|
||||
|
|
@ -9,7 +9,7 @@ using DatMagicSchool = DatReaderWriter.Enums.MagicSchool;
|
|||
using DatSpellCategory = DatReaderWriter.Enums.SpellCategory;
|
||||
using CoreSpellTable = AcDream.Core.Spells.SpellTable;
|
||||
|
||||
namespace AcDream.App.Tests.Spells;
|
||||
namespace AcDream.Content.Tests.Spells;
|
||||
|
||||
public sealed class RetailSpellMetadataProjectorTests
|
||||
{
|
||||
|
|
@ -79,7 +79,8 @@ public sealed class RetailSpellMetadataProjectorTests
|
|||
string? csvPath = ResolveRepoFile("docs", "research", "data", "spells.csv");
|
||||
if (datDir is null || csvPath is null) return;
|
||||
|
||||
using var dats = new DatCollection(datDir, DatAccessType.Read);
|
||||
using var rawDats = new DatCollection(datDir, DatAccessType.Read);
|
||||
using var dats = new DatCollectionAdapter(rawDats);
|
||||
MagicCatalog catalog = MagicCatalog.Load(dats);
|
||||
CoreSpellTable legacy = CoreSpellTable.LoadFromCsv(csvPath);
|
||||
|
||||
|
|
@ -106,7 +107,8 @@ public sealed class RetailSpellMetadataProjectorTests
|
|||
string? csvPath = ResolveRepoFile("docs", "research", "data", "spells.csv");
|
||||
if (datDir is null || csvPath is null) return;
|
||||
|
||||
using var dats = new DatCollection(datDir, DatAccessType.Read);
|
||||
using var rawDats = new DatCollection(datDir, DatAccessType.Read);
|
||||
using var dats = new DatCollectionAdapter(rawDats);
|
||||
MagicCatalog catalog = MagicCatalog.Load(dats);
|
||||
CoreSpellTable legacy = CoreSpellTable.LoadFromCsv(csvPath);
|
||||
var mismatches = new List<string>();
|
||||
|
|
@ -1,8 +1,7 @@
|
|||
using AcDream.App.Spells;
|
||||
using AcDream.Core.Items;
|
||||
using AcDream.Core.Spells;
|
||||
|
||||
namespace AcDream.App.Tests.Spells;
|
||||
namespace AcDream.Core.Tests.Spells;
|
||||
|
||||
public sealed class SpellComponentRequirementServiceTests
|
||||
{
|
||||
|
|
@ -111,6 +111,9 @@ public sealed class HeadlessProcessContentOwnerTests
|
|||
Assert.Equal(1, factory.OpenCount);
|
||||
Assert.Equal(sessionCount, host.Sessions.Count);
|
||||
Assert.Equal(sessionCount, host.Content?.LeaseCount);
|
||||
IPreparedCollisionSource sharedCollision =
|
||||
Assert.IsAssignableFrom<IPreparedCollisionSource>(
|
||||
host.Sessions[0].Content?.PreparedCollision);
|
||||
Assert.All(
|
||||
host.Sessions,
|
||||
session =>
|
||||
|
|
@ -121,6 +124,12 @@ public sealed class HeadlessProcessContentOwnerTests
|
|||
Assert.Same(
|
||||
factory.PreparedResource,
|
||||
session.Content?.PreparedAssets);
|
||||
Assert.Same(
|
||||
MagicCatalog.Empty,
|
||||
session.Content?.MagicCatalog);
|
||||
Assert.Same(
|
||||
sharedCollision,
|
||||
session.Content?.PreparedCollision);
|
||||
});
|
||||
Assert.Equal(
|
||||
sessionCount,
|
||||
|
|
@ -206,12 +215,15 @@ public sealed class HeadlessProcessContentOwnerTests
|
|||
internal TestResourceProxy Dats { get; }
|
||||
internal TestResourceProxy Prepared { get; }
|
||||
|
||||
public (IDatReaderWriter Dats, IPreparedAssetSource Prepared) Open(
|
||||
public HeadlessOpenedProcessContent Open(
|
||||
HeadlessContentDescriptor descriptor,
|
||||
Action<string> diagnostic)
|
||||
{
|
||||
OpenCount++;
|
||||
return (DatsResource, PreparedResource);
|
||||
return new HeadlessOpenedProcessContent(
|
||||
DatsResource,
|
||||
PreparedResource,
|
||||
MagicCatalog.Empty);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue