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

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;
}