Create built-in Relation tag

Entities meant to be uses as relations
are now marked as such with this tag.
wip/source-generators
copygirl 1 year ago
parent b534d3db55
commit 49ae2efc19
  1. 6
      src/gaemstone.Client/Components/ResourceComponents.cs
  2. 16
      src/gaemstone/ECS/Attributes.cs
  3. 22
      src/gaemstone/ECS/Relation.cs
  4. 36
      src/gaemstone/ECS/Universe+Modules.cs
  5. 18
      src/gaemstone/ECS/Universe.cs
  6. 8
      src/gaemstone/Flecs/Core.cs
  7. 4
      src/gaemstone/Flecs/DeletionEvent.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<Resource>]
[Relation, Tag, IsA<Resource>]
public struct Texture { }
[Tag, Relation]//, IsA<Resource>]
[Relation, Tag, IsA<Resource>]
public struct Mesh { }
}

@ -50,22 +50,6 @@ public class AddAttribute<TRelation, TTarget> : AddRelationAttribute
[AttributeUsage(AttributeTargets.Struct)]
public class TagAttribute : AddAttribute<Tag>, ICreateEntityAttribute { }
/// <summary>
/// Marked entity represents a relationship type, meaning it may be used as
/// the "relation" in a pair. However, this attribute is purely informational.
/// </summary>
/// <remarks>
/// The relationship may have component data associated with
/// it when added to an entity under these circumstances:
/// <list type="bullet">
/// <item>If marked as a <see cref="TagAttribute"/>, does not carry data.</item>
/// <item>If marked as a <see cref="ComponentAttribute"/>, carries the relation's data.</item>
/// <item>If marked with neither, will carry the target's data, if it's a component.</item>
/// </list>
/// </remarks>
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Enum)]
public class RelationAttribute : Attribute, ICreateEntityAttribute { }
/// <seealso cref="IsA"/>
public class IsAAttribute<TTarget> : AddAttribute<IsA, TTarget> { }

@ -0,0 +1,22 @@
using System;
namespace gaemstone.ECS;
[Tag]
public struct Relation { }
/// <summary>
/// Marked entity represents a relationship type.
/// It may be used as the "relation" in a pair.
/// </summary>
/// <remarks>
/// The relationship may have component data associated with
/// it when added to an entity under these circumstances:
/// <list type="bullet">
/// <item>If marked as a <see cref="TagAttribute"/>, does not carry data.</item>
/// <item>If marked as a <see cref="ComponentAttribute"/>, carries the relation's data.</item>
/// <item>If marked with neither, will carry the target's data, if it's a component.</item>
/// </list>
/// </remarks>
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Enum)]
public class RelationAttribute : Attribute, ICreateEntityAttribute { }

@ -29,42 +29,44 @@ public class ModuleManager
public EntityRef Register<T>()
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<ModuleAttribute>() 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<ModuleAttribute>() 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<ICreateEntityAttribute>().Any()) continue;
var name = nested.Get<EntityAttribute>()?.Path?.Single() ?? nested.Name;
Universe.LookupOrThrow(entity, name).CreateLookup(nested);
foreach (var type in moduleType.GetNestedTypes()) {
if (!type.GetCustomAttributes(true).OfType<ICreateEntityAttribute>().Any()) continue;
var name = type.Get<EntityAttribute>()?.Path?.Single() ?? type.Name;
var entity = Universe.LookupOrThrow(module, name);
entity.CreateLookup(type);
if (type.Has<RelationAttribute>()) entity.Add<Relation>();
}
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<RelationAttribute>()) builder.Add<Relation>();
var entity = builder.Build();
EntityRef Lookup(Type toLookup)

@ -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<Relation>();
// 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<Flecs.Core.ChildOf>();
// Create "Game" entity to store global state.
New("Game").Symbol("Game").Build()
New("/Game").Symbol("Game").Build()
.CreateLookup<Game>().Add<Game>();
}

@ -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 { }
}

@ -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")]

Loading…
Cancel
Save