Skip to content

Commit

Permalink
Merge pull request #20 from andreakarasho/feat/more-term-op
Browse files Browse the repository at this point in the history
Feat/more term op
  • Loading branch information
andreakarasho authored May 20, 2024
2 parents aca199a + 3629b27 commit 8843406
Show file tree
Hide file tree
Showing 13 changed files with 717 additions and 432 deletions.
431 changes: 234 additions & 197 deletions samples/MyBattleground/Program.cs

Large diffs are not rendered by default.

6 changes: 4 additions & 2 deletions src/Archetype.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using System.Collections.Immutable;
using Microsoft.Collections.Extensions;

namespace TinyEcs;

Expand Down Expand Up @@ -74,6 +73,7 @@ public sealed class Archetype
private readonly World _world;
private readonly ComponentComparer _comparer;
private readonly Dictionary<ulong, int> _lookup;
private readonly EcsID[] _ids;
private int _count;
internal List<EcsEdge> _edgesLeft, _edgesRight;

Expand All @@ -97,6 +97,8 @@ ComponentComparer comparer
{
_lookup.Add(components[i].ID, i);
}

_ids = components.Select(s => s.ID).ToArray();
}

public World World => _world;
Expand Down Expand Up @@ -328,7 +330,7 @@ private bool IsSuperset(ReadOnlySpan<ComponentInfo> other)

internal int FindMatch(ReadOnlySpan<Term> searching)
{
return Match.Validate(_comparer, Components.AsSpan(), searching);
return Match.Validate(_comparer, _ids, searching);
}

public void Print()
Expand Down
10 changes: 5 additions & 5 deletions src/ComponentComparer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ namespace TinyEcs;

sealed class ComponentComparer :
IComparer<ulong>,
IComparer<Term>,
//IComparer<Term>,
IComparer<ComponentInfo>,
IEqualityComparer<ulong>,
IEqualityComparer<ComponentInfo>
Expand All @@ -29,10 +29,10 @@ public int Compare(ulong x, ulong y)
return CompareTerms(_world, x, y);
}

public int Compare(Term x, Term y)
{
return CompareTerms(_world, x.ID, y.ID);
}
// public int Compare(Term x, Term y)
// {
// return CompareTerms(_world, x.ID, y.ID);
// }

public static int CompareTerms(World world, ulong a, ulong b)
{
Expand Down
2 changes: 2 additions & 0 deletions src/DotnetAddons.cs
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,8 @@ internal sealed class CallerArgumentExpressionAttribute : Attribute
// The name of the parameter whose expression should be captured as a string.
public CallerArgumentExpressionAttribute(string parameterName) { }
}

internal static class IsExternalInit {}
}
#endif

Expand Down
6 changes: 0 additions & 6 deletions src/EcsID.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,15 +32,9 @@ namespace TinyEcs;

public static implicit operator ulong(EcsID id) => id.Value;
public static implicit operator EcsID(ulong value) => new (value);
public static implicit operator Term(EcsID value) => Term.With(value.Value);

public static bool operator ==(EcsID id, EcsID other) => id.Value.Equals(other.Value);
public static bool operator !=(EcsID id, EcsID other) => !id.Value.Equals(other.Value);

// public static Term operator !(EcsID id) => Term.Without(id.Value);
// public static Term operator -(EcsID id) => Term.Without(id.Value);
// public static Term operator +(EcsID id) => Term.With(id.Value);


public readonly override bool Equals(object? obj) => obj is EcsID ent && Equals(ent);
public readonly override int GetHashCode() => Value.GetHashCode();
Expand Down
82 changes: 39 additions & 43 deletions src/Match.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,53 +2,49 @@ namespace TinyEcs;

static class Match
{
public static int Validate(IComparer<ulong> comparer, ReadOnlySpan<ComponentInfo> ids, ReadOnlySpan<Term> terms)
public static int Validate(IComparer<ulong> comparer, EcsID[] ids, ReadOnlySpan<Term> terms)
{
int idIndex = 0;
int termIndex = 0;

while (idIndex < ids.Length && termIndex < terms.Length)
foreach (var term in terms)
{
var id = ids[idIndex].ID;
ref readonly var term = ref terms[termIndex];

if (comparer.Compare(id.Value, term.ID.Value) == 0)
{
switch (term.Op)
{
case TermOp.With:
termIndex++;
break;
case TermOp.Without:
return -1; // Forbidden ID found
case TermOp.Optional:
termIndex++;
break;
}
idIndex++;
}
else if (id < term.ID)
switch (term.Op)
{
idIndex++;
}
else if (id > term.ID)
{
if (term.Op == TermOp.With)
{
return 1; // Required ID not found
}
termIndex++;
}
}

// Check any remaining required terms
while (termIndex < terms.Length)
{
if (terms[termIndex].Op == TermOp.With)
{
return 1; // Required ID not found
case TermOp.With:
if (!ids.Any(id => comparer.Compare(id, term.IDs[0]) == 0))
{
return 1; // Required ID not found
}
break;
case TermOp.Without:
if (ids.Any(id => comparer.Compare(id, term.IDs[0]) == 0))
{
return -1; // Forbidden ID found
}
break;
case TermOp.Optional:
// Do nothing, as presence or absence is acceptable
break;
case TermOp.AtLeastOne:
if (!ids.Any(id => term.IDs.Any(tid => comparer.Compare(id, tid) == 0)))
{
return 1; // At least one required ID not found
}
break;
case TermOp.Exactly:
if (!ids.SequenceEqual(term.IDs))
{
return 1; // Exact match required but not found
}
break;
case TermOp.None:
if (ids.Any(id => term.IDs.Any(tid => comparer.Compare(id, tid) == 0)))
{
return -1; // None of the specified IDs should be present
}
break;
case TermOp.Or:
// Or is applied on query side
break;
}
termIndex++;
}

return 0;
Expand Down
83 changes: 63 additions & 20 deletions src/Query.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,11 @@ namespace TinyEcs;
public ref struct QueryInternal
{
private readonly ReadOnlySpan<Archetype> _archetypes;
private readonly ReadOnlySpan<Term> _terms;
private int _index;

internal QueryInternal(ReadOnlySpan<Archetype> archetypes, ReadOnlySpan<Term> terms)
internal QueryInternal(ReadOnlySpan<Archetype> archetypes)
{
_archetypes = archetypes;
_terms = terms;
_index = -1;
}

Expand Down Expand Up @@ -62,7 +60,7 @@ public QueryBuilder With(EcsID action, EcsID target)

public QueryBuilder With(EcsID id)
{
_components.Add(Term.With(id));
_components.Add(new(id, TermOp.With));
return this;
}

Expand All @@ -83,7 +81,7 @@ public QueryBuilder Without(EcsID action, EcsID target)

public QueryBuilder Without(EcsID id)
{
_components.Add(Term.Without(id));
_components.Add(new (id, TermOp.Without));
return this;
}

Expand All @@ -92,7 +90,7 @@ public QueryBuilder Optional<T>() where T :struct

public QueryBuilder Optional(EcsID id)
{
_components.Add(new Term() { ID = id, Op = TermOp.Optional });
_components.Add(new (id, TermOp.Optional));
return this;
}

Expand All @@ -108,18 +106,18 @@ public Query Build()
}


public sealed partial class Query<TQuery> : Query
where TQuery : struct
public sealed partial class Query<TQueryData> : Query
where TQueryData : struct
{
internal Query(World world) : base(world, Lookup.Query<TQuery>.Terms)
internal Query(World world) : base(world, Lookup.Query<TQueryData>.Terms)
{
}
}

public sealed partial class Query<TQuery, TFilter> : Query
where TQuery : struct where TFilter : struct
public sealed partial class Query<TQueryData, TQueryFilter> : Query
where TQueryData : struct where TQueryFilter : struct
{
internal Query(World world) : base(world, Lookup.Query<TQuery, TFilter>.Terms)
internal Query(World world) : base(world, Lookup.Query<TQueryData, TQueryFilter>.Terms)
{
}
}
Expand All @@ -129,41 +127,78 @@ public partial class Query : IDisposable
private readonly ImmutableArray<Term> _terms;
private readonly List<Archetype> _matchedArchetypes;
private ulong _lastArchetypeIdMatched = 0;
private Query? _subQuery;

internal Query(World world, ImmutableArray<Term> terms)
{
World = world;
_terms = terms;
_matchedArchetypes = new List<Archetype>();

_terms = terms.Where(s => s.Op != TermOp.Or)
.ToImmutableSortedSet()
.ToImmutableArray();

ref var subQuery = ref _subQuery;

foreach (var or in terms.Where(s => s.Op == TermOp.Or).Reverse())
{
var orIds = or.IDs.Select(s => new Term(s, TermOp.With));
subQuery = World.GetQuery
(
Hashing.Calculate(orIds.ToArray()),
orIds.ToImmutableArray(),
static (world, terms) => new Query(world, terms)
);

subQuery = ref subQuery._subQuery;
}
}

public World World { get; internal set; }
internal List<Archetype> MatchedArchetypes => _matchedArchetypes;

internal CountdownEvent ThreadCounter { get; } = new CountdownEvent(1);

public void Dispose() => ThreadCounter.Dispose();


public void Dispose()
{
_subQuery?.Dispose();
ThreadCounter.Dispose();
}

internal void Match()
{
_subQuery?.Match();

var allArchetypes = World.Archetypes;

if (allArchetypes.IsEmpty || _lastArchetypeIdMatched == allArchetypes[^1].Id)
return;

var terms = _terms.AsSpan();
var first = World.FindArchetype(Hashing.Calculate(terms));
var ids = _terms
.Where(s => s.Op == TermOp.With || s.Op == TermOp.Exactly)
.SelectMany(s => s.IDs);

var first = World.FindArchetype(Hashing.Calculate(ids));
if (first == null)
return;

_lastArchetypeIdMatched = allArchetypes[^1].Id;
_matchedArchetypes.Clear();
World.MatchArchetypes(first, terms, _matchedArchetypes);
World.MatchArchetypes(first, _terms.AsSpan(), _matchedArchetypes);
}

public int Count()
{
Match();
return _matchedArchetypes.Sum(static s => s.Count);

var count = _matchedArchetypes.Sum(static s => s.Count);
if (count == 0 && _subQuery != null)
{
return _subQuery.Count();
}

return count;
}

public ref T Single<T>() where T : struct
Expand Down Expand Up @@ -199,7 +234,15 @@ public QueryInternal GetEnumerator()
{
Match();

return new (CollectionsMarshal.AsSpan(_matchedArchetypes), _terms.AsSpan());
if (_subQuery != null)
{
if (_matchedArchetypes.All(static s => s.Count == 0))
{
return _subQuery.GetEnumerator();
}
}

return new (CollectionsMarshal.AsSpan(_matchedArchetypes));
}

public void Each(QueryFilterDelegateWithEntity fn)
Expand Down
6 changes: 3 additions & 3 deletions src/System.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ namespace TinyEcs;

using SysParamMap = Dictionary<Type, ISystemParam>;

public sealed partial class FuncSystem<TArg>
public sealed partial class FuncSystem<TArg> where TArg : notnull
{
private readonly TArg _arg;
private readonly Action<TArg, SysParamMap, SysParamMap, Func<SysParamMap, TArg, bool>> _fn;
Expand Down Expand Up @@ -216,7 +216,7 @@ public World() : this(256) { }
void ISystemParam.New(object arguments) { }
}

partial class Query<TQuery> : ISystemParam
partial class Query<TQueryData> : ISystemParam
{
public Query() : this (null!) { }

Expand All @@ -226,7 +226,7 @@ void ISystemParam.New(object arguments)
}
}

partial class Query<TQuery, TFilter> : ISystemParam
partial class Query<TQueryData, TQueryFilter> : ISystemParam
{
public Query() : this (null!) { }

Expand Down
Loading

0 comments on commit 8843406

Please sign in to comment.