Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/dotnet.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ jobs:
- name: Run tests with coverage
run: |
cd ./src/Hexecs.Tests/
dotnet test -c Release --no-build /p:CollectCoverage=true /p:CoverletOutput=TestResults/ /p:CoverletOutputFormat=opencover
dotnet test -c Release --no-build /p:CollectCoverage=true /p:CoverletOutput=TestResults/ /p:CoverletOutputFormat=opencover /p:Exclude="[*Tests*]*|[*Benchmarks*]*|[*Demo*]*"
- name: Publish coverage report
if: matrix.os == 'ubuntu-latest' && matrix.dotnet-version == '10.0.x'
uses: codecov/codecov-action@v3
Expand Down
83 changes: 83 additions & 0 deletions src/Hexecs.Tests/Assets/AssetFilter1Should.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
using Hexecs.Tests.Mocks;

namespace Hexecs.Tests.Assets;

public sealed class AssetFilter1Should(AssetTestFixture fixture) : IClassFixture<AssetTestFixture>
{
[Fact(DisplayName = "Фильтр ассетов должен содержать все созданные ассеты")]
public void ContainsAllAssets()
{
// arrange
var assetIds = new List<uint>();

var (context, world) = fixture.CreateAssetContext(loader =>
{
for (int i = 1; i < 100; i++)
{
var asset = loader.CreateAsset(new CarAsset(i, i));
assetIds.Add(asset.Id);
}
});

var expectedAssets = assetIds.Select(id => context.GetAsset(id)).ToArray();

// act

var filter = context.Filter<CarAsset>();
var actualActors = filter.ToArray();

// assert

actualActors
.Should()
.Contain(expectedAssets);

world.Dispose();
}

[Fact(DisplayName = "Фильтр ассетов можно перебирать как AssetRef")]
public void AssetFilterShouldEnumerable()
{
// arrange
var expectedIds = new Dictionary<uint, CarAsset>();

var (context, world) = fixture.CreateAssetContext(loader =>
{
for (var i = 0; i < 100; i++)
{
var component = new CarAsset(i, i);
var asset = loader.CreateAsset(component);

expectedIds.Add(asset.Id, component);
}
});

// act

var filter = context.Filter<CarAsset>();

// assert

var actualIds = new List<uint>();
foreach (var asset in filter)
{
actualIds.Add(asset.Id);
asset
.Component1
.Should().Be(expectedIds[asset.Id]);
}

filter.Length
.Should().Be(expectedIds.Count);

actualIds
.Should()
.HaveCount(expectedIds.Count);

actualIds
.Should()
.Contain(expectedIds.Keys);

world.Dispose();
}
}
3 changes: 3 additions & 0 deletions src/Hexecs.Tests/Hexecs.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
<PropertyGroup>
<IsPackable>false</IsPackable>
<IsTestProject>true</IsTestProject>
<CollectCoverage>true</CollectCoverage>
<CoverletOutputFormat>opencover</CoverletOutputFormat>
<Exclude>[*Tests*]*,[*Benchmarks*]*,[*Demo*]*</Exclude>
</PropertyGroup>

<ItemGroup>
Expand Down
12 changes: 12 additions & 0 deletions src/Hexecs.Tests/Mocks/CarAsset.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using Hexecs.Assets;

namespace Hexecs.Tests.Mocks;

public readonly struct CarAsset(int price, int speed) : IAssetComponent
{
public const string Alias1 = "Alias1";
public const string Alias2 = "Alias2";

public readonly int Price = price;
public readonly int Speed = speed;
}
2 changes: 1 addition & 1 deletion src/Hexecs/Actors/ActorContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ public Actor Clone(uint actorId, bool withParent = true)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Actor CreateActor(uint? expectedId = null)
{
if (expectedId == Actor.EmptyId) ActorError.WrongId();
if (expectedId == Actor.EmptyId) ActorError.InvalidId();
var actorId = expectedId ?? GetNextActorId();

AddEntry(actorId);
Expand Down
12 changes: 6 additions & 6 deletions src/Hexecs/Actors/ActorError.cs
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,12 @@ public static T QueueNotFound<T>()
throw new Exception($"System {TypeOf<T>.GetTypeName()} isn't found");
}

[DoesNotReturn]
public static void InvalidId()
{
throw new Exception("Invalid actor id");
}

/// <summary>
/// Генерирует исключение, когда актёр с указанным ключом не найден.
/// </summary>
Expand Down Expand Up @@ -255,10 +261,4 @@ public static void ValueNotFound(string name, Type type)
{
throw new Exception($"Value '{name}' (type {TypeOf.GetTypeName(type)}) isn't found");
}

[DoesNotReturn]
public static void WrongId()
{
throw new Exception("Wrong actor id");
}
}
1 change: 1 addition & 0 deletions src/Hexecs/Assets/AssetContext.Dictionary.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ public int Length

