You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
184 lines
5.7 KiB
184 lines
5.7 KiB
using System; |
|
using System.Collections.Generic; |
|
using System.Diagnostics; |
|
using System.Linq; |
|
using System.Reflection; |
|
using System.Runtime.InteropServices; |
|
using gaemstone.Utility; |
|
using static flecs_hub.flecs; |
|
|
|
namespace gaemstone.ECS; |
|
|
|
[Entity] |
|
public struct Game { } |
|
|
|
[Component] |
|
public unsafe partial class Universe |
|
{ |
|
// Roles |
|
public static ecs_id_t ECS_PAIR { get; } = pinvoke_ECS_PAIR(); |
|
public static ecs_id_t ECS_OVERRIDE { get; } = pinvoke_ECS_OVERRIDE(); |
|
|
|
// Relationships |
|
public static ecs_entity_t EcsIsA { get; } = pinvoke_EcsIsA(); |
|
public static ecs_entity_t EcsDependsOn { get; } = pinvoke_EcsDependsOn(); |
|
public static ecs_entity_t EcsChildOf { get; } = pinvoke_EcsChildOf(); |
|
public static ecs_entity_t EcsSlotOf { get; } = pinvoke_EcsSlotOf(); |
|
|
|
// Entity Tags |
|
public static ecs_entity_t EcsPrefab { get; } = pinvoke_EcsPrefab(); |
|
|
|
|
|
private readonly Dictionary<Type, ecs_entity_t> _byType = new(); |
|
|
|
public ecs_world_t* Handle { get; } |
|
|
|
public UniverseSystems Systems { get; } |
|
public UniverseModules Modules { get; } |
|
|
|
public Universe(string[]? args = null) |
|
{ |
|
[UnmanagedCallersOnly] |
|
static void Abort() => throw new FlecsAbortException(); |
|
|
|
ecs_os_set_api_defaults(); |
|
var api = ecs_os_get_api(); |
|
api.abort_ = new FnPtr_Void { Pointer = &Abort }; |
|
ecs_os_set_api(&api); |
|
|
|
if (args?.Length > 0) { |
|
var argv = Runtime.CStrings.CStringArray(args); |
|
Handle = ecs_init_w_args(args.Length, argv); |
|
Runtime.CStrings.FreeCStrings(argv, args.Length); |
|
} else { |
|
Handle = ecs_init(); |
|
} |
|
|
|
Systems = new(this); |
|
Modules = new(this); |
|
|
|
RegisterAll(typeof(Universe).Assembly); |
|
} |
|
|
|
public bool Progress(TimeSpan delta) |
|
{ |
|
if (Modules._deferred.Count > 0) throw new Exception( |
|
"Modules with unmet dependencies: \n" + |
|
string.Join(" \n", Modules._deferred.Values.Select( |
|
m => m.Type + " is missing " + string.Join(", ", m.UnmetDependencies)))); |
|
return ecs_progress(this, (float)delta.TotalSeconds); |
|
} |
|
|
|
|
|
public Entity Lookup<T>() |
|
=> Lookup(typeof(T)); |
|
public Entity Lookup(Type type) |
|
=> _byType.TryGetValue(type, out var e) ? new(this, e) : default; |
|
|
|
public Entity Lookup(string path) |
|
=> new(this, !path.Contains('.') ? ecs_lookup(this, path) |
|
: ecs_lookup_path_w_sep(this, default, path, ".", default, true)); |
|
public Entity Lookup(ecs_entity_t value) |
|
=> new(this, ecs_get_alive(this, value)); |
|
|
|
|
|
public void RegisterAll(Assembly? from = null) |
|
{ |
|
from ??= Assembly.GetEntryAssembly()!; |
|
foreach (var type in from.GetTypes()) { |
|
var isPartOfModule = type.DeclaringType?.Has<ModuleAttribute>() == true; |
|
if (type.Has<RelationAttribute>()) { |
|
if (!isPartOfModule) RegisterRelation(type); |
|
} else if (type.Has<ComponentAttribute>()) { |
|
if (!isPartOfModule) RegisterComponent(type); |
|
} else if (type.Has<TagAttribute>()) { |
|
if (!isPartOfModule) RegisterTag(type); |
|
} else if (type.Has<EntityAttribute>()) { |
|
if (!isPartOfModule) RegisterEntity(type); |
|
} else if (type.Has<ModuleAttribute>()) |
|
RegisterModule(type); |
|
} |
|
} |
|
|
|
public Entity RegisterRelation<T>() |
|
=> RegisterRelation(typeof(T)); |
|
public Entity RegisterRelation(Type type) |
|
=> throw new NotImplementedException(); |
|
|
|
public Entity RegisterComponent<T>() |
|
=> RegisterComponent(typeof(T)); |
|
public Entity RegisterComponent(Type type) |
|
{ |
|
var typeInfo = default(ecs_type_info_t); |
|
if (type.IsValueType) { |
|
var wrapper = TypeWrapper.For(type); |
|
if (!wrapper.IsUnmanaged) throw new Exception( |
|
"Component struct must satisfy the unmanaged constraint. " + |
|
"(Must not contain any reference types or structs that contain references.)"); |
|
var structLayout = type.StructLayoutAttribute; |
|
if (structLayout == null || structLayout.Value == LayoutKind.Auto) throw new Exception( |
|
"Component struct must have a StructLayout attribute with LayoutKind sequential or explicit. " + |
|
"This is to ensure that the struct fields are not reorganized by the C# compiler."); |
|
typeInfo.size = wrapper.Size; |
|
typeInfo.alignment = structLayout.Pack; |
|
} else { |
|
typeInfo.size = sizeof(nint); |
|
typeInfo.alignment = sizeof(nint); |
|
} |
|
|
|
var name = type.GetFriendlyName(); |
|
var entityDesc = new ecs_entity_desc_t { name = name, symbol = name }; |
|
var componentDesc = new ecs_component_desc_t { entity = Create(entityDesc), type = typeInfo }; |
|
|
|
var id = ecs_component_init(Handle, &componentDesc); |
|
_byType[type] = id; |
|
// TODO: SetHooks(hooks, id); |
|
var entity = new Entity(this, id); |
|
|
|
if (type.Has<EntityAttribute>()) { |
|
if (type.IsValueType) entity.Add(entity); |
|
else entity.Set(type, Activator.CreateInstance(type)!); |
|
} |
|
|
|
return entity; |
|
} |
|
|
|
public Entity RegisterTag<T>() |
|
where T : unmanaged |
|
=> RegisterTag(typeof(T)); |
|
public Entity RegisterTag(Type type) |
|
{ |
|
if (!type.IsValueType || type.IsPrimitive || type.GetFields().Length > 0) |
|
throw new Exception("Tag must be an empty, used-defined struct."); |
|
var entity = Create(type.GetFriendlyName()); |
|
_byType.Add(type, entity); |
|
return entity; |
|
} |
|
|
|
public Entity RegisterEntity<T>() |
|
where T : unmanaged |
|
=> RegisterEntity(typeof(T)); |
|
public Entity RegisterEntity(Type type) |
|
{ |
|
if (!type.IsValueType || type.IsPrimitive || type.GetFields().Length > 0) |
|
throw new Exception("Entity must be an empty, used-defined struct."); |
|
var entity = Create(type.GetFriendlyName()); |
|
_byType.Add(type, entity); |
|
return entity; |
|
} |
|
|
|
|
|
public Entity Create() |
|
=> Create(new ecs_entity_desc_t()); |
|
public Entity Create(string name) |
|
=> Create(new ecs_entity_desc_t { name = name }); |
|
public Entity Create(ecs_entity_desc_t desc) |
|
{ |
|
var entity = ecs_entity_init(Handle, &desc); |
|
Debug.Assert(entity.Data != 0, "ECS_INVALID_PARAMETER"); |
|
return new(this, entity); |
|
} |
|
|
|
|
|
public static implicit operator ecs_world_t*(Universe w) => w.Handle; |
|
}
|
|
|