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.
168 lines
6.4 KiB
168 lines
6.4 KiB
2 years ago
|
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;
|
||
|
}
|
||
|
}
|
||
|
}
|