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.
 
 

167 lines
6.4 KiB

using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Threading;
using gaemstone.Utility;
using gaemstone.Utility.IL;
using static flecs_hub.flecs;
namespace gaemstone.ECS;
public unsafe partial class Universe
{
public SystemInfo RegisterSystem(Action<Iterator> callback, string expression,
Phase? phase = null, string? name = null)
=> RegisterSystem(name ?? callback.Method.Name, expression, phase ?? Phase.OnUpdate, new() { expr = expression }, callback);
public SystemInfo RegisterSystem(Action<Iterator> callback, ecs_filter_desc_t filter,
Phase? phase = null, string? name = null)
=> RegisterSystem(name ?? callback.Method.Name, null, phase ?? Phase.OnUpdate, filter, callback);
public SystemInfo RegisterSystem(string name, string? expression,
Phase phase, ecs_filter_desc_t filter, Action<Iterator> callback)
{
var _phase = Systems._phaseLookup[phase];
var entityDesc = default(ecs_entity_desc_t);
entityDesc.name = name;
entityDesc.add[0] = !_phase.IsNone ? (EcsDependsOn & _phase) : default;
entityDesc.add[1] = _phase;
// TODO: Provide a nice way to create these entity descriptors.
var systemDesc = default(ecs_system_desc_t);
systemDesc.entity = Create(entityDesc);
systemDesc.binding_ctx = (void*)UniverseSystems.CreateSystemCallbackContext(this, callback);
systemDesc.callback.Data.Pointer = &UniverseSystems.SystemCallback;
systemDesc.query.filter = filter;
var entity = new Entity(this, ecs_system_init(Handle, &systemDesc));
var system = new SystemInfo(this, entity, name, expression, phase, filter, callback);
Systems._systems.Add(system);
return system;
}
public SystemInfo RegisterSystem(Delegate action)
{
var name = action.Method.Name;
var attr = action.Method.Get<SystemAttribute>();
var phase = attr?.Phase ?? Phase.OnUpdate;
if (action is Action<Iterator> iterAction) {
if (attr?.Expression == null) throw new Exception(
"System must specify expression in SystemAttribute");
return RegisterSystem(name, attr.Expression, phase, new() { expr = attr.Expression }, iterAction);
} else {
var method = action.GetType().GetMethod("Invoke")!;
var gen = QueryActionGenerator.GetOrBuild(this, method);
var filter = (attr?.Expression == null) ? gen.Filter : new() { expr = attr.Expression };
return RegisterSystem(name, attr?.Expression, phase, filter,
iter => gen.RunWithTryCatch(action.Target, iter));
}
}
public SystemInfo RegisterSystem(object? instance, MethodInfo method)
{
var attr = method.Get<SystemAttribute>();
var phase = attr?.Phase ?? Phase.OnUpdate;
var param = method.GetParameters();
if (param.Length == 1 && param[0].ParameterType == typeof(Iterator)) {
if (attr?.Expression == null) throw new Exception(
"System must specify expression in SystemAttribute");
var action = (Action<Iterator>)Delegate.CreateDelegate(typeof(Action<Iterator>), instance, method);
return RegisterSystem(method.Name, attr.Expression, phase, new() { expr = attr.Expression }, action);
} else {
var gen = QueryActionGenerator.GetOrBuild(this, method);
var filter = (attr?.Expression == null) ? gen.Filter : new() { expr = attr.Expression };
return RegisterSystem(method.Name, attr?.Expression, phase, filter,
iter => gen.RunWithTryCatch(instance, iter));
}
}
public class UniverseSystems
: IReadOnlyCollection<SystemInfo>
{
public readonly struct SystemCallbackContext
{
public Universe Universe { get; }
public Action<Iterator> Callback { get; }
public SystemCallbackContext(Universe universe, Action<Iterator> callback)
{ Universe = universe; Callback = callback; }
}
private static SystemCallbackContext[] _systemCallbackContexts = new SystemCallbackContext[64];
private static int _systemCallbackContextsCount = 0;
public static nint CreateSystemCallbackContext(Universe universe, Action<Iterator> callback)
{
var data = new SystemCallbackContext(universe, callback);
var count = Interlocked.Increment(ref _systemCallbackContextsCount);
if (count > _systemCallbackContexts.Length)
Array.Resize(ref _systemCallbackContexts, count * 2);
_systemCallbackContexts[count - 1] = data;
return count;
}
public static SystemCallbackContext GetSystemCallbackContext(nint context)
=> _systemCallbackContexts[(int)context - 1];
[UnmanagedCallersOnly]
internal static void SystemCallback(ecs_iter_t* iter)
{
var data = GetSystemCallbackContext((nint)iter->binding_ctx);
data.Callback(new Iterator(data.Universe, null, *iter));
}
internal readonly List<SystemInfo> _systems = new();
internal readonly Dictionary<Phase, Entity> _phaseLookup = new();
internal UniverseSystems(Universe universe)
{
_phaseLookup.Add(Phase.PreFrame, new(universe, pinvoke_EcsPreFrame()));
_phaseLookup.Add(Phase.OnLoad, new(universe, pinvoke_EcsOnLoad()));
_phaseLookup.Add(Phase.PostLoad, new(universe, pinvoke_EcsPostLoad()));
_phaseLookup.Add(Phase.PreUpdate, new(universe, pinvoke_EcsPreUpdate()));
_phaseLookup.Add(Phase.OnUpdate, new(universe, pinvoke_EcsOnUpdate()));
_phaseLookup.Add(Phase.OnValidate, new(universe, pinvoke_EcsOnValidate()));
_phaseLookup.Add(Phase.PostUpdate, new(universe, pinvoke_EcsPostUpdate()));
_phaseLookup.Add(Phase.PreStore, new(universe, pinvoke_EcsPreStore()));
_phaseLookup.Add(Phase.OnStore, new(universe, pinvoke_EcsOnStore()));
_phaseLookup.Add(Phase.PostFrame, new(universe, pinvoke_EcsPostFrame()));
}
// IReadOnlyCollection implementation
public int Count => _systems.Count;
public IEnumerator<SystemInfo> GetEnumerator() => _systems.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}
public class SystemInfo
{
public Universe Universe { get; }
public Entity Entity { get; }
public string Name { get; }
public string? Expression { get; }
public Phase Phase { get; }
public ecs_filter_desc_t Filter { get; }
public Action<Iterator> Callback { get; }
internal SystemInfo(Universe universe, Entity entity, string name, string? expression,
Phase phase, ecs_filter_desc_t filter, Action<Iterator> callback)
{
Universe = universe;
Entity = entity;
Name = name;
Expression = expression;
Phase = phase;
Filter = filter;
Callback = callback;
}
}
}