Initial Commit

This commit is contained in:
Booklordofthedings 2024-11-11 18:00:37 +01:00
parent fe7eba4573
commit e49dca0403
8 changed files with 436 additions and 0 deletions

109
src/ComponentManager.bf Normal file
View 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
View 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);
}
}

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

View 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");
}
}
}