private ref Entry AddEntry(uint id)
{
if (id == Asset.EmptyId) AssetError.InvalidId();
ref var entry = ref CollectionsMarshal.GetValueRefOrAddDefault(_entries, id, out var exists);
if (exists) AssetError.AlreadyExists(id);
return ref entry;
Expand Down
2 changes: 1 addition & 1 deletion src/Hexecs/Assets/AssetContext.Entry.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ public void Add(ushort componentId)
ArrayUtils.InsertOrCreate(ref _array, _length, componentId);
_length++;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly ReadOnlySpan<ushort> AsReadOnlySpan() => _length == 0
? ReadOnlySpan<ushort>.Empty
Expand Down
11 changes: 8 additions & 3 deletions src/Hexecs/Assets/AssetContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,14 @@ public Asset<T1> GetAsset<T1>()
where T1 : struct, IAssetComponent
{
var pool = GetComponentPool<T1>();
if (pool is { Length: > 0 }) return pool.First();
if (pool != null)
{
var firstId = pool.FirstId();
if (firstId != Asset.EmptyId)
{
return new Asset<T1>(this, firstId);
}
}

AssetError.NotFound<T1>();
return Asset<T1>.Empty;
Expand All @@ -92,8 +99,6 @@ public Asset<T1> GetAsset<T1>()
public Asset<T1> GetAsset<T1>(uint assetId)
where T1 : struct, IAssetComponent
{
Debug.Assert(ExistsAsset(assetId), $"Asset {assetId} isn't found");

var pool = GetComponentPool<T1>();
if (pool == null || !pool.Has(assetId)) AssetError.ComponentNotFound<T1>(assetId);

Expand Down
6 changes: 6 additions & 0 deletions src/Hexecs/Assets/AssetError.cs
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,12 @@ public static void ConstraintExists<T>()
throw new Exception($"Constraint for {TypeOf<T>.GetTypeName()} already exists");
}

[DoesNotReturn]
public static void InvalidId()
{
throw new Exception("Invalid asset id");
}

/// <summary>
/// Генерирует исключение при попытке использовать загрузчик ассетов, который уже освобожден.
/// </summary>
Expand Down
22 changes: 21 additions & 1 deletion src/Hexecs/Assets/AssetFilter1.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,25 @@ public AssetRef<T1> Get(uint assetId)
ref _pool1.GetByIndex(entry.Index1));
}

public Asset[] ToArray()
{
var dictionary = _dictionary;

var count = dictionary.Count;
if (count == 0) return [];

var assets = new Asset[count];
var ctx = Context;

var index = 0;
foreach (var assetId in dictionary.Keys)
{
assets[index++] = new Asset(ctx, assetId);
}

return assets;
}

private static FrozenDictionary<uint, Entry> Collect(
AssetContext context,
AssetComponentPool<T1> pool1,
Expand Down Expand Up @@ -73,7 +92,8 @@ private static FrozenDictionary<uint, Entry> Collect(
length++;
}

var result = buffer.ToFrozenDictionary();
var segment = new ArraySegment<KeyValuePair<uint, Entry>>(buffer, 0, length);
var result = segment.ToFrozenDictionary();

bufferPool.Return(buffer, true);

Expand Down
22 changes: 21 additions & 1 deletion src/Hexecs/Assets/AssetFilter2.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,25 @@ ref _pool1.GetByIndex(entry.Index1),
ref _pool2.GetByIndex(entry.Index2));
}

public Asset[] ToArray()
{
var dictionary = _dictionary;

var count = dictionary.Count;
if (count == 0) return [];

var assets = new Asset[count];
var ctx = Context;

var index = 0;
foreach (var assetId in dictionary.Keys)
{
assets[index++] = new Asset(ctx, assetId);
}

return assets;
}

private static FrozenDictionary<uint, Entry> Collect(
AssetContext context,
AssetComponentPool<T1> pool1,
Expand Down Expand Up @@ -81,7 +100,8 @@ private static FrozenDictionary<uint, Entry> Collect(
length++;
}

var result = buffer.ToFrozenDictionary();
var segment = new ArraySegment<KeyValuePair<uint, Entry>>(buffer, 0, length);
var result = segment.ToFrozenDictionary();

bufferPool.Return(buffer, true);

Expand Down
22 changes: 21 additions & 1 deletion src/Hexecs/Assets/AssetFilter3.cs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,25 @@ ref _pool2.GetByIndex(entry.Index2),
ref _pool3.GetByIndex(entry.Index3));
}

public Asset[] ToArray()
{
var dictionary = _dictionary;

var count = dictionary.Count;
if (count == 0) return [];

var assets = new Asset[count];
var ctx = Context;

var index = 0;
foreach (var assetId in dictionary.Keys)
{
assets[index++] = new Asset(ctx, assetId);
}

return assets;
}

private static FrozenDictionary<uint, Entry> Collect(
AssetContext context,
AssetComponentPool<T1> pool1,
Expand Down Expand Up @@ -89,7 +108,8 @@ private static FrozenDictionary<uint, Entry> Collect(
length++;
}

var result = buffer.ToFrozenDictionary();
var segment = new ArraySegment<KeyValuePair<uint, Entry>>(buffer, 0, length);
var result = segment.ToFrozenDictionary();

bufferPool.Return(buffer, true);

Expand Down
Loading