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