From 49ae2efc19793318fc45bbc50cd24ec410d75085 Mon Sep 17 00:00:00 2001 From: copygirl Date: Thu, 22 Dec 2022 02:53:19 +0100 Subject: [PATCH] Create built-in Relation tag Entities meant to be uses as relations are now marked as such with this tag. --- .../Components/ResourceComponents.cs | 6 ++-- src/gaemstone/ECS/Attributes.cs | 16 --------- src/gaemstone/ECS/Relation.cs | 22 ++++++++++++ src/gaemstone/ECS/Universe+Modules.cs | 36 ++++++++++--------- src/gaemstone/ECS/Universe.cs | 18 +++++++--- src/gaemstone/Flecs/Core.cs | 8 ++--- src/gaemstone/Flecs/DeletionEvent.cs | 4 +-- 7 files changed, 64 insertions(+), 46 deletions(-) create mode 100644 src/gaemstone/ECS/Relation.cs diff --git a/src/gaemstone.Client/Components/ResourceComponents.cs b/src/gaemstone.Client/Components/ResourceComponents.cs index 5dd9fc7..dcb566a 100644 --- a/src/gaemstone.Client/Components/ResourceComponents.cs +++ b/src/gaemstone.Client/Components/ResourceComponents.cs @@ -14,11 +14,9 @@ public class ResourceComponents // Entities can also have a (Texture, $T) pair where $T is a resource, // meaning the entity has that resource assigned as their texture. - // TODO: Reintroduce IsA when flecs bug is fixed. - // https://github.com/SanderMertens/flecs/issues/858 - [Tag, Relation]//, IsA] + [Relation, Tag, IsA] public struct Texture { } - [Tag, Relation]//, IsA] + [Relation, Tag, IsA] public struct Mesh { } } diff --git a/src/gaemstone/ECS/Attributes.cs b/src/gaemstone/ECS/Attributes.cs index 0a80b16..e59b81b 100644 --- a/src/gaemstone/ECS/Attributes.cs +++ b/src/gaemstone/ECS/Attributes.cs @@ -50,22 +50,6 @@ public class AddAttribute : AddRelationAttribute [AttributeUsage(AttributeTargets.Struct)] public class TagAttribute : AddAttribute, ICreateEntityAttribute { } -/// -/// Marked entity represents a relationship type, meaning it may be used as -/// the "relation" in a pair. However, this attribute is purely informational. -/// -/// -/// The relationship may have component data associated with -/// it when added to an entity under these circumstances: -/// -/// If marked as a , does not carry data. -/// If marked as a , carries the relation's data. -/// If marked with neither, will carry the target's data, if it's a component. -/// -/// -[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Enum)] -public class RelationAttribute : Attribute, ICreateEntityAttribute { } - /// public class IsAAttribute : AddAttribute { } diff --git a/src/gaemstone/ECS/Relation.cs b/src/gaemstone/ECS/Relation.cs new file mode 100644 index 0000000..5b564e0 --- /dev/null +++ b/src/gaemstone/ECS/Relation.cs @@ -0,0 +1,22 @@ +using System; + +namespace gaemstone.ECS; + +[Tag] +public struct Relation { } + +/// +/// Marked entity represents a relationship type. +/// It may be used as the "relation" in a pair. +/// +/// +/// The relationship may have component data associated with +/// it when added to an entity under these circumstances: +/// +/// If marked as a , does not carry data. +/// If marked as a , carries the relation's data. +/// If marked with neither, will carry the target's data, if it's a component. +/// +/// +[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Enum)] +public class RelationAttribute : Attribute, ICreateEntityAttribute { } diff --git a/src/gaemstone/ECS/Universe+Modules.cs b/src/gaemstone/ECS/Universe+Modules.cs index 5124954..942e54a 100644 --- a/src/gaemstone/ECS/Universe+Modules.cs +++ b/src/gaemstone/ECS/Universe+Modules.cs @@ -29,42 +29,44 @@ public class ModuleManager public EntityRef Register() where T : class => Register(typeof(T)); - public EntityRef Register(Type type) + public EntityRef Register(Type moduleType) { - if (!type.IsClass || type.IsGenericType || type.IsGenericTypeDefinition) throw new Exception( - $"Module {type} must be a non-generic class"); - if (type.Get() is not ModuleAttribute attr) throw new Exception( - $"Module {type} must be marked with ModuleAttribute"); + if (!moduleType.IsClass || moduleType.IsGenericType || moduleType.IsGenericTypeDefinition) throw new Exception( + $"Module {moduleType} must be a non-generic class"); + if (moduleType.Get() is not ModuleAttribute attr) throw new Exception( + $"Module {moduleType} must be marked with ModuleAttribute"); // Check if module type is static. - if (type.IsAbstract && type.IsSealed) { + if (moduleType.IsAbstract && moduleType.IsSealed) { // Static modules represent existing modules, as such they don't // create entities, only look up existing ones to add type lookups // for use with the Lookup(Type) method. if (attr.Path == null) throw new Exception( - $"Existing module {type} must have ModuleAttribute.Name set"); + $"Existing module {moduleType} must have ModuleAttribute.Name set"); var path = new EntityPath(true, attr.Path); - var entity = Universe.Lookup(path) ?? throw new Exception( - $"Existing module {type} with name '{path}' not found"); + var module = Universe.Lookup(path) ?? throw new Exception( + $"Existing module {moduleType} with name '{path}' not found"); // This implementation is pretty naive. It simply gets all nested // types which are tagged with an ICreateEntityAttribute base // attribute and creates a lookup mapping. No sanity checking. - foreach (var nested in type.GetNestedTypes()) { - if (!nested.GetCustomAttributes(true).OfType().Any()) continue; - var name = nested.Get()?.Path?.Single() ?? nested.Name; - Universe.LookupOrThrow(entity, name).CreateLookup(nested); + foreach (var type in moduleType.GetNestedTypes()) { + if (!type.GetCustomAttributes(true).OfType().Any()) continue; + var name = type.Get()?.Path?.Single() ?? type.Name; + var entity = Universe.LookupOrThrow(module, name); + entity.CreateLookup(type); + if (type.Has()) entity.Add(); } - return entity; + return module; } else { - var path = GetModulePath(type); - var module = new ModuleInfo(Universe, type, path); + var path = GetModulePath(moduleType); + var module = new ModuleInfo(Universe, moduleType, path); _modules.Add(module.Entity, module); TryEnableModule(module); return module.Entity; @@ -194,6 +196,8 @@ internal class ModuleInfo var builder = path.IsAbsolute ? Universe.New(path) : Entity.NewChild(path); if (!type.IsPublic) builder.Symbol(path.Name); + if (type.Has()) builder.Add(); + var entity = builder.Build(); EntityRef Lookup(Type toLookup) diff --git a/src/gaemstone/ECS/Universe.cs b/src/gaemstone/ECS/Universe.cs index eb647af..14f2707 100644 --- a/src/gaemstone/ECS/Universe.cs +++ b/src/gaemstone/ECS/Universe.cs @@ -9,7 +9,8 @@ public unsafe partial class Universe public ecs_world_t* Handle { get; } public ModuleManager Modules { get; } - // flecs built-ins that are important to internals. + public EntityRef Wildcard { get; } + public EntityRef Any { get; } public EntityRef ChildOf { get; } public bool IsDeferred => ecs_is_deferred(this); @@ -19,15 +20,24 @@ public unsafe partial class Universe Handle = ecs_init_w_args(args.Length, null); Modules = new(this); + // Bootstrap flecs built-ins that are important to internals. + Wildcard = LookupOrThrow("/flecs/core/*"); + Any = LookupOrThrow("/flecs/core/_"); + ChildOf = LookupOrThrow("/flecs/core/ChildOf"); + + // Bootstrap custom "Relation" tag. + // This will be retrofitted to certain flecs built-ins. + New("/gaemstone/Relation") + .Add(LookupOrThrow("/flecs/core/Tag")) + .Build().CreateLookup(); + // Register built-in (static) modules, which // are defined in the "gaemstone.Flecs" namespace. Modules.RegisterAll(GetType().Assembly.GetTypes() .Where(t => t.IsAbstract && t.IsSealed)); - ChildOf = LookupOrThrow(); - // Create "Game" entity to store global state. - New("Game").Symbol("Game").Build() + New("/Game").Symbol("Game").Build() .CreateLookup().Add(); } diff --git a/src/gaemstone/Flecs/Core.cs b/src/gaemstone/Flecs/Core.cs index 15b866f..78bd04e 100644 --- a/src/gaemstone/Flecs/Core.cs +++ b/src/gaemstone/Flecs/Core.cs @@ -24,9 +24,9 @@ public static class Core // Entity Relationships - [Tag, Relation] public struct IsA { } - [Tag, Relation] public struct ChildOf { } - [Tag, Relation] public struct DependsOn { } + [Relation, Tag] public struct IsA { } + [Relation, Tag] public struct ChildOf { } + [Relation, Tag] public struct DependsOn { } // Component / Relationship Properties @@ -39,6 +39,6 @@ public static class Core [Tag] public struct Union { } [Tag] public struct Exclusive { } [Tag] public struct Acyclic { } - [Tag, Relation] public struct With { } + [Relation, Tag] public struct With { } [Tag] public struct OneOf { } } diff --git a/src/gaemstone/Flecs/DeletionEvent.cs b/src/gaemstone/Flecs/DeletionEvent.cs index 97927b1..1263254 100644 --- a/src/gaemstone/Flecs/DeletionEvent.cs +++ b/src/gaemstone/Flecs/DeletionEvent.cs @@ -5,8 +5,8 @@ namespace gaemstone.Flecs; [Module("flecs", "core")] public static class DeletionEvent { - [Tag, Relation] public struct OnDelete { } - [Tag, Relation] public struct OnDeleteTarget { } + [Relation, Tag] public struct OnDelete { } + [Relation, Tag] public struct OnDeleteTarget { } } [Module("flecs", "core")]