diff --git a/encompass-cs/Engine.cs b/encompass-cs/Engine.cs index 6428abc..2557b9f 100644 --- a/encompass-cs/Engine.cs +++ b/encompass-cs/Engine.cs @@ -213,7 +213,7 @@ namespace Encompass /// /// Returns all Entities containing the specified Component type. /// - protected Span ReadEntities() where TComponent : struct + protected Span ReadEntities() where TComponent : struct, IComponent { var immediateRead = ReadImmediateTypes.Contains(typeof(TComponent)); var existingRead = ReadTypes.Contains(typeof(TComponent)); @@ -238,7 +238,7 @@ namespace Encompass /// /// Returns an Entity containing the specified Component type. /// - protected ref readonly Entity ReadEntity() where TComponent : struct + protected ref readonly Entity ReadEntity() where TComponent : struct, IComponent { var immediateRead = ReadImmediateTypes.Contains(typeof(TComponent)); var existingRead = ReadTypes.Contains(typeof(TComponent)); @@ -263,7 +263,7 @@ namespace Encompass /// /// Returns all of the Components with the specified Component Type. /// - protected Span ReadComponents() where TComponent : struct + protected Span ReadComponents() where TComponent : struct, IComponent { var immediateRead = ReadImmediateTypes.Contains(typeof(TComponent)); var existingRead = ReadTypes.Contains(typeof(TComponent)); @@ -288,7 +288,7 @@ namespace Encompass /// /// Returns a Component with the specified Component Type. If multiples exist, an arbitrary Component is returned. /// - protected ref readonly TComponent ReadComponent() where TComponent : struct + protected ref readonly TComponent ReadComponent() where TComponent : struct, IComponent { var immediateRead = ReadImmediateTypes.Contains(typeof(TComponent)); var existingRead = ReadTypes.Contains(typeof(TComponent)); @@ -313,7 +313,7 @@ namespace Encompass /// /// Returns true if any Component with the specified Component Type exists. /// - protected bool SomeComponent() where TComponent : struct + protected bool SomeComponent() where TComponent : struct, IComponent { var immediateRead = ReadImmediateTypes.Contains(typeof(TComponent)); var existingRead = ReadTypes.Contains(typeof(TComponent)); @@ -335,7 +335,7 @@ namespace Encompass } } - private ref TComponent GetComponentHelper(int entityID) where TComponent : struct + private ref TComponent GetComponentHelper(int entityID) where TComponent : struct, IComponent { var immediateRead = ReadImmediateTypes.Contains(typeof(TComponent)); var existingRead = ReadTypes.Contains(typeof(TComponent)); @@ -366,7 +366,7 @@ namespace Encompass /// /// Thrown when the Engine does not declare that it reads the given Component Type. /// - protected ref readonly TComponent GetComponent(in Entity entity) where TComponent : struct + protected ref readonly TComponent GetComponent(in Entity entity) where TComponent : struct, IComponent { return ref GetComponentHelper(entity.ID); } @@ -380,7 +380,7 @@ namespace Encompass /// /// Thrown when the Engine does not declare that it reads the given Component Type. /// - protected ref TComponent GetComponentMutable(in Entity entity) where TComponent : struct + protected ref TComponent GetComponentMutable(in Entity entity) where TComponent : struct, IComponent { return ref GetComponentHelper(entity.ID); } @@ -391,7 +391,7 @@ namespace Encompass /// /// Thrown when the Engine does not declare that is Reads the given Component Type. /// - protected bool HasComponent(in Entity entity) where TComponent : struct + protected bool HasComponent(in Entity entity) where TComponent : struct, IComponent { var immediateRead = ReadImmediateTypes.Contains(typeof(TComponent)); var existingRead = ReadTypes.Contains(typeof(TComponent)); @@ -449,7 +449,7 @@ namespace Encompass /// /// Thrown when the Engine does not declare that it Writes the given Component Type. /// - protected void SetComponent(in Entity entity, in TComponent component) where TComponent : struct + protected void SetComponent(in Entity entity, in TComponent component) where TComponent : struct, IComponent { var priority = WritePriorities.ContainsKey(typeof(TComponent)) ? WritePriorities[typeof(TComponent)] : DefaultWritePriority; @@ -489,7 +489,7 @@ namespace Encompass /// /// Thrown when the Engine does not declare that it Writes the given Component Type. /// - protected void AddComponent(in Entity entity, in TComponent component) where TComponent : struct + protected void AddComponent(in Entity entity, in TComponent component) where TComponent : struct, IComponent { if (!EntityCreatedThisFrame(entity.ID)) { @@ -597,7 +597,7 @@ namespace Encompass /// Destroys an arbitrary Entity containing a Component of the specified Type. /// Entity destruction takes place after all the Engines have been processed by World Update. /// - protected void DestroyWith() where TComponent : struct + protected void DestroyWith() where TComponent : struct, IComponent { Destroy(ReadEntity()); } @@ -606,7 +606,7 @@ namespace Encompass /// Destroys all Entities containing a Component of the specified Type. /// Entity destruction takes place after all the Engines have been processed by World Update. /// - protected void DestroyAllWith() where TComponent : struct + protected void DestroyAllWith() where TComponent : struct, IComponent { foreach (var entity in ReadEntities()) { @@ -619,7 +619,7 @@ namespace Encompass /// Note that the Engine must Read the Component type that is being removed. /// If a Component with the specified type does not exist on the Entity, returns false and does not mutate the Entity. /// - protected void RemoveComponent(in Entity entity) where TComponent : struct + protected void RemoveComponent(in Entity entity) where TComponent : struct, IComponent { var priority = WritePriorities.ContainsKey(typeof(TComponent)) ? WritePriorities[typeof(TComponent)] : DefaultWritePriority; diff --git a/encompass-cs/Interfaces/IComponent.cs b/encompass-cs/Interfaces/IComponent.cs new file mode 100644 index 0000000..d51b93c --- /dev/null +++ b/encompass-cs/Interfaces/IComponent.cs @@ -0,0 +1,4 @@ +namespace Encompass +{ + public interface IComponent { } +} diff --git a/encompass-cs/Renderer.cs b/encompass-cs/Renderer.cs index ccc8e59..85b5489 100644 --- a/encompass-cs/Renderer.cs +++ b/encompass-cs/Renderer.cs @@ -18,37 +18,37 @@ namespace Encompass _componentManager = componentManager; } - protected Span ReadEntities() where TComponent : struct + protected Span ReadEntities() where TComponent : struct, IComponent { return _componentManager.GetExistingEntities(); } - protected ref readonly Entity ReadEntity() where TComponent : struct + protected ref readonly Entity ReadEntity() where TComponent : struct, IComponent { return ref _componentManager.ExistingSingularEntity(); } - protected Span ReadComponents() where TComponent : struct + protected Span ReadComponents() where TComponent : struct, IComponent { return _componentManager.GetComponentsByType(); } - protected ref readonly TComponent ReadComponent() where TComponent : struct + protected ref readonly TComponent ReadComponent() where TComponent : struct, IComponent { return ref _componentManager.ExistingSingular(); } - protected ref readonly TComponent GetComponent(Entity entity) where TComponent : struct + protected ref readonly TComponent GetComponent(Entity entity) where TComponent : struct, IComponent { return ref _componentManager.GetComponentByEntityAndType(entity.ID); } - protected bool HasComponent(Entity entity) where TComponent : struct + protected bool HasComponent(Entity entity) where TComponent : struct, IComponent { return _componentManager.EntityHasComponentOfType(entity.ID); } - protected bool SomeComponent() where TComponent : struct + protected bool SomeComponent() where TComponent : struct, IComponent { return _componentManager.SomeExistingComponent(); } diff --git a/encompass-cs/Renderers/OrderedRenderer.cs b/encompass-cs/Renderers/OrderedRenderer.cs index 56935f2..5b462b3 100644 --- a/encompass-cs/Renderers/OrderedRenderer.cs +++ b/encompass-cs/Renderers/OrderedRenderer.cs @@ -5,7 +5,7 @@ namespace Encompass /// /// OrdereredRenderer provides a structure for the common pattern of wishing to draw a specific DrawComponent at a specific layer. /// - public abstract class OrderedRenderer : Renderer where TComponent : struct, IDrawableComponent + public abstract class OrderedRenderer : Renderer where TComponent : struct, IComponent, IDrawableComponent { public abstract void Render(Entity entity, in TComponent drawComponent); diff --git a/encompass-cs/UberEngine.cs b/encompass-cs/UberEngine.cs index d0957bf..5d5378e 100644 --- a/encompass-cs/UberEngine.cs +++ b/encompass-cs/UberEngine.cs @@ -35,15 +35,14 @@ namespace Encompass } } - // we can't reflect invoke on Span returns right now... tragic public override void Update(double dt) { foreach (var type in _componentTypes) { CallGenericMethod(type, "ReadComponent", null); - //CallGenericMethod(type, "ReadComponents", null); + CallGenericWrappedMethod(type, "ReadComponentsWrapper", null); CallGenericMethod(type, "ReadEntity", null); - //CallGenericMethod(type, "ReadEntities", null); + CallGenericWrappedMethod(type, "ReadEntitiesWrapper", null); CallGenericMethod(type, "GetComponent", new object[] { Entity }); CallGenericMethod(type, "HasComponent", 1, new object[] { Entity }); CallGenericMethod(type, "SomeComponent", null); @@ -59,7 +58,7 @@ namespace Encompass CallGenericMethod(type, "SendMessage", 2, new object[] { Activator.CreateInstance(type), 1 }); CallGenericMethod(type, "ReadMessage", null); - //CallGenericMethod(type, "ReadMessages", null); + CallGenericWrappedMethod(type, "ReadMessagesWrapper", null); CallGenericMethod(type, "SomeMessage", null); if (typeof(IHasEntity).IsAssignableFrom(type)) { @@ -70,6 +69,23 @@ namespace Encompass } } + // we can't reflect invoke on Span returns right now... so we have non-return wrapper methods + + protected void ReadComponentsWrapper() where TComponent : struct, IComponent + { + ReadComponents(); + } + + protected void ReadMessagesWrapper() where TMessage : struct, IMessage + { + ReadMessages(); + } + + protected void ReadEntitiesWrapper() where TComponent : struct, IComponent + { + ReadEntities(); + } + // trying to use PrepareMethod because we can't reflect invoke methods that return a span... private void CallGenericMethod(Type type, string methodName, object[] parameters) { @@ -79,6 +95,13 @@ namespace Encompass // RuntimeHelpers.PrepareMethod(genericReadComponentMethod.MethodHandle); } + private void CallGenericWrappedMethod(Type type, string methodName, object[] parameters) + { + var readComponentMethod = typeof(UberEngine).GetMethod(methodName, BindingFlags.NonPublic | BindingFlags.Instance); + var genericReadComponentMethod = readComponentMethod.MakeGenericMethod(type); + genericReadComponentMethod.Invoke(this, parameters); + } + private void CallGenericMethod(Type type, string methodName, Type[] types, object[] parameters) { var readComponentMethod = typeof(Engine).GetMethod(methodName, BindingFlags.NonPublic | BindingFlags.Instance, null, types, null); diff --git a/encompass-cs/WorldBuilder.cs b/encompass-cs/WorldBuilder.cs index 5da3d3a..9295844 100644 --- a/encompass-cs/WorldBuilder.cs +++ b/encompass-cs/WorldBuilder.cs @@ -183,7 +183,7 @@ namespace Encompass /// /// Adds the specified OrderedRenderer to the World. /// - public OrderedRenderer AddOrderedRenderer(OrderedRenderer renderer) where TComponent : struct, IDrawableComponent + public OrderedRenderer AddOrderedRenderer(OrderedRenderer renderer) where TComponent : struct, IComponent, IDrawableComponent { RegisterComponentType(); renderer.AssignEntityManager(_entityManager); @@ -354,7 +354,7 @@ namespace Encompass throw new EngineWriteConflictException(errorString); } - PreloadJIT(_componentTypesToPreload, _messageTypes); + PreloadJIT(_messageTypes); var engineOrder = new List(); @@ -388,7 +388,7 @@ namespace Encompass /// It does so by grabbing all component and message types known to the WorldBuilder and /// executing every possible generic method that could be executed with those types. /// - private void PreloadJIT(IEnumerable componentTypes, IEnumerable messageTypes) + private void PreloadJIT(IEnumerable messageTypes) { var dummyTimeManager = new TimeManager(); var dummyMessageManager = new MessageManager(dummyTimeManager); @@ -398,9 +398,30 @@ namespace Encompass var dummyEntityManager = new EntityManager(dummyComponentManager, _entityCapacity); var dummyRenderManager = new RenderManager(dummyEntityManager, dummyDrawLayerManager); + // doing reflection to grab all component types, because not all writes need to be declared + foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies()) + { + foreach (var componentType in assembly.GetTypes()) + { + if (typeof(IComponent).IsAssignableFrom(componentType) && componentType.IsValueType && !componentType.IsEnum && !componentType.IsPrimitive) + { + var method = typeof(WorldBuilder).GetMethod("RegisterComponentType", BindingFlags.NonPublic | BindingFlags.Instance); + var generic = method.MakeGenericMethod(componentType); + generic.Invoke(this, null); + } + + if (componentType.GetInterface("IDrawableComponent") != null) + { + var drawLayerManagerRegisterMethod = typeof(DrawLayerManager).GetMethod("RegisterOrderedDrawable"); + var drawLayerManagerRegisterGenericMethod = drawLayerManagerRegisterMethod.MakeGenericMethod(componentType); + drawLayerManagerRegisterGenericMethod.Invoke(dummyDrawLayerManager, null); + } + } + } + var prepEngineOrder = new List(); - var uberEngine = new UberEngine(componentTypes, messageTypes); + var uberEngine = new UberEngine(_componentTypesToPreload, messageTypes); uberEngine.AssignEntityManager(dummyEntityManager); uberEngine.AssignComponentManager(dummyComponentManager); @@ -408,24 +429,10 @@ namespace Encompass uberEngine.AssignTimeManager(dummyTimeManager); uberEngine.AssignTrackingManager(dummyTrackingManager); - var uberRenderer = new UberRenderer(componentTypes); + var uberRenderer = new UberRenderer(_componentTypesToPreload); uberRenderer.AssignComponentManager(dummyComponentManager); uberRenderer.AssignEntityManager(dummyEntityManager); - foreach (var type in componentTypes) - { - var componentManagerRegisterMethod = typeof(ComponentManager).GetMethod("RegisterComponentType"); - var componentManagerRegisterGenericMethod = componentManagerRegisterMethod.MakeGenericMethod(type); - componentManagerRegisterGenericMethod.Invoke(dummyComponentManager, null); - - if (type.GetInterface("IDrawableComponent") != null) - { - var drawLayerManagerRegisterMethod = typeof(DrawLayerManager).GetMethod("RegisterOrderedDrawable"); - var drawLayerManagerRegisterGenericMethod = drawLayerManagerRegisterMethod.MakeGenericMethod(type); - drawLayerManagerRegisterGenericMethod.Invoke(dummyDrawLayerManager, null); - } - } - prepEngineOrder.Add(uberEngine); var dummyWorld = new World( diff --git a/test/ComponentTest.cs b/test/ComponentTest.cs index 62050f6..9c6e2e7 100644 --- a/test/ComponentTest.cs +++ b/test/ComponentTest.cs @@ -8,7 +8,7 @@ namespace Tests { public class ComponentTests { - struct MockComponent + struct MockComponent : IComponent { public int myInt; } diff --git a/test/EngineTest.cs b/test/EngineTest.cs index ebc5f29..c5621fe 100644 --- a/test/EngineTest.cs +++ b/test/EngineTest.cs @@ -10,7 +10,7 @@ using Encompass.Exceptions; namespace Tests { - struct MockComponent + struct MockComponent : IComponent { public int myInt; } @@ -618,7 +618,7 @@ namespace Tests world.Update(0.01f); } - struct DestroyerComponent { } + struct DestroyerComponent : IComponent { } [Reads(typeof(DestroyerComponent))] class DestroyerEngine : Engine @@ -964,7 +964,7 @@ namespace Tests entity.Should().BeEquivalentTo(readEntity); } - struct MockComponentB + struct MockComponentB : IComponent { private int value; @@ -1407,9 +1407,9 @@ namespace Tests public class QueryTests { - struct MockComponentB { } - struct MockComponentC { } - struct MockComponentD { } + struct MockComponentB : IComponent { } + struct MockComponentC : IComponent { } + struct MockComponentD : IComponent { } [Reads(typeof(MockComponent), typeof(MockComponentB))] [Writes(typeof(MockComponentB))] @@ -1863,7 +1863,7 @@ namespace Tests _components.Should().NotBeEmpty(); } - struct MockTimerComponent + struct MockTimerComponent : IComponent { public double Timer { get; } diff --git a/test/GeneralRendererTest.cs b/test/GeneralRendererTest.cs index 7ccc3ea..32659c0 100644 --- a/test/GeneralRendererTest.cs +++ b/test/GeneralRendererTest.cs @@ -5,7 +5,7 @@ namespace Tests { public static class GeneralRendererTest { - struct AComponent { } + struct AComponent : IComponent { } public class SingletonRead { diff --git a/test/OrderedRendererTest.cs b/test/OrderedRendererTest.cs index 2ee2af7..92cb3b8 100644 --- a/test/OrderedRendererTest.cs +++ b/test/OrderedRendererTest.cs @@ -9,11 +9,11 @@ namespace Tests { public class OrderedRendererTest { - struct AComponent { } - struct BComponent { } - struct CComponent { } + struct AComponent : IComponent { } + struct BComponent : IComponent { } + struct CComponent : IComponent { } - struct TestDrawComponent : IDrawableComponent + struct TestDrawComponent : IComponent, IDrawableComponent { public int Layer { get; set; } } diff --git a/test/SpawnerTest.cs b/test/SpawnerTest.cs index e31ed37..c0c5f0c 100644 --- a/test/SpawnerTest.cs +++ b/test/SpawnerTest.cs @@ -5,7 +5,7 @@ namespace Tests { public class SpawnerTest { - struct TestComponent { } + struct TestComponent : IComponent { } struct SpawnMessageA : IMessage { } static Entity resultEntity; diff --git a/test/WorldBuilderTest.cs b/test/WorldBuilderTest.cs index 871dce4..2f3e7d2 100644 --- a/test/WorldBuilderTest.cs +++ b/test/WorldBuilderTest.cs @@ -147,7 +147,7 @@ namespace Tests public Entity entity; } - struct AComponent + struct AComponent : IComponent { public int myInt; } @@ -216,7 +216,7 @@ namespace Tests public Entity entity; } - struct AComponent + struct AComponent : IComponent { public int myInt; } @@ -407,8 +407,8 @@ namespace Tests { static List order = new List(); - struct AComponent { } - struct BComponent { } + struct AComponent : IComponent { } + struct BComponent : IComponent { } struct AMessage : IMessage { } struct BMessage : IMessage { } diff --git a/test/WorldTest.cs b/test/WorldTest.cs index f985dc1..6ce089b 100644 --- a/test/WorldTest.cs +++ b/test/WorldTest.cs @@ -11,8 +11,8 @@ namespace Tests { public class WorldTest { - struct TestComponent { } - struct TestDrawComponent : IDrawableComponent + struct TestComponent : IComponent { } + struct TestDrawComponent : IComponent, IDrawableComponent { public int Layer { get; set; } }