Initial Commit (#2)
Move to this branch for ecs changes Reviewed-on: #2
This commit is contained in:
parent
fe7eba4573
commit
171b36d571
8 changed files with 436 additions and 0 deletions
109
src/ComponentManager.bf
Normal file
109
src/ComponentManager.bf
Normal file
|
@ -0,0 +1,109 @@
|
||||||
|
namespace Theater_ECS;
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Collections;
|
||||||
|
|
||||||
|
class ComponentManager
|
||||||
|
{
|
||||||
|
private Monitor _Lock = new .() ~ delete _;
|
||||||
|
|
||||||
|
private int_cosize _ComponentSize = -1;
|
||||||
|
private readonly int _IndexSize = sizeof(int_cosize);
|
||||||
|
|
||||||
|
private int_cosize _Next = 0;
|
||||||
|
private List<uint8> _Data = null ~ delete _;
|
||||||
|
private List<int_cosize> _Available = new .() ~ delete _;
|
||||||
|
private HashSet<int_cosize> _Deleted = new .() ~ delete _;
|
||||||
|
|
||||||
|
///Make the component manager useable for a specific type of component
|
||||||
|
public void Initialize<T>(int_cosize count) where T : struct
|
||||||
|
{
|
||||||
|
_ComponentSize = sizeof(T);
|
||||||
|
_Data = new .( //So that we dont have to realloc as frequently
|
||||||
|
(_IndexSize + _ComponentSize) * count
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
///Creates space for a new component instance and returns a pointer to it
|
||||||
|
public (Component, void*) Create(Entity owner)
|
||||||
|
{
|
||||||
|
if (_Available.Count > 0)
|
||||||
|
{
|
||||||
|
var idx = _Available.PopFront();
|
||||||
|
_Deleted.Remove(idx);
|
||||||
|
|
||||||
|
*(int_cosize*)(void*)&_Data[idx * (_ComponentSize + _IndexSize)] = owner;
|
||||||
|
void* toReturn = (void*)&_Data[idx * (_ComponentSize + _IndexSize) + _IndexSize];
|
||||||
|
return (
|
||||||
|
idx,
|
||||||
|
toReturn
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Grow();
|
||||||
|
*(int_cosize*)(void*)(_Data.Ptr + (_Next * (_ComponentSize + _IndexSize))) = owner;
|
||||||
|
//This calculates to the next biggest position + the offset of the index
|
||||||
|
void* toReturn = (void*)(_Data.Ptr + (_Next * (_ComponentSize + _IndexSize) + _IndexSize));
|
||||||
|
return (
|
||||||
|
_Next++,
|
||||||
|
toReturn
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
///Remove an entry, and returns a pointer to the removed object, if you want to
|
||||||
|
public Result<void*> Remove(int_cosize idx)
|
||||||
|
{
|
||||||
|
if (idx >= _Next || _Deleted.Contains(idx))
|
||||||
|
return .Err;
|
||||||
|
|
||||||
|
*(int_cosize*)(void*)&_Data[idx * (_ComponentSize + _IndexSize)] = -1;
|
||||||
|
|
||||||
|
_Deleted.Add(idx);
|
||||||
|
|
||||||
|
return .Ok(
|
||||||
|
(void*)(_Data.Ptr + idx * (_ComponentSize + _IndexSize) + _IndexSize)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int_cosize Count
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return (.)(_Data.Count / (_ComponentSize + _IndexSize));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Span<(Entity, T)> GetAll<T>() where T : struct
|
||||||
|
{
|
||||||
|
return .((.)(void*)_Data.Ptr, Count);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Unchecked]
|
||||||
|
public T* Get<T>(int_cosize idx) where T : struct
|
||||||
|
{
|
||||||
|
return (T*)(void*)(_Data.Ptr + idx * (_ComponentSize + _IndexSize) + _IndexSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Unchecked]
|
||||||
|
public void GetEntities(List<Entity> list)
|
||||||
|
{
|
||||||
|
var ec = (_Data.Count / (_ComponentSize + _IndexSize));
|
||||||
|
list.GrowUninitialized(ec);
|
||||||
|
for (int i < ec)
|
||||||
|
*(list.Ptr + (i)) = *(Entity*)(void*)(_Data.Ptr +i * (_ComponentSize + _IndexSize));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Use() => _Lock.Enter();
|
||||||
|
public void StopUsing() => _Lock.Exit();
|
||||||
|
|
||||||
|
|
||||||
|
public void Grow(int_cosize amount = 1)
|
||||||
|
{ //Make more size useable
|
||||||
|
_Data.GrowUninitialized((_ComponentSize + _IndexSize) * amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
102
src/ECS.bf
Normal file
102
src/ECS.bf
Normal file
|
@ -0,0 +1,102 @@
|
||||||
|
namespace Theater_ECS;
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
|
||||||
|
public typealias Entity = int_cosize;
|
||||||
|
|
||||||
|
public typealias Component = int_cosize;
|
||||||
|
|
||||||
|
class ECS
|
||||||
|
{
|
||||||
|
public int_cosize MaxComponents;
|
||||||
|
|
||||||
|
private int_cosize _ComponentsNext = 0;
|
||||||
|
private List<ComponentManager> _ComponentsPacked = new .() ~ DeleteContainerAndItems!(_);
|
||||||
|
private Dictionary<Type, Component> _ComponentMap = new .() ~ delete _;
|
||||||
|
|
||||||
|
private Entity _EntityNext = 0;
|
||||||
|
private List<List<int_cosize>> _EntityList = new .() ~ DeleteContainerAndItems!(_);
|
||||||
|
private Queue<Entity> _EntityAvailable = new .() ~ delete _;
|
||||||
|
private HashSet<Entity> _DeletedEntities = new .() ~ delete _;
|
||||||
|
|
||||||
|
public Component RegisterComponent<T>(int_cosize count) where T : struct
|
||||||
|
{
|
||||||
|
_ComponentsPacked.Add(new ComponentManager()..Initialize<T>(count));
|
||||||
|
_EntityList.Add(new .(MaxComponents));
|
||||||
|
_ComponentMap.Add(typeof(T), _ComponentsNext);
|
||||||
|
return _ComponentsNext++;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Component GetComponent<T>()
|
||||||
|
{
|
||||||
|
if(_ComponentMap.ContainsKey(typeof(T)))
|
||||||
|
return _ComponentMap[typeof(T)];
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Entity CreateEntity()
|
||||||
|
{
|
||||||
|
if(_EntityAvailable.Count > 0)
|
||||||
|
{
|
||||||
|
var e = _EntityAvailable.PopFront();
|
||||||
|
_DeletedEntities.Remove(e);
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for(var i in _EntityList)
|
||||||
|
i.Add(-1);
|
||||||
|
return _EntityNext++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RemoveEntity(Entity e)
|
||||||
|
{
|
||||||
|
if(e >= _EntityNext || _DeletedEntities.Contains(e))
|
||||||
|
return;
|
||||||
|
|
||||||
|
_DeletedEntities.Add(e);
|
||||||
|
_EntityAvailable.Add(e);
|
||||||
|
|
||||||
|
for(int i < _EntityList.Count)
|
||||||
|
{
|
||||||
|
if(_EntityList[i][e] != -1)
|
||||||
|
{
|
||||||
|
_ComponentsPacked[i].Remove(_EntityList[i][e]);
|
||||||
|
_EntityList[i][e] = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Result<void*> AddComponentToEntity(Entity e, Component c)
|
||||||
|
{
|
||||||
|
if(e >= _EntityNext || _DeletedEntities.Contains(e))
|
||||||
|
return .Err;
|
||||||
|
|
||||||
|
var res = _ComponentsPacked[c].Create(e);
|
||||||
|
_EntityList[c][e] = res.0;
|
||||||
|
return res.1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Result<void*> RemoveComponentFromEntity(Entity e, Component c)
|
||||||
|
{
|
||||||
|
if(e >= _EntityNext || _DeletedEntities.Contains(e))
|
||||||
|
return .Err;
|
||||||
|
|
||||||
|
return _ComponentsPacked[c].Remove(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void GetEntityFromComponent(Component c, List<Entity> e)
|
||||||
|
{
|
||||||
|
_ComponentsPacked[c].[Unchecked]GetEntities(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ExecuteSystem<A>(delegate void(ECS, Entity, ref A) func) where A : struct
|
||||||
|
{
|
||||||
|
var idx = GetComponent<A>();
|
||||||
|
var data = _ComponentsPacked[idx].GetAll<A>();
|
||||||
|
for(var i in ref data)
|
||||||
|
func.Invoke(this, i.0, ref i.1);
|
||||||
|
}
|
||||||
|
}
|
13
src/Example/MoverSystem.bf
Normal file
13
src/Example/MoverSystem.bf
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
namespace Theater_ECS.Example;
|
||||||
|
|
||||||
|
|
||||||
|
class MoverSystem : System
|
||||||
|
{
|
||||||
|
|
||||||
|
[SystemMethod("MoveItems", typeof(Velocity), typeof(Position))]
|
||||||
|
public void MoveItems(ref Velocity v,ref Position p)
|
||||||
|
{
|
||||||
|
//p.x = v.x + p.x;
|
||||||
|
//p.y = v.y = p.y;
|
||||||
|
}
|
||||||
|
}
|
9
src/Example/Position.bf
Normal file
9
src/Example/Position.bf
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
namespace Theater_ECS.Example;
|
||||||
|
|
||||||
|
using System;
|
||||||
|
|
||||||
|
struct Position
|
||||||
|
{
|
||||||
|
public float x = (.)gRand.NextI32() % 25;
|
||||||
|
public float y = (.)gRand.NextI32() % 25;
|
||||||
|
}
|
62
src/Example/Program.bf
Normal file
62
src/Example/Program.bf
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
namespace Theater_ECS;
|
||||||
|
using Theater_ECS.Example;
|
||||||
|
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
|
||||||
|
class Program
|
||||||
|
{
|
||||||
|
public static uint64 counter = 0;
|
||||||
|
|
||||||
|
public static void Main()
|
||||||
|
{
|
||||||
|
ECS ecs = scope .();
|
||||||
|
var pos = ecs.RegisterComponent<Position>(100);
|
||||||
|
//var waste = ecs.RegisterComponent<waste>(100);
|
||||||
|
var vel = ecs.RegisterComponent<Velocity>(100);
|
||||||
|
|
||||||
|
|
||||||
|
List<Entity> entities = scope .();
|
||||||
|
for(int i < 500000)
|
||||||
|
entities.Add(ecs.CreateEntity());
|
||||||
|
|
||||||
|
for(var i in entities)
|
||||||
|
*(Position*)ecs.AddComponentToEntity(i, pos).Value = .();
|
||||||
|
|
||||||
|
for(var i in entities)
|
||||||
|
*(Velocity*)ecs.AddComponentToEntity(i, vel).Value = .();
|
||||||
|
|
||||||
|
/*
|
||||||
|
for(var i in entities)
|
||||||
|
if(gRand.NextI32() % 2 == 1)
|
||||||
|
*(waste*)ecs.AddComponentToEntity(i, waste).Value = .();
|
||||||
|
*/
|
||||||
|
|
||||||
|
MoverSystem s = scope .();
|
||||||
|
s.IntializeSystem(ecs);
|
||||||
|
|
||||||
|
System.Diagnostics.Stopwatch watch = scope .();
|
||||||
|
for(int a < 10)
|
||||||
|
{
|
||||||
|
watch.Start();
|
||||||
|
for(int i < 10)
|
||||||
|
{
|
||||||
|
|
||||||
|
s.RunSystem(ecs);
|
||||||
|
|
||||||
|
}
|
||||||
|
Console.WriteLine(scope $"{watch.ElapsedMilliseconds}ms");
|
||||||
|
watch.Stop();
|
||||||
|
watch.Reset();
|
||||||
|
}
|
||||||
|
//Console.Read();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct waste
|
||||||
|
{
|
||||||
|
private float[10] a;
|
||||||
|
}
|
9
src/Example/Velocity.bf
Normal file
9
src/Example/Velocity.bf
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
namespace Theater_ECS.Example;
|
||||||
|
|
||||||
|
using System;
|
||||||
|
|
||||||
|
struct Velocity
|
||||||
|
{
|
||||||
|
public float x = (.)gRand.NextI32() % 25;
|
||||||
|
public float y = (.)gRand.NextI32() % 25;
|
||||||
|
}
|
15
src/System.bf
Normal file
15
src/System.bf
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
namespace Theater_ECS;
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
|
||||||
|
abstract class System
|
||||||
|
{
|
||||||
|
protected Dictionary<Type, Component> _ComponentMap = new .() ~ delete _;
|
||||||
|
protected List<Entity> _Target = new .(100) ~ delete _;
|
||||||
|
|
||||||
|
public abstract void RunSystem(ECS ecs);
|
||||||
|
|
||||||
|
public abstract bool IntializeSystem(ECS ecs);
|
||||||
|
|
||||||
|
}
|
117
src/SystemMethodAttribute.bf
Normal file
117
src/SystemMethodAttribute.bf
Normal file
|
@ -0,0 +1,117 @@
|
||||||
|
namespace Theater_ECS;
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Collections;
|
||||||
|
|
||||||
|
[AttributeUsage(.Method)]
|
||||||
|
struct SystemMethodAttribute : Attribute, IOnTypeInit
|
||||||
|
{
|
||||||
|
private StringView _Name;
|
||||||
|
private Span<Type> _Types;
|
||||||
|
|
||||||
|
public this(StringView name, params Span<Type> types)
|
||||||
|
{
|
||||||
|
_Name = name;
|
||||||
|
_Types = types;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Comptime]
|
||||||
|
public void OnTypeInit(Type type, Self* prev)
|
||||||
|
{
|
||||||
|
//InitializeFunction
|
||||||
|
Compiler.EmitTypeBody(type, scope $"""
|
||||||
|
public override bool IntializeSystem(ECS ecs)
|
||||||
|
\{
|
||||||
|
{InitializeSystemTypes( .. scope .())}
|
||||||
|
return true;
|
||||||
|
\}
|
||||||
|
|
||||||
|
""");
|
||||||
|
|
||||||
|
|
||||||
|
Compiler.EmitTypeBody(type, scope $"""
|
||||||
|
[System.DisableChecks]
|
||||||
|
public override void RunSystem(ECS ecs)
|
||||||
|
\{
|
||||||
|
Lock(ecs);
|
||||||
|
|
||||||
|
Component lowest = System.int_cosize.MaxValue;
|
||||||
|
System.int_cosize lowestAmount = System.int_cosize.MaxValue;
|
||||||
|
for(var c in _ComponentMap)
|
||||||
|
\{
|
||||||
|
if(ecs.[System.Friend]_ComponentsPacked[c.value].Count < lowestAmount)
|
||||||
|
\{
|
||||||
|
lowest = c.value;
|
||||||
|
lowestAmount = ecs.[System.Friend]_ComponentsPacked[c.value].Count;
|
||||||
|
\}
|
||||||
|
\}
|
||||||
|
|
||||||
|
|
||||||
|
ecs.GetEntityFromComponent(lowest, _Target);
|
||||||
|
|
||||||
|
{CacheComponentData(.. scope .())}
|
||||||
|
|
||||||
|
|
||||||
|
for(var e in _Target)
|
||||||
|
\{
|
||||||
|
|
||||||
|
{_Name}(
|
||||||
|
{GetComponentData(.. scope .())}
|
||||||
|
);
|
||||||
|
\}
|
||||||
|
|
||||||
|
_Target.Clear();
|
||||||
|
|
||||||
|
Unlock(ecs);
|
||||||
|
|
||||||
|
\}
|
||||||
|
|
||||||
|
private void Lock(ECS ecs)
|
||||||
|
\{
|
||||||
|
for(var i in _ComponentMap)
|
||||||
|
ecs.[System.Friend]_ComponentsPacked[i.value].Use();
|
||||||
|
\}
|
||||||
|
|
||||||
|
private void Unlock(ECS ecs)
|
||||||
|
\{
|
||||||
|
for(var i in _ComponentMap)
|
||||||
|
ecs.[System.Friend]_ComponentsPacked[i.value].StopUsing();
|
||||||
|
\}
|
||||||
|
""");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InitializeSystemTypes(String buffer)
|
||||||
|
{
|
||||||
|
for(var i in _Types)
|
||||||
|
{
|
||||||
|
buffer.Append(scope $"""
|
||||||
|
if(ecs.GetComponent<{i.GetName( .. scope .())}>() == -1)
|
||||||
|
return false;
|
||||||
|
_ComponentMap.Add(typeof({i.GetName(.. scope .())}), ecs.GetComponent<{i.GetName( .. scope .())}>());
|
||||||
|
|
||||||
|
|
||||||
|
""");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void GetComponentData(String buffer)
|
||||||
|
{
|
||||||
|
for(var i in _Types)
|
||||||
|
{
|
||||||
|
buffer.Append(scope $"""
|
||||||
|
ref *ecs.[System.Friend]_ComponentsPacked[_{i.GetName(.. scope .())}].Get<{i.GetName(.. scope .())}>(ecs.[System.Friend]_EntityList[_{i.GetName(.. scope .())}][e])
|
||||||
|
""");
|
||||||
|
if(@i.Index+1 < _Types.Length)
|
||||||
|
buffer.Append(",\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CacheComponentData(String buffer)
|
||||||
|
{
|
||||||
|
for(var i in _Types)
|
||||||
|
{
|
||||||
|
buffer.Append(scope $"Component _{i.GetName(.. scope .())} = _ComponentMap[typeof({i.GetName(.. scope .())})];\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue