From 7b380fd0594454f3b1e427ee6cf455d1570eed13 Mon Sep 17 00:00:00 2001 From: copygirl Date: Tue, 27 Dec 2022 18:20:15 +0100 Subject: [PATCH] Rename Lookup functions for clarity - EntityPath.Lookup can now optionally throw exception --- README.md | 2 +- src/gaemstone.ECS/EntityBase.cs | 44 ++++++++++++------------ src/gaemstone.ECS/EntityPath.cs | 27 +++++++++------ src/gaemstone.ECS/EntityRef.cs | 12 +++---- src/gaemstone.ECS/IdentifierRef.cs | 6 ++-- src/gaemstone.ECS/Iterator.cs | 2 +- src/gaemstone.ECS/World+Lookup.cs | 54 ++++++++++++++++-------------- src/gaemstone.ECS/World.cs | 6 ++-- 8 files changed, 81 insertions(+), 72 deletions(-) diff --git a/README.md b/README.md index 28c0f05..88da6ca 100644 --- a/README.md +++ b/README.md @@ -59,7 +59,7 @@ foreach (var child in entities.GetChildren()) { pos = new(pos.X * 10, pos.Y * 10); } -var onUpdate = world.LookupOrThrow("/flecs/pipeline/OnUpdate"); +var onUpdate = world.LookupByPathOrThrow("/flecs/pipeline/OnUpdate"); // Create a system that will move all entities with // the "Position" component downwards by 2 every frame. diff --git a/src/gaemstone.ECS/EntityBase.cs b/src/gaemstone.ECS/EntityBase.cs index b4fb6f2..52902f2 100644 --- a/src/gaemstone.ECS/EntityBase.cs +++ b/src/gaemstone.ECS/EntityBase.cs @@ -9,23 +9,23 @@ public abstract class EntityBase public abstract TReturn Remove(Identifier id); public abstract bool Has(Identifier id); - public TReturn Add(string symbol) => Add(World.LookupSymbolOrThrow(symbol)); - public TReturn Add() => Add(World.LookupOrThrow(typeof(T))); + public TReturn Add(string symbol) => Add(World.LookupBySymbolOrThrow(symbol)); + public TReturn Add() => Add(World.LookupByTypeOrThrow(typeof(T))); public TReturn Add(Entity relation, Entity target) => Add(Identifier.Pair(relation, target)); - public TReturn Add(Entity target) => Add(World.LookupOrThrow(), target); - public TReturn Add() => Add(World.LookupOrThrow(), World.LookupOrThrow()); + public TReturn Add(Entity target) => Add(World.LookupByTypeOrThrow(), target); + public TReturn Add() => Add(World.LookupByTypeOrThrow(), World.LookupByTypeOrThrow()); - public TReturn Remove(string symbol) => Remove(World.LookupSymbolOrThrow(symbol)); - public TReturn Remove() => Remove(World.LookupOrThrow(typeof(T))); + public TReturn Remove(string symbol) => Remove(World.LookupBySymbolOrThrow(symbol)); + public TReturn Remove() => Remove(World.LookupByTypeOrThrow(typeof(T))); public TReturn Remove(Entity relation, Entity target) => Remove(Identifier.Pair(relation, target)); - public TReturn Remove(Entity target) => Remove(World.LookupOrThrow(), target); - public TReturn Remove() => Remove(World.LookupOrThrow(), World.LookupOrThrow()); + public TReturn Remove(Entity target) => Remove(World.LookupByTypeOrThrow(), target); + public TReturn Remove() => Remove(World.LookupByTypeOrThrow(), World.LookupByTypeOrThrow()); - public bool Has(string symbol) => Has(World.LookupSymbolOrThrow(symbol)); - public bool Has() => Has(World.LookupOrThrow(typeof(T))); + public bool Has(string symbol) => Has(World.LookupBySymbolOrThrow(symbol)); + public bool Has() => Has(World.LookupByTypeOrThrow(typeof(T))); public bool Has(Entity relation, Entity target) => Has(Identifier.Pair(relation, target)); - public bool Has(Entity target) => Has(World.LookupOrThrow(), target); - public bool Has() => Has(World.LookupOrThrow(), World.LookupOrThrow()); + public bool Has(Entity target) => Has(World.LookupByTypeOrThrow(), target); + public bool Has() => Has(World.LookupByTypeOrThrow(), World.LookupByTypeOrThrow()); public abstract T Get(Identifier id); @@ -36,24 +36,24 @@ public abstract class EntityBase public abstract ref T GetRefOrThrow(Identifier id) where T : unmanaged; public abstract void Modified(Identifier id); - public T Get() => Get(World.LookupOrThrow()); - public T? GetOrNull() where T : unmanaged => GetOrNull(World.LookupOrThrow()); - public T? GetOrNull(T _ = null!) where T : class => GetOrNull(World.LookupOrThrow()); - public ref T GetMut() where T : unmanaged => ref GetMut(World.LookupOrThrow()); - public ref T GetRefOrNull() where T : unmanaged => ref GetRefOrNull(World.LookupOrThrow()); - public ref T GetRefOrThrow() where T : unmanaged => ref GetRefOrThrow(World.LookupOrThrow()); - public void Modified() => Modified(World.LookupOrThrow()); + public T Get() => Get(World.LookupByTypeOrThrow()); + public T? GetOrNull() where T : unmanaged => GetOrNull(World.LookupByTypeOrThrow()); + public T? GetOrNull(T _ = null!) where T : class => GetOrNull(World.LookupByTypeOrThrow()); + public ref T GetMut() where T : unmanaged => ref GetMut(World.LookupByTypeOrThrow()); + public ref T GetRefOrNull() where T : unmanaged => ref GetRefOrNull(World.LookupByTypeOrThrow()); + public ref T GetRefOrThrow() where T : unmanaged => ref GetRefOrThrow(World.LookupByTypeOrThrow()); + public void Modified() => Modified(World.LookupByTypeOrThrow()); public abstract TReturn Set(Identifier id, in T value) where T : unmanaged; public abstract TReturn Set(Identifier id, T obj) where T : class; - public TReturn Set(in T value) where T : unmanaged => Set(World.LookupOrThrow(), value); - public TReturn Set(T obj) where T : class => Set(World.LookupOrThrow(), obj); + public TReturn Set(in T value) where T : unmanaged => Set(World.LookupByTypeOrThrow(), value); + public TReturn Set(T obj) where T : class => Set(World.LookupByTypeOrThrow(), obj); public TReturn ChildOf(Entity parent) => Add(World.ChildOf, parent); - public TReturn ChildOf() => Add(World.ChildOf, World.LookupOrThrow()); + public TReturn ChildOf() => Add(World.ChildOf, World.LookupByTypeOrThrow()); public TReturn Disable() => Add(World.Disabled); public TReturn Enable() => Remove(World.Disabled); diff --git a/src/gaemstone.ECS/EntityPath.cs b/src/gaemstone.ECS/EntityPath.cs index 0886a35..e8bade4 100644 --- a/src/gaemstone.ECS/EntityPath.cs +++ b/src/gaemstone.ECS/EntityPath.cs @@ -134,22 +134,29 @@ public class EntityPath } - internal static unsafe Entity Lookup(World world, Entity parent, EntityPath path) + internal static unsafe Entity Lookup( + World world, Entity parent, EntityPath path, bool throwOnNotFound) { - // If path is absolute, ignore parent and start at root. - if (path.IsAbsolute) parent = default; - // Otherwise, if no parent is specified, use the current scope. - else if (parent.IsNone) parent = new(ecs_get_scope(world)); + var start = path.IsAbsolute ? Entity.None // If path is absolute, ignore parent and use root. + : parent.IsNone ? new(ecs_get_scope(world)) // If no parent is specified, use the current scope. + : parent; // Otherwise just use the specified parent. + var current = start; foreach (var part in path) fixed (byte* ptr = part.AsSpan()) { - // FIXME: This breaks when using large entity Ids. - parent = new(ecs_lookup_child(world, parent, ptr)); - if (parent.IsNone || !ecs_is_alive(world, parent)) - return Entity.None; + current = new(ecs_lookup_child(world, current, ptr)); + if (current.IsSome && ecs_is_alive(world, current)) continue; + if (!throwOnNotFound) return Entity.None; + + var startStr = EntityRef.CreateOrNull(world, start)?.GetFullPath().ToString() ?? start.ToString(); + throw new World.EntityNotFoundException( + (start == parent) ? $"Child entity of '{startStr}' at '{path}' not found" + : start.IsSome ? $"Entity at scope '{startStr}' at '{path}' not found" + : $"Entity at '{path}' not found" + ); } - return parent; + return current; } /// Used by . diff --git a/src/gaemstone.ECS/EntityRef.cs b/src/gaemstone.ECS/EntityRef.cs index 96ddff5..57687fd 100644 --- a/src/gaemstone.ECS/EntityRef.cs +++ b/src/gaemstone.ECS/EntityRef.cs @@ -50,10 +50,10 @@ public unsafe class EntityRef public EntityBuilder NewChild(EntityPath? path = null) => World.New(EnsureRelativePath(path)).ChildOf(this); - public EntityRef? Lookup(EntityPath path) - => World.Lookup(this, EnsureRelativePath(path)!); - public EntityRef LookupOrThrow(EntityPath path) - => World.LookupOrThrow(this, EnsureRelativePath(path)!); + public EntityRef? LookupChild(EntityPath path) + => World.LookupByPath(this, EnsureRelativePath(path)!); + public EntityRef LookupChildOrThrow(EntityPath path) + => World.LookupByPathOrThrow(this, EnsureRelativePath(path)!); private static EntityPath? EnsureRelativePath(EntityPath? path) { if (path?.IsAbsolute == true) throw new ArgumentException("path must not be absolute", nameof(path)); return path; } @@ -142,9 +142,9 @@ public unsafe class EntityRef public EntityRef? GetTarget(Entity relation, int index = 0) => CreateOrNull(World, new(ecs_get_target(World, this, relation, index))); public EntityRef? GetTarget(string symbol, int index = 0) - => GetTarget(World.LookupSymbolOrThrow(symbol), index); + => GetTarget(World.LookupBySymbolOrThrow(symbol), index); public EntityRef? GetTarget(int index = 0) - => GetTarget(World.LookupOrThrow(typeof(T)), index); + => GetTarget(World.LookupByTypeOrThrow(typeof(T)), index); public bool Equals(EntityRef? other) => (other is not null) && (World == other.World) && (Entity == other.Entity); public override bool Equals(object? obj) => Equals(obj as EntityRef); diff --git a/src/gaemstone.ECS/IdentifierRef.cs b/src/gaemstone.ECS/IdentifierRef.cs index 4c1459b..ba48a68 100644 --- a/src/gaemstone.ECS/IdentifierRef.cs +++ b/src/gaemstone.ECS/IdentifierRef.cs @@ -27,10 +27,10 @@ public unsafe class IdentifierRef => new(target.World, Identifier.Pair(relation, target)); public EntityRef? AsEntity() - => (Flags == default) ? World.Lookup(new Entity(new() { Data = Id })) : null; + => (Flags == default) ? World.LookupAlive(new Entity(new() { Data = Id })) : null; public (EntityRef Relation, EntityRef Target)? AsPair() - => IsPair && (World.Lookup(Id.RelationUnsafe) is EntityRef relation) && - (World.Lookup(Id.TargetUnsafe ) is EntityRef target ) + => IsPair && (World.LookupAlive(Id.RelationUnsafe) is EntityRef relation) && + (World.LookupAlive(Id.TargetUnsafe ) is EntityRef target ) ? (relation, target) : null; public bool Equals(IdentifierRef? other) => (other is not null) && (World == other.World) && (Id == other.Id); diff --git a/src/gaemstone.ECS/Iterator.cs b/src/gaemstone.ECS/Iterator.cs index 6108867..db6bbe7 100644 --- a/src/gaemstone.ECS/Iterator.cs +++ b/src/gaemstone.ECS/Iterator.cs @@ -98,7 +98,7 @@ public unsafe class Iterator { fixed (ecs_iter_t* ptr = &Value) { var id = ecs_field_id(ptr, index); - var comp = World.LookupOrThrow(); + var comp = World.LookupByTypeOrThrow(); return id == comp.Entity.Value.Data; } } diff --git a/src/gaemstone.ECS/World+Lookup.cs b/src/gaemstone.ECS/World+Lookup.cs index d83733e..088bdb5 100644 --- a/src/gaemstone.ECS/World+Lookup.cs +++ b/src/gaemstone.ECS/World+Lookup.cs @@ -20,38 +20,40 @@ public unsafe partial class World $"Lookup for {type} does not exist"); } - private EntityRef? CreateOrNull(Entity entity) - => EntityRef.CreateOrNull(this, entity); + public EntityRef? LookupByType() + => LookupByType(typeof(T)); + public EntityRef? LookupByType(Type type) + => LookupAlive(_lookupByType.GetValueOrDefault(type)); + public EntityRef LookupByTypeOrThrow() + => LookupByTypeOrThrow(typeof(T)); + public EntityRef LookupByTypeOrThrow(Type type) + => LookupByType(type) ?? throw new EntityNotFoundException( + $"Entity of type {type} not found"); - public EntityRef? Lookup() - => Lookup(typeof(T)); - public EntityRef? Lookup(Type type) - => Lookup(_lookupByType.GetValueOrDefault(type)); + public EntityRef? LookupAlive(Entity value) + => EntityRef.CreateOrNull(this, new(ecs_get_alive(this, value))); + public EntityRef LookupAliveOrThrow(Entity entity) + => LookupAlive(entity) ?? throw new EntityNotFoundException( + $"Entity {entity} is not alive"); - public EntityRef? Lookup(Entity value) - => CreateOrNull(new(ecs_get_alive(this, value))); + public EntityRef? LookupByPath(EntityPath path) + => LookupByPath(default, path); + public EntityRef? LookupByPath(Entity parent, EntityPath path) + => EntityRef.CreateOrNull(this, EntityPath.Lookup(this, parent, path, false)); + public EntityRef LookupByPathOrThrow(EntityPath path) + => LookupByPathOrThrow(default, path); + public EntityRef LookupByPathOrThrow(Entity parent, EntityPath path) + => new(this, EntityPath.Lookup(this, parent, path, true)); - public EntityRef? Lookup(EntityPath path) - => Lookup(default, path); - public EntityRef? Lookup(Entity parent, EntityPath path) - => CreateOrNull(EntityPath.Lookup(this, parent, path)); - public EntityRef? LookupSymbol(string symbol) + public EntityRef? LookupBySymbol(string symbol) { using var alloc = TempAllocator.Use(); - return CreateOrNull(new(ecs_lookup_symbol(this, alloc.AllocateCString(symbol), false))); + var entity = ecs_lookup_symbol(this, alloc.AllocateCString(symbol), false); + return EntityRef.CreateOrNull(this, new(entity)); } - - public EntityRef LookupOrThrow() => LookupOrThrow(typeof(T)); - public EntityRef LookupOrThrow(Type type) => Lookup(type) - ?? throw new EntityNotFoundException($"Entity of type {type} not found"); - public EntityRef LookupOrThrow(Entity entity) => Lookup(entity) - ?? throw new EntityNotFoundException($"Entity {entity} not alive"); - public EntityRef LookupOrThrow(EntityPath path) => Lookup(default, path) - ?? throw new EntityNotFoundException($"Entity '{path}' not found"); - public EntityRef LookupOrThrow(Entity parent, EntityPath path) => Lookup(parent, path) - ?? throw new EntityNotFoundException($"Child entity of {parent} '{path}' not found"); - public EntityRef LookupSymbolOrThrow(string symbol) => LookupSymbol(symbol) - ?? throw new EntityNotFoundException($"Entity with symbol '{symbol}' not found"); + public EntityRef LookupBySymbolOrThrow(string symbol) + => LookupBySymbol(symbol) ?? throw new EntityNotFoundException( + $"Entity with symbol '{symbol}' not found"); public class EntityNotFoundException : Exception diff --git a/src/gaemstone.ECS/World.cs b/src/gaemstone.ECS/World.cs index db8655f..05dd8b7 100644 --- a/src/gaemstone.ECS/World.cs +++ b/src/gaemstone.ECS/World.cs @@ -19,9 +19,9 @@ public unsafe partial class World { Handle = ecs_init_w_args(args.Length, null); - ChildOf = LookupOrThrow("/flecs/core/ChildOf"); - Disabled = LookupOrThrow("/flecs/core/Disabled"); - DependsOn = LookupOrThrow("/flecs/core/DependsOn"); + ChildOf = LookupByPathOrThrow("/flecs/core/ChildOf"); + Disabled = LookupByPathOrThrow("/flecs/core/Disabled"); + DependsOn = LookupByPathOrThrow("/flecs/core/DependsOn"); } public void Dispose()