From db73b54d5396acf8d1cf4e2b141d200ad5c98fb8 Mon Sep 17 00:00:00 2001 From: copygirl Date: Fri, 11 Nov 2022 20:34:01 +0100 Subject: [PATCH] Allow specifying path in EntityAttribute - [Module] now based on [Entity] - Entities can be registered as global --- src/gaemstone/ECS/Component.cs | 6 ++++- src/gaemstone/ECS/Entity.cs | 24 ++++++++++++++--- src/gaemstone/ECS/Module.cs | 11 ++------ src/gaemstone/ECS/Universe+Modules.cs | 39 +++++++++++++-------------- src/gaemstone/Flecs/Core.cs | 12 ++++----- 5 files changed, 53 insertions(+), 39 deletions(-) diff --git a/src/gaemstone/ECS/Component.cs b/src/gaemstone/ECS/Component.cs index 5dcc260..b2c1a87 100644 --- a/src/gaemstone/ECS/Component.cs +++ b/src/gaemstone/ECS/Component.cs @@ -6,7 +6,11 @@ using static flecs_hub.flecs; namespace gaemstone.ECS; [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Enum)] -public class ComponentAttribute : EntityAttribute { } +public class ComponentAttribute : EntityAttribute +{ + public ComponentAttribute() { } + public ComponentAttribute(params string[] path) : base(path) { } +} public static class ComponentExtensions { diff --git a/src/gaemstone/ECS/Entity.cs b/src/gaemstone/ECS/Entity.cs index f974882..f06cc15 100644 --- a/src/gaemstone/ECS/Entity.cs +++ b/src/gaemstone/ECS/Entity.cs @@ -3,9 +3,23 @@ using static flecs_hub.flecs; namespace gaemstone.ECS; -[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct)] +[AttributeUsage(AttributeTargets.Struct)] public class EntityAttribute : Attribute, ICreateEntityAttribute - { public string? Name { get; init; } } +{ + /// If specified, uses this path instead of the default name. + public string[]? Path { get; } + + /// If true, the path will be absolute instead of relative. + public bool Global { get; init; } + + public EntityAttribute() { } + public EntityAttribute(params string[] path) + { + if (path.Length == 0) throw new ArgumentException( + "Path must not be empty", nameof(path)); + Path = path; + } +} /// /// A singleton is a single instance of a tag or component that can be retrieved @@ -13,7 +27,11 @@ public class EntityAttribute : Attribute, ICreateEntityAttribute /// to with itself as the generic type parameter. /// [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Enum)] -public class SingletonAttribute : EntityAttribute { } +public class SingletonAttribute : EntityAttribute +{ + public SingletonAttribute() { } + public SingletonAttribute(params string[] path) : base(path) { } +} public readonly struct Entity : IEquatable diff --git a/src/gaemstone/ECS/Module.cs b/src/gaemstone/ECS/Module.cs index c2aabf4..7644352 100644 --- a/src/gaemstone/ECS/Module.cs +++ b/src/gaemstone/ECS/Module.cs @@ -3,17 +3,10 @@ using System; namespace gaemstone.ECS; [AttributeUsage(AttributeTargets.Class)] -public class ModuleAttribute : Attribute +public class ModuleAttribute : EntityAttribute { - public string[]? Path { get; set; } - public ModuleAttribute() { } - public ModuleAttribute(params string[] path) - { - if (path.Length == 0) throw new ArgumentException( - "Path must not be empty", nameof(path)); - Path = path; - } + public ModuleAttribute(params string[] path) : base(path) { } } public interface IModuleInitializer diff --git a/src/gaemstone/ECS/Universe+Modules.cs b/src/gaemstone/ECS/Universe+Modules.cs index 3186ffa..71f0da6 100644 --- a/src/gaemstone/ECS/Universe+Modules.cs +++ b/src/gaemstone/ECS/Universe+Modules.cs @@ -54,7 +54,7 @@ public class ModuleManager foreach (var nested in type.GetNestedTypes()) { if (!nested.GetCustomAttributes(true).OfType().Any()) continue; - var name = nested.Get()?.Name ?? nested.Name; + var name = nested.Get()?.Path?.Single() ?? nested.Name; Universe.LookupOrThrow(entity, name).CreateLookup(nested); } @@ -97,22 +97,22 @@ public class ModuleManager if (attr == null) throw new ArgumentException( $"Module {type} must be marked with ModuleAttribute", nameof(type)); - // If path is not specified in the attribute, return the type's name. - if (attr.Path == null) { - var assemblyName = type.Assembly.GetName().Name!; + // If module is static, its path will be implictly global. + var global = (type.IsAbstract && type.IsSealed) || attr.Global; - if (!type.FullName!.StartsWith(assemblyName + '.')) throw new InvalidOperationException( - $"Module {type} must be defined under namespace {assemblyName}"); - var fullNameWithoutAssembly = type.FullName![(assemblyName.Length + 1)..]; + // If global or path are specified in the attribute, return an absolute path. + if (global || attr.Path != null) + return new(global, attr.Path ?? new[] { type.Name }); - var parts = fullNameWithoutAssembly.Split('.'); - return new(true, parts.Prepend(assemblyName).ToArray()); - } + // Otherwise, create it based on the type's assembly, namespace and name. + var assemblyName = type.Assembly.GetName().Name!; + + if (!type.FullName!.StartsWith(assemblyName + '.')) throw new InvalidOperationException( + $"Module {type} must be defined under namespace {assemblyName}"); + var fullNameWithoutAssembly = type.FullName![(assemblyName.Length + 1)..]; - var fullPath = new EntityPath(true, attr.Path); - if (!fullPath.IsAbsolute) throw new ArgumentException( - $"Module {type} must have an absolute path (if specified)", nameof(type)); - return fullPath; + var parts = fullNameWithoutAssembly.Split('.'); + return new(true, parts.Prepend(assemblyName).ToArray()); } } @@ -182,13 +182,12 @@ internal class ModuleInfo throw new Exception($"Type {typeHint} must be an empty, used-defined struct."); } - var name = type.Get()?.Name ?? proxyType.Name; - try { EntityPath.ValidateName(name); } - catch (Exception ex) { throw new Exception( - $"{type} has invalid entity name '{name}: {ex.Message}'", ex); } + var path = (type.Get() is EntityAttribute entityAttr) + ? new EntityPath(entityAttr.Global, entityAttr.Path ?? new[] { proxyType.Name }) + : new EntityPath(false, proxyType.Name); - var builder = Entity.NewChild(name); - if (!type.Has()) builder.Symbol(name); + var builder = path.IsAbsolute ? Universe.New(path) : Entity.NewChild(path); + if (!type.Has()) builder.Symbol(path.Name); foreach (var attr in type.GetMultiple()) builder.Add(Universe.LookupOrThrow(attr.Entity)); diff --git a/src/gaemstone/Flecs/Core.cs b/src/gaemstone/Flecs/Core.cs index 38c036a..887fc81 100644 --- a/src/gaemstone/Flecs/Core.cs +++ b/src/gaemstone/Flecs/Core.cs @@ -15,12 +15,12 @@ public static class Core // Entities - [Entity] public struct World { } - [Entity(Name = "*")] public struct Wildcard { } - [Entity(Name = "_")] public struct Any { } - [Entity] public struct This { } - [Entity(Name = "$")] public struct Variable { } - [Entity] public struct Flag { } + [Entity] public struct World { } + [Entity("*")] public struct Wildcard { } + [Entity("_")] public struct Any { } + [Entity] public struct This { } + [Entity("$")] public struct Variable { } + [Entity] public struct Flag { } // Entity Relationships