From e8efb77a3636b80665073ac297d5245145159780 Mon Sep 17 00:00:00 2001 From: PowerfulBacon <26465327+PowerfulBacon@users.noreply.github.com> Date: Wed, 13 Sep 2023 19:15:10 +0100 Subject: [PATCH] ECS & Networking Refactoring (#170) * Implements CVar * Fixes a few things that didnt work * Adds NetVar * NetVar framework * Some messy netvars * Adds the ability to queue arbitrary actions to run on a system thread This allows arbitrary actions to be called on a system without the requirement of a signal. This means that we can set the value of a variable on the thread which has ownership of that value. Note that these will not be networked and cannot be networked unlike signals. Hopefully this can be abstracted away when we introduce proper CVars and thread ownership management. * Contents no longer uses this module which doesn't exist * Cvars * Implements world to act as a container for static instances * Implements entity system setup * Implements processing systems * Implements world specific events, down to 8 failing tests * Fixes a failing test case * Optimises signal call from 30000 => 7000000 (Still only 1% of just calling methods though) * Fixes network initialisation order issues * Fixes processing systems constantly firing if control was relinquished * Fixes entity systems becoming deadlocked * Processing systems * Fixes entity system processing * Disables processing system debug * Adds in a rendering scaling mode, fixes button test * Update ExampleRenderCore.cs * Update NetCVar.cs * Fixes CVAR network communications. Deletes automap * Render Core Depth Output * Adds the ability to fetch the depth texture and render texture from render cores * Implements proper depth drawing across render core layers * Removes the depth program * Fixes the tests not compiling * Update RenderCore.cs * Update NetworkingTest.cs --- AutoMap/AutoMap.cs | 10 +- AutoMap/Rendering/AutoMapRenderCore.cs | 5 + CorgEng.AiBehaviour/BehaviourManager.cs | 9 +- CorgEng.AiBehaviour/BehaviourNode.cs | 5 +- CorgEng.AiBehaviour/BehaviourRoot.cs | 4 + .../Factories/BehaviourManagerFactory.cs | 4 +- .../Systems/AiActionProcessingSystem.cs | 2 +- .../Systems/BehaviourProcessingSystem.cs | 2 +- CorgEng.Audio/Systems/AudioListenerSystem.cs | 4 +- CorgEng.Claims/Systems/ClaimSystem.cs | 2 +- .../DefinitionNodes/ArrayNode.cs | 5 +- .../DefinitionNodes/ComponentNode.cs | 4 +- .../DefinitionNodes/DefinitionNode.cs | 8 +- .../DefinitionNodes/DependencyNode.cs | 5 +- .../DefinitionNodes/DictionaryNode.cs | 5 +- .../DefinitionNodes/ElementNode.cs | 13 +- .../DefinitionNodes/EntityNode.cs | 17 +- .../DefinitionNodes/InstanceNode.cs | 5 +- .../DefinitionNodes/KeyNode.cs | 1 + .../DefinitionNodes/ObjectNode.cs | 7 +- .../DefinitionNodes/ParameterNode.cs | 5 +- .../DefinitionNodes/PropertyNode.cs | 5 +- .../DefinitionNodes/ValueNode.cs | 1 + CorgEng.ContentLoading/EntityCreator.cs | 8 +- CorgEng.Contents/Systems/ContentsSystem.cs | 4 +- .../Shaders/CoreShader/CoreShader.frag | 10 +- .../Shaders/DepthShader/DepthShader.frag | 13 + .../Shaders/DepthShader/DepthShader.vert | 7 + CorgEng.Core/CorgEng.Core.csproj | 6 + CorgEng.Core/CorgEng.Core.sln | 44 +- CorgEng.Core/CorgEngMain.cs | 163 +++++- .../Modules/WorldInitialiseAttribute.cs | 15 + CorgEng.Core/Rendering/RenderCore.cs | 97 +++- CorgEng.Core/Rendering/ScalingModes.cs | 14 + CorgEng.Debug/CorgEng.Debug.csproj | 13 + .../RenderCores/DebugOverlayRenderCore.cs | 28 + .../Dependencies/NonDependencyAttribute.cs | 24 + .../Components/Component.cs | 77 ++- ...tensions.cs => ComponentSignalInjector.cs} | 61 +- .../Components/ComponentVariables/CVar.cs | 42 ++ .../ComponentVariables/IComponentVariable.cs | 21 + .../ComponentVariables/Networking/INetVar.cs | 17 + .../ComponentVariables/Networking/NetCVar.cs | 132 +++++ .../CorgEng.EntityComponentSystem.csproj | 1 - .../Entities/Entity.cs | 58 +- .../Entities/EntityFactory.cs | 34 -- .../Entities/EntityManager.cs | 52 +- .../Events/EventExtensions.cs | 36 +- .../Events/EventManager.cs | 15 - .../Deletion/EntityDeletionSystem.cs | 4 +- .../EntityInitialisationSystem.cs | 2 +- .../Implementations/Transform/MoveEvent.cs | 1 + .../Transform/RotationEvent.cs | 2 +- .../Transform/TransformComponent.cs | 7 +- .../Transform/TransformSystem.cs | 18 +- .../Systems/EntitySystem.cs | 528 ++++++++++-------- .../Systems/EntitySystemFlags.cs | 2 + .../Systems/EntitySystemManager.cs | 196 +++++++ .../Systems/EntitySystemThreadManager.cs | 201 +++++++ .../Systems/ProcessingSystem.cs | 125 +++-- .../WorldManager/World.cs | 109 ++++ .../WorldManager/WorldFactory.cs | 21 + CorgEng.EntityQuery/EntityQuery.cs | 21 +- CorgEng.EntityQuery/EntityQuerySystem.cs | 3 +- .../CorgEng.Example.Server.csproj | 1 - CorgEng.Example.Server/Program.cs | 41 +- .../Common/Components/Camera/CameraSystem.cs | 2 +- .../FollowCursorComponent.cs | 8 +- .../Components/Gravity/SandSystem.cs | 6 +- .../PlayerMovement/PlayerMovementSystem.cs | 2 +- .../SandFactory/SandFactorySystem.cs | 15 +- .../CorgEng.Example.Shared.csproj | 1 + .../RenderCores/ExampleRenderCore.cs | 23 +- .../CameraScroll/CameraScrollSystem.cs | 3 +- CorgEng.Example/Program.cs | 13 +- CorgEng.Functional/CorgEng.Functional.csproj | 9 + CorgEng.Functional/Monads/Result.cs | 47 ++ .../AiBehaviours/IBehaviourManagerFactory.cs | 2 +- .../ContentLoading/IEntityCreator.cs | 4 +- .../ContentLoading/IEntityDefinition.cs | 2 +- CorgEng.GenericInterfaces/Core/IRenderCore.cs | 6 +- .../NonDependencyAttribute.cs | 32 ++ .../EventComponentPair.cs | 8 +- .../IComponentSignalInjector.cs | 17 + .../EntityComponentSystem/IEntity.cs | 17 + .../EntityComponentSystem/IEntityFactory.cs | 25 - .../EntityComponentSystem/IEntityManager.cs | 34 ++ .../IEntitySystemManager.cs | 98 ++++ .../EntityComponentSystem/IWorld.cs | 27 + .../EntityComponentSystem/IWorldFactory.cs | 15 + .../EntityComponentSystem/WorldObject.cs | 20 + .../IContentsHolder.cs | 0 .../IEntityPositionTracker.cs} | 2 +- .../IWorldTrackComponent.cs | 0 ...INetworkingClient.cs => INetworkClient.cs} | 2 +- .../Client/INetworkClientFactory.cs | 16 + .../Server/IEntityCommunicatorFactory.cs | 16 + ...INetworkingServer.cs => INetworkServer.cs} | 2 +- .../Server/INetworkServerFactory.cs | 16 + .../Networking/Packets/PacketHeaders.cs | 2 + .../Networking/PrototypeManager/IPrototype.cs | 4 +- .../Components/IUserInterfaceComponent.cs | 5 + .../IUserInterfaceComponentFactory.cs | 11 +- .../Generators/IUserInterfaceXmlLoader.cs | 5 +- .../UserInterface/Popups/IPopupManager.cs | 4 +- .../UtilityTypes/IBindableProperty.cs | 5 +- .../UtilityTypes/IListenable.cs | 16 + .../UtilityTypes/IVector.cs | 4 +- .../CorgEng.IconSmoothing.csproj | 1 + .../Systems/SmoothIconSystem.cs | 8 +- .../ClickHandler/SelectableSystem.cs | 7 +- .../CorgEng.InputHandling.csproj | 1 + CorgEng.InputHandling/InputHandler.cs | 77 ++- .../Components/LightingComponent.cs | 1 - CorgEng.Lighting/CorgEng.Lighting.csproj | 1 + .../RenderCores/LightingRenderCore.cs | 27 +- CorgEng.Lighting/Systems/LightingSystem.cs | 6 +- .../Components/ComponentExtensions.cs | 3 +- .../Components/NetworkTransformComponent.cs | 45 +- .../EntitySystems/ClientSystem.cs | 10 +- .../EntitySystems/DelayedEventSystem.cs | 2 +- .../EntitySystems/NetworkSystem.cs | 13 +- .../Networking/Client/ClientCommunicator.cs | 14 +- .../{NetworkingClient.cs => NetworkClient.cs} | 23 +- .../Networking/Client/NetworkClientFactory.cs | 23 + .../{Server => }/EntityCommunicator.cs | 14 +- .../Networking/EntityCommunicatorFactory.cs | 20 + .../Networking/NetworkCommunicator.cs | 30 +- .../{NetworkingServer.cs => NetworkServer.cs} | 43 +- .../Networking/Server/NetworkServerFactory.cs | 22 + .../Networking/Server/ServerCommunicator.cs | 2 +- CorgEng.Networking/Prototypes/Prototype.cs | 12 +- .../Serialization/AutoSerialiser.cs | 33 +- .../CorgEng.NetworkingPrimitives.csproj | 9 + .../Networking/INetVar.cs | 17 + .../Networking/NetCVar.cs | 65 +++ .../Queryers/WorldPathCellQueryer.cs | 2 +- CorgEng.Pathfinding/Systems/SolidSystem.cs | 8 +- .../DepthSpriteRenderComponent.cs | 34 ++ .../SpriteRendering/AddOverlayEvent.cs | 0 .../SpriteRendering/RemoveOverlayEvent.cs | 0 .../SpriteRendering/SetDirectionEvent.cs | 0 .../SpriteRendering/SetSpriteEvent.cs | 0 .../SpriteRendering/SetSpriteRendererEvent.cs | 0 .../SpriteRendering/SpriteRenderComponent.cs | 6 +- .../SpriteRendering/SpriteRenderSystem.cs | 6 +- .../Shaders/SpriteShader/SpriteShader.frag | 1 + CorgEng.Rendering/CorgEng.Rendering.csproj | 1 + .../DepthParallax/ParallaxLayerRenderCore.cs | 80 +++ CorgEng.Rendering/DepthParallax/README.md | 4 + CorgEng.Rendering/Icons/Icon.cs | 2 +- CorgEng.Rendering/InstancedRenderer.cs | 2 + .../ContentLoadingTests.cs | 9 +- .../DependencyInjection/DependencyTests.cs | 2 +- .../ProcessingSystemTest.cs | 31 +- .../EntityComponentSystem/SignalTests.cs | 32 +- .../TestEntitySystems.cs | 98 ++++ CorgEng.Tests/ExperimentationTests.cs | 2 +- CorgEng.Tests/FontTests/FontTests.cs | 2 +- .../NetworkingTests/ClientAddressTests.cs | 2 +- .../EntityCommunicatorTests.cs | 26 +- .../NetworkingTests/NetworkingTest.cs | 117 ++-- .../NetworkingTests/PacketQueueTests.cs | 2 +- .../NetworkingTests/PrototypeTests.cs | 33 +- .../NetworkingTests/SerializationTest.cs | 2 +- CorgEng.Tests/NetworkingTests/TestCVars.cs | 93 +++ CorgEng.Tests/Pathfinding/PathfindingTests.cs | 2 +- CorgEng.Tests/Performance/EventProcessing.cs | 26 +- .../Performance/SerialisationPerformance.cs | 20 +- CorgEng.Tests/Performance/ThreadTest.cs | 2 +- CorgEng.Tests/Performance/UnsortedTests.cs | 127 +++++ .../RenderingTests/BindedBatchTest.cs | 2 +- .../TransparencyLoadingTests.cs | 2 +- CorgEng.Tests/Stubs/RenderCoreStub.cs | 2 +- CorgEng.Tests/TestBase.cs | 20 + CorgEng.Tests/TestSetup.cs | 2 +- .../UnitTests/UserInterfaceButtonTests.cs | 13 +- .../UnitTests/UserInterfaceComponentTests.cs | 43 +- .../UnitTests/UserInterfaceRaycastTests.cs | 10 +- .../UnitTests/UserInterfaceXmlLoaderTests.cs | 18 +- CorgEng.Tests/UtilityTypes/BatchTests.cs | 2 +- CorgEng.Tests/UtilityTypes/BinaryListTests.cs | 2 +- .../MatrixTests/GeneralMatrixTests.cs | 2 +- .../MatrixTests/IdentityMatrixTests.cs | 2 +- .../MatrixTests/MatrixDimensionErrorTests.cs | 2 +- .../MatrixTests/MultiplicationMatrixTests.cs | 2 +- .../MatrixTests/TranslationMatrixTests.cs | 2 +- CorgEng.Tests/UtilityTypes/VectorTests.cs | 2 +- CorgEng.Tests/World/WorldEntitySystem.cs | 23 + CorgEng.Tests/World/WorldTests.cs | 38 +- .../Generators/UserInterfaceXmlLoader.cs | 10 +- CorgEng.UserInterface/Popups/PopupManager.cs | 7 +- .../Systems/UserInterfaceClickSystem.cs | 2 +- .../UserInterfaceBox.cs | 5 +- .../UserInterfaceButton.cs | 5 +- .../UserInterfaceComponent.cs | 12 +- .../UserInterfaceComponentFactory.cs | 27 +- .../UserInterfaceDropdown.cs | 7 +- .../UserInterfaceIcon.cs | 5 +- .../UserInterfaceText.cs | 5 +- .../BindableProperties/BindableProperty.cs | 23 +- .../Linecasting/LineCaster.cs | 2 +- CorgEng.UtilityTypes/Monads/Result.cs | 47 ++ CorgEng.UtilityTypes/Vectors/Vector.cs | 8 +- CorgEng.World/EntitySystems/WorldSystem.cs | 8 +- .../{World.cs => EntityPositionTracker.cs} | 2 +- 206 files changed, 3584 insertions(+), 1031 deletions(-) create mode 100644 CorgEng.Core/Content/Shaders/DepthShader/DepthShader.frag create mode 100644 CorgEng.Core/Content/Shaders/DepthShader/DepthShader.vert create mode 100644 CorgEng.Core/Modules/WorldInitialiseAttribute.cs create mode 100644 CorgEng.Core/Rendering/ScalingModes.cs create mode 100644 CorgEng.Debug/CorgEng.Debug.csproj create mode 100644 CorgEng.Debug/RenderCores/DebugOverlayRenderCore.cs create mode 100644 CorgEng.DependencyInjection/Dependencies/NonDependencyAttribute.cs rename CorgEng.EntityComponentSystem/Components/{ComponentExtensions.cs => ComponentSignalInjector.cs} (60%) create mode 100644 CorgEng.EntityComponentSystem/Components/ComponentVariables/CVar.cs create mode 100644 CorgEng.EntityComponentSystem/Components/ComponentVariables/IComponentVariable.cs create mode 100644 CorgEng.EntityComponentSystem/Components/ComponentVariables/Networking/INetVar.cs create mode 100644 CorgEng.EntityComponentSystem/Components/ComponentVariables/Networking/NetCVar.cs delete mode 100644 CorgEng.EntityComponentSystem/Entities/EntityFactory.cs delete mode 100644 CorgEng.EntityComponentSystem/Events/EventManager.cs create mode 100644 CorgEng.EntityComponentSystem/Systems/EntitySystemManager.cs create mode 100644 CorgEng.EntityComponentSystem/Systems/EntitySystemThreadManager.cs create mode 100644 CorgEng.EntityComponentSystem/WorldManager/World.cs create mode 100644 CorgEng.EntityComponentSystem/WorldManager/WorldFactory.cs create mode 100644 CorgEng.Functional/CorgEng.Functional.csproj create mode 100644 CorgEng.Functional/Monads/Result.cs create mode 100644 CorgEng.GenericInterfaces/DependencyInjection/NonDependencyAttribute.cs rename {CorgEng.EntityComponentSystem/Events => CorgEng.GenericInterfaces/EntityComponentSystem}/EventComponentPair.cs (59%) create mode 100644 CorgEng.GenericInterfaces/EntityComponentSystem/IComponentSignalInjector.cs delete mode 100644 CorgEng.GenericInterfaces/EntityComponentSystem/IEntityFactory.cs create mode 100644 CorgEng.GenericInterfaces/EntityComponentSystem/IEntityManager.cs create mode 100644 CorgEng.GenericInterfaces/EntityComponentSystem/IEntitySystemManager.cs create mode 100644 CorgEng.GenericInterfaces/EntityComponentSystem/IWorld.cs create mode 100644 CorgEng.GenericInterfaces/EntityComponentSystem/IWorldFactory.cs create mode 100644 CorgEng.GenericInterfaces/EntityComponentSystem/WorldObject.cs rename CorgEng.GenericInterfaces/{World => EntityPositionTracker}/IContentsHolder.cs (100%) rename CorgEng.GenericInterfaces/{World/IWorld.cs => EntityPositionTracker/IEntityPositionTracker.cs} (99%) rename CorgEng.GenericInterfaces/{World => EntityPositionTracker}/IWorldTrackComponent.cs (100%) rename CorgEng.GenericInterfaces/Networking/Networking/Client/{INetworkingClient.cs => INetworkClient.cs} (97%) create mode 100644 CorgEng.GenericInterfaces/Networking/Networking/Client/INetworkClientFactory.cs create mode 100644 CorgEng.GenericInterfaces/Networking/Networking/Server/IEntityCommunicatorFactory.cs rename CorgEng.GenericInterfaces/Networking/Networking/Server/{INetworkingServer.cs => INetworkServer.cs} (97%) create mode 100644 CorgEng.GenericInterfaces/Networking/Networking/Server/INetworkServerFactory.cs create mode 100644 CorgEng.GenericInterfaces/UtilityTypes/IListenable.cs rename CorgEng.Networking/Networking/Client/{NetworkingClient.cs => NetworkClient.cs} (95%) create mode 100644 CorgEng.Networking/Networking/Client/NetworkClientFactory.cs rename CorgEng.Networking/Networking/{Server => }/EntityCommunicator.cs (95%) create mode 100644 CorgEng.Networking/Networking/EntityCommunicatorFactory.cs rename CorgEng.Networking/Networking/Server/{NetworkingServer.cs => NetworkServer.cs} (93%) create mode 100644 CorgEng.Networking/Networking/Server/NetworkServerFactory.cs create mode 100644 CorgEng.NetworkingPrimitives/CorgEng.NetworkingPrimitives.csproj create mode 100644 CorgEng.NetworkingPrimitives/Networking/INetVar.cs create mode 100644 CorgEng.NetworkingPrimitives/Networking/NetCVar.cs create mode 100644 CorgEng.Rendering/ComponentSystem/DepthParallax/DepthSpriteRenderComponent.cs rename {CorgEng.EntityComponentSystem/Implementations/Rendering => CorgEng.Rendering/ComponentSystem}/SpriteRendering/AddOverlayEvent.cs (100%) rename {CorgEng.EntityComponentSystem/Implementations/Rendering => CorgEng.Rendering/ComponentSystem}/SpriteRendering/RemoveOverlayEvent.cs (100%) rename {CorgEng.EntityComponentSystem/Implementations/Rendering => CorgEng.Rendering/ComponentSystem}/SpriteRendering/SetDirectionEvent.cs (100%) rename {CorgEng.EntityComponentSystem/Implementations/Rendering => CorgEng.Rendering/ComponentSystem}/SpriteRendering/SetSpriteEvent.cs (100%) rename {CorgEng.EntityComponentSystem/Implementations/Rendering => CorgEng.Rendering/ComponentSystem}/SpriteRendering/SetSpriteRendererEvent.cs (100%) rename {CorgEng.EntityComponentSystem/Implementations/Rendering => CorgEng.Rendering/ComponentSystem}/SpriteRendering/SpriteRenderComponent.cs (94%) rename {CorgEng.EntityComponentSystem/Implementations/Rendering => CorgEng.Rendering/ComponentSystem}/SpriteRendering/SpriteRenderSystem.cs (98%) create mode 100644 CorgEng.Rendering/DepthParallax/ParallaxLayerRenderCore.cs create mode 100644 CorgEng.Rendering/DepthParallax/README.md create mode 100644 CorgEng.Tests/EntityComponentSystem/TestEntitySystems.cs create mode 100644 CorgEng.Tests/NetworkingTests/TestCVars.cs create mode 100644 CorgEng.Tests/Performance/UnsortedTests.cs create mode 100644 CorgEng.Tests/TestBase.cs create mode 100644 CorgEng.Tests/World/WorldEntitySystem.cs create mode 100644 CorgEng.UtilityTypes/Monads/Result.cs rename CorgEng.World/WorldTracking/{World.cs => EntityPositionTracker.cs} (98%) diff --git a/AutoMap/AutoMap.cs b/AutoMap/AutoMap.cs index 0e838ea8..344bb252 100644 --- a/AutoMap/AutoMap.cs +++ b/AutoMap/AutoMap.cs @@ -19,8 +19,8 @@ class AutoMap [UsingDependency] private static IIsometricCameraFactory isometricCameraFactory; - [UsingDependency] - private static IEntityFactory EntityFactory; + //[UsingDependency] + //private static IEntityFactory EntityFactory; public static void Main(string[] args) { @@ -35,11 +35,11 @@ public static void Main(string[] args) IIsometricCamera camera = isometricCameraFactory.CreateCamera(); //Create the entity to hold and move the camera - EntityFactory.CreateEmptyEntity((mainCameraEntity) => { - mainCameraEntity.AddComponent(new TransformComponent()); + //EntityFactory.CreateEmptyEntity((mainCameraEntity) => { + // mainCameraEntity.AddComponent(new TransformComponent()); //mainCameraEntity.AddComponent(new PlayerMovementComponent()); //mainCameraEntity.AddComponent(new CameraComponent(camera)); - }); + //}); //Set the main camera CorgEngMain.SetMainCamera(camera); diff --git a/AutoMap/Rendering/AutoMapRenderCore.cs b/AutoMap/Rendering/AutoMapRenderCore.cs index 31a5484b..ccc67b11 100644 --- a/AutoMap/Rendering/AutoMapRenderCore.cs +++ b/AutoMap/Rendering/AutoMapRenderCore.cs @@ -1,6 +1,7 @@ using CorgEng.Core; using CorgEng.Core.Dependencies; using CorgEng.Core.Rendering; +using CorgEng.GenericInterfaces.EntityComponentSystem; using CorgEng.GenericInterfaces.Rendering.Renderers.SpriteRendering; using System; using System.Collections.Generic; @@ -18,6 +19,10 @@ public class AutoMapRenderCore : RenderCore private ISpriteRenderer spriteRenderer; + public AutoMapRenderCore(IWorld world) : base(world) + { + } + public override void Initialize() { spriteRenderer = SpriteRendererFactory.CreateSpriteRenderer(0); diff --git a/CorgEng.AiBehaviour/BehaviourManager.cs b/CorgEng.AiBehaviour/BehaviourManager.cs index d04d0dc9..15207145 100644 --- a/CorgEng.AiBehaviour/BehaviourManager.cs +++ b/CorgEng.AiBehaviour/BehaviourManager.cs @@ -11,7 +11,7 @@ namespace CorgEng.AiBehaviour { - internal class BehaviourManager : IBehaviourManager + internal class BehaviourManager : WorldObject, IBehaviourManager { //Are we currently processing? Prevents AIs from having multiple AI trees thinking @@ -22,7 +22,7 @@ internal class BehaviourManager : IBehaviourManager private TransformComponent _transform; - public IVector Position => _transform.Position; + public IVector Position => _transform.Position.Value; /// /// The pawn entity we are attached to @@ -57,15 +57,16 @@ public IEntity PawnEntity /// /// The root behaviour node /// - internal BehaviourRoot root = new BehaviourRoot(); + internal BehaviourRoot root; /// /// Setup the behaviour manager /// /// - public BehaviourManager(IEntity pawnEntity) + public BehaviourManager(IWorld world, IEntity pawnEntity) : base(world) { PawnEntity = pawnEntity; + root = new BehaviourRoot(world); } public async Task Process() diff --git a/CorgEng.AiBehaviour/BehaviourNode.cs b/CorgEng.AiBehaviour/BehaviourNode.cs index 558e8b7b..c79fa157 100644 --- a/CorgEng.AiBehaviour/BehaviourNode.cs +++ b/CorgEng.AiBehaviour/BehaviourNode.cs @@ -2,6 +2,7 @@ using CorgEng.Core.Dependencies; using CorgEng.GenericInterfaces.AiBehaviours; +using CorgEng.GenericInterfaces.EntityComponentSystem; using CorgEng.GenericInterfaces.Logging; using CorgEng.GenericInterfaces.UtilityTypes.BinaryLists; using System; @@ -12,7 +13,7 @@ namespace CorgEng.AiBehaviour { - public abstract class BehaviourNode : IBehaviourNode + public abstract class BehaviourNode : WorldObject, IBehaviourNode { [UsingDependency] @@ -27,7 +28,7 @@ public abstract class BehaviourNode : IBehaviourNode public abstract BehaviourContinuationMode ContinuationMode { get; } - public BehaviourNode() + public BehaviourNode(IWorld world) : base(world) { Subtasks = BinaryListFactory.CreateEmpty(); } diff --git a/CorgEng.AiBehaviour/BehaviourRoot.cs b/CorgEng.AiBehaviour/BehaviourRoot.cs index 3bfced44..a65d04c6 100644 --- a/CorgEng.AiBehaviour/BehaviourRoot.cs +++ b/CorgEng.AiBehaviour/BehaviourRoot.cs @@ -1,4 +1,5 @@ using CorgEng.GenericInterfaces.AiBehaviours; +using CorgEng.GenericInterfaces.EntityComponentSystem; using System; using System.Collections.Generic; using System.Linq; @@ -9,6 +10,9 @@ namespace CorgEng.AiBehaviour { internal class BehaviourRoot : BehaviourNode { + public BehaviourRoot(IWorld world) : base(world) + { + } public override BehaviourContinuationMode ContinuationMode => BehaviourContinuationMode.CANCEL_ON_FAIL; diff --git a/CorgEng.AiBehaviour/Factories/BehaviourManagerFactory.cs b/CorgEng.AiBehaviour/Factories/BehaviourManagerFactory.cs index 44203b93..caaffd1a 100644 --- a/CorgEng.AiBehaviour/Factories/BehaviourManagerFactory.cs +++ b/CorgEng.AiBehaviour/Factories/BehaviourManagerFactory.cs @@ -13,9 +13,9 @@ namespace CorgEng.AiBehaviour.Factories internal class BehaviourManagerFactory : IBehaviourManagerFactory { - public IBehaviourManager CreateBehaviourManager(IEntity attachedPawn, params IBehaviourNode[] behaviourNodes) + public IBehaviourManager CreateBehaviourManager(IWorld world, IEntity attachedPawn, params IBehaviourNode[] behaviourNodes) { - BehaviourManager createdManager = new BehaviourManager(attachedPawn); + BehaviourManager createdManager = new BehaviourManager(world, attachedPawn); int i = 0; foreach (IBehaviourNode behaviourNode in behaviourNodes) diff --git a/CorgEng.AiBehaviour/Systems/AiActionProcessingSystem.cs b/CorgEng.AiBehaviour/Systems/AiActionProcessingSystem.cs index 5b73ee2e..3413fd14 100644 --- a/CorgEng.AiBehaviour/Systems/AiActionProcessingSystem.cs +++ b/CorgEng.AiBehaviour/Systems/AiActionProcessingSystem.cs @@ -17,7 +17,7 @@ internal class AiActionProcessingSystem : ProcessingSystem protected override int ProcessDelay => 100; - public override void SystemSetup() + public override void SystemSetup(IWorld world) { RegisterLocalEvent(OnComponentAdded); } diff --git a/CorgEng.AiBehaviour/Systems/BehaviourProcessingSystem.cs b/CorgEng.AiBehaviour/Systems/BehaviourProcessingSystem.cs index 77701cfc..2dc4a309 100644 --- a/CorgEng.AiBehaviour/Systems/BehaviourProcessingSystem.cs +++ b/CorgEng.AiBehaviour/Systems/BehaviourProcessingSystem.cs @@ -22,7 +22,7 @@ internal class BehaviourProcessingSystem : ProcessingSystem protected override int ProcessDelay => 1000; - public override void SystemSetup() + public override void SystemSetup(IWorld world) { RegisterLocalEvent(OnComponentAdded); } diff --git a/CorgEng.Audio/Systems/AudioListenerSystem.cs b/CorgEng.Audio/Systems/AudioListenerSystem.cs index 1e69efd6..3425e068 100644 --- a/CorgEng.Audio/Systems/AudioListenerSystem.cs +++ b/CorgEng.Audio/Systems/AudioListenerSystem.cs @@ -18,7 +18,7 @@ internal class AudioListenerSystem : EntitySystem public override EntitySystemFlags SystemFlags => EntitySystemFlags.CLIENT_SYSTEM; - public override void SystemSetup() + public override void SystemSetup(IWorld world) { RegisterLocalEvent(AudioListenerMoved); RegisterLocalEvent(AudioListenerInitialised); @@ -32,7 +32,7 @@ private void AudioListenerMoved(IEntity entity, AudioListenerComponent audioList private void AudioListenerInitialised(IEntity entity, AudioListenerComponent audioListenerComponent, InitialiseEvent initialiseEvent) { - IVector position = audioListenerComponent.Transform.Position; + IVector position = audioListenerComponent.Transform.Position.Value; AudioMaster.UpdateListener(position.X, position.Y, 0); } diff --git a/CorgEng.Claims/Systems/ClaimSystem.cs b/CorgEng.Claims/Systems/ClaimSystem.cs index 9e3bd392..c8e69b88 100644 --- a/CorgEng.Claims/Systems/ClaimSystem.cs +++ b/CorgEng.Claims/Systems/ClaimSystem.cs @@ -16,7 +16,7 @@ internal class ClaimSystem : EntitySystem public override EntitySystemFlags SystemFlags => EntitySystemFlags.HOST_SYSTEM; - public override void SystemSetup() + public override void SystemSetup(IWorld world) { //Deletable is common to all entities, so register to that component. RegisterLocalEvent(HandleClaimRequest); diff --git a/CorgEng.ContentLoading/DefinitionNodes/ArrayNode.cs b/CorgEng.ContentLoading/DefinitionNodes/ArrayNode.cs index aa85f01e..b22f42b6 100644 --- a/CorgEng.ContentLoading/DefinitionNodes/ArrayNode.cs +++ b/CorgEng.ContentLoading/DefinitionNodes/ArrayNode.cs @@ -1,4 +1,5 @@ using CorgEng.GenericInterfaces.ContentLoading.DefinitionNodes; +using CorgEng.GenericInterfaces.EntityComponentSystem; using System; using System.Collections.Generic; using System.Linq; @@ -26,7 +27,7 @@ public override void ParseSelf(XmlNode node) arrayType = EntityLoader.TypePaths[node.Attributes["type"].Value]; } - public override object CreateInstance(object parent, Dictionary instanceRefs) + public override object CreateInstance(IWorld world, object parent, Dictionary instanceRefs) { //Create the array object Array createdArray = Array.CreateInstance(arrayType, Children.Count); @@ -34,7 +35,7 @@ public override object CreateInstance(object parent, Dictionary int i = 0; foreach (DefinitionNode child in Children) { - createdArray.SetValue(child.CreateInstance(createdArray, instanceRefs), i++); + createdArray.SetValue(child.CreateInstance(world, createdArray, instanceRefs), i++); } //Add a reference to the created array if (Key != null) diff --git a/CorgEng.ContentLoading/DefinitionNodes/ComponentNode.cs b/CorgEng.ContentLoading/DefinitionNodes/ComponentNode.cs index ea4e9d3c..c427d2cf 100644 --- a/CorgEng.ContentLoading/DefinitionNodes/ComponentNode.cs +++ b/CorgEng.ContentLoading/DefinitionNodes/ComponentNode.cs @@ -19,10 +19,10 @@ public ComponentNode(DefinitionNode parent) : base(parent) Parent = parent as EntityNode; } - public override object CreateInstance(object parent, Dictionary instanceRefs) + public override object CreateInstance(IWorld world, object parent, Dictionary instanceRefs) { //Create the component - IComponent createdComponent = base.CreateInstance(parent, instanceRefs) as IComponent; + IComponent createdComponent = base.CreateInstance(world, parent, instanceRefs) as IComponent; //Add the compoennt IEntity parentEntity = parent as IEntity; parentEntity.AddComponent(createdComponent); diff --git a/CorgEng.ContentLoading/DefinitionNodes/DefinitionNode.cs b/CorgEng.ContentLoading/DefinitionNodes/DefinitionNode.cs index f077692b..be04619f 100644 --- a/CorgEng.ContentLoading/DefinitionNodes/DefinitionNode.cs +++ b/CorgEng.ContentLoading/DefinitionNodes/DefinitionNode.cs @@ -1,4 +1,5 @@ -using System; +using CorgEng.GenericInterfaces.EntityComponentSystem; +using System; using System.Collections.Generic; using System.Linq; using System.Text; @@ -16,8 +17,11 @@ internal abstract class DefinitionNode public string? ID { get; set; } + protected IWorld world; + public DefinitionNode(DefinitionNode parent) { + this.world = world; parent?.Children.Add(this); } @@ -36,7 +40,7 @@ public virtual void ParseSelf(XmlNode node) /// Called when the instance needs to be created /// /// - public abstract object CreateInstance(object parent, Dictionary instanceRefs); + public abstract object CreateInstance(IWorld world, object parent, Dictionary instanceRefs); } } diff --git a/CorgEng.ContentLoading/DefinitionNodes/DependencyNode.cs b/CorgEng.ContentLoading/DefinitionNodes/DependencyNode.cs index 549e9425..f0ea23a6 100644 --- a/CorgEng.ContentLoading/DefinitionNodes/DependencyNode.cs +++ b/CorgEng.ContentLoading/DefinitionNodes/DependencyNode.cs @@ -1,6 +1,7 @@ using CorgEng.Core.Dependencies; using CorgEng.GenericInterfaces.ContentLoading.DefinitionNodes; using CorgEng.GenericInterfaces.DependencyInjection; +using CorgEng.GenericInterfaces.EntityComponentSystem; using System; using System.Collections.Generic; using System.ComponentModel; @@ -85,7 +86,7 @@ public override void ParseSelf(XmlNode node) } } - public override object CreateInstance(object parent, Dictionary instanceRefs) + public override object CreateInstance(IWorld world, object parent, Dictionary instanceRefs) { //Set the parameters if (hasDynamicParams) @@ -94,7 +95,7 @@ public override object CreateInstance(object parent, Dictionary { if (isDynamic[i]) { - parameters[i] = Children[i].CreateInstance(parent, instanceRefs); + parameters[i] = Children[i].CreateInstance(world, parent, instanceRefs); } } } diff --git a/CorgEng.ContentLoading/DefinitionNodes/DictionaryNode.cs b/CorgEng.ContentLoading/DefinitionNodes/DictionaryNode.cs index f351070d..23646523 100644 --- a/CorgEng.ContentLoading/DefinitionNodes/DictionaryNode.cs +++ b/CorgEng.ContentLoading/DefinitionNodes/DictionaryNode.cs @@ -1,4 +1,5 @@ using CorgEng.GenericInterfaces.ContentLoading.DefinitionNodes; +using CorgEng.GenericInterfaces.EntityComponentSystem; using System; using System.Collections.Generic; using System.Linq; @@ -41,7 +42,7 @@ public override void ParseSelf(XmlNode node) BindingFlags.Public | BindingFlags.Instance); } - public override object CreateInstance(object parent, Dictionary instanceRefs) + public override object CreateInstance(IWorld world, object parent, Dictionary instanceRefs) { //Create the array object object dictionary = Activator.CreateInstance(dictionaryType); @@ -49,7 +50,7 @@ public override object CreateInstance(object parent, Dictionary foreach (DefinitionNode child in Children) { addMethod.Invoke(dictionary, new object[] { - child.CreateInstance(dictionary, instanceRefs) + child.CreateInstance(world, dictionary, instanceRefs) }); } //Add a reference to the created array diff --git a/CorgEng.ContentLoading/DefinitionNodes/ElementNode.cs b/CorgEng.ContentLoading/DefinitionNodes/ElementNode.cs index 6e3663e5..a5ee85b1 100644 --- a/CorgEng.ContentLoading/DefinitionNodes/ElementNode.cs +++ b/CorgEng.ContentLoading/DefinitionNodes/ElementNode.cs @@ -1,4 +1,5 @@ using CorgEng.GenericInterfaces.ContentLoading.DefinitionNodes; +using CorgEng.GenericInterfaces.EntityComponentSystem; using System; using System.Collections.Generic; using System.ComponentModel; @@ -70,9 +71,9 @@ protected void ParseValue(XmlNode node) } } - public override object CreateInstance(object parent, Dictionary instanceRefs) + public override object CreateInstance(IWorld world, object parent, Dictionary instanceRefs) { - object propertyValue = GetValue(parent, instanceRefs); + object propertyValue = GetValue(world, parent, instanceRefs); if (Key != null) { instanceRefs.Add(Key, propertyValue); @@ -82,7 +83,7 @@ public override object CreateInstance(object parent, Dictionary private static MethodInfo cachedMethod = null; - private object GetValue(object parent, Dictionary instanceRefs) + private object GetValue(IWorld world, object parent, Dictionary instanceRefs) { //Has 2 children, key value pair if (Children.Count == 2) @@ -99,8 +100,8 @@ private object GetValue(object parent, Dictionary instanceRefs) } //Make the generic method and call it return cachedMethod.MakeGenericMethod(dictionaryParentKeyType, dictionaryParentValueType).Invoke(null, new object[] { - childKey.CreateInstance(parent, instanceRefs), - childValue.CreateInstance(parent, instanceRefs) + childKey.CreateInstance(world, parent, instanceRefs), + childValue.CreateInstance(world, parent, instanceRefs) }); } @@ -119,7 +120,7 @@ private object GetValue(object parent, Dictionary instanceRefs) } //1 Child, return value // Either object or value node - return Children[0].CreateInstance(null, instanceRefs); + return Children[0].CreateInstance(world, null, instanceRefs); } } diff --git a/CorgEng.ContentLoading/DefinitionNodes/EntityNode.cs b/CorgEng.ContentLoading/DefinitionNodes/EntityNode.cs index 0d2e6bb0..7cc17800 100644 --- a/CorgEng.ContentLoading/DefinitionNodes/EntityNode.cs +++ b/CorgEng.ContentLoading/DefinitionNodes/EntityNode.cs @@ -15,9 +15,6 @@ namespace CorgEng.GenericInterfaces.ContentLoading.DefinitionNodes internal class EntityNode : DefinitionNode, IEntityDefinition { - [UsingDependency] - public static IEntityFactory EntityFactory; - public bool Abstract { get; set; } = false; /// @@ -50,19 +47,19 @@ public override void ParseSelf(XmlNode node) /// Create an entity from this node /// /// - public IEntity CreateEntity(Action setupAction) + public IEntity CreateEntity(IWorld world, Action setupAction) { - return (IEntity)CreateInstance(null, new Dictionary(), setupAction); + return (IEntity)CreateInstance(world, null, new Dictionary(), setupAction); } - public override object CreateInstance(object parent, Dictionary instanceRefs) + public override object CreateInstance(IWorld world, object parent, Dictionary instanceRefs) { - return CreateInstance(parent, instanceRefs, null); + return CreateInstance(world, parent, instanceRefs, null); } - public object CreateInstance(object parent, Dictionary instanceRefs, Action setupAction) + public object CreateInstance(IWorld world, object parent, Dictionary instanceRefs, Action setupAction) { - IEntity createdEntity = EntityFactory.CreateUninitialisedEntity(); + IEntity createdEntity = world.EntityManager.CreateUninitialisedEntity(); //Store the key if (Key != null) { @@ -71,7 +68,7 @@ public object CreateInstance(object parent, Dictionary instanceR //Add on properties foreach (DefinitionNode childNode in Children) { - childNode.CreateInstance(createdEntity, instanceRefs); + childNode.CreateInstance(world, createdEntity, instanceRefs); } //Run init event setupAction?.Invoke(createdEntity); diff --git a/CorgEng.ContentLoading/DefinitionNodes/InstanceNode.cs b/CorgEng.ContentLoading/DefinitionNodes/InstanceNode.cs index 066d94d1..2f5bb905 100644 --- a/CorgEng.ContentLoading/DefinitionNodes/InstanceNode.cs +++ b/CorgEng.ContentLoading/DefinitionNodes/InstanceNode.cs @@ -1,4 +1,5 @@ using CorgEng.GenericInterfaces.ContentLoading.DefinitionNodes; +using CorgEng.GenericInterfaces.EntityComponentSystem; using System; using System.Collections.Generic; using System.Linq; @@ -25,7 +26,7 @@ public override void ParseSelf(XmlNode node) referenceName = node.InnerText.Trim(); } - public override object CreateInstance(object parent, Dictionary instanceRefs) + public override object CreateInstance(IWorld world, object parent, Dictionary instanceRefs) { if (instanceRefs.ContainsKey(referenceName)) { @@ -33,7 +34,7 @@ public override object CreateInstance(object parent, Dictionary } else { - return EntityLoader.GetDefinition(referenceName).CreateInstance(parent, instanceRefs); + return EntityLoader.GetDefinition(referenceName).CreateInstance(world, parent, instanceRefs); } } diff --git a/CorgEng.ContentLoading/DefinitionNodes/KeyNode.cs b/CorgEng.ContentLoading/DefinitionNodes/KeyNode.cs index 339fa6b1..30e2e36e 100644 --- a/CorgEng.ContentLoading/DefinitionNodes/KeyNode.cs +++ b/CorgEng.ContentLoading/DefinitionNodes/KeyNode.cs @@ -1,4 +1,5 @@ using CorgEng.GenericInterfaces.ContentLoading.DefinitionNodes; +using CorgEng.GenericInterfaces.EntityComponentSystem; using System; using System.Collections.Generic; using System.Linq; diff --git a/CorgEng.ContentLoading/DefinitionNodes/ObjectNode.cs b/CorgEng.ContentLoading/DefinitionNodes/ObjectNode.cs index fc515d02..81423ebf 100644 --- a/CorgEng.ContentLoading/DefinitionNodes/ObjectNode.cs +++ b/CorgEng.ContentLoading/DefinitionNodes/ObjectNode.cs @@ -1,4 +1,5 @@ using CorgEng.ContentLoading; +using CorgEng.GenericInterfaces.EntityComponentSystem; using System; using System.Collections.Generic; using System.ComponentModel; @@ -78,10 +79,10 @@ protected void ParseValue(XmlNode node) } } - public override object CreateInstance(object parent, Dictionary instanceRefs) + public override object CreateInstance(IWorld world, object parent, Dictionary instanceRefs) { //Create ourself - object createdObject = PropertyValue ?? Activator.CreateInstance(ObjectType); + object createdObject = PropertyValue ?? (typeof(WorldObject).IsAssignableFrom(ObjectType) ? Activator.CreateInstance(ObjectType, new object[] { world }) : Activator.CreateInstance(ObjectType)); //Store it if (Key != null) { @@ -90,7 +91,7 @@ public override object CreateInstance(object parent, Dictionary //Pass ourself to our children foreach (DefinitionNode childNode in Children) { - childNode.CreateInstance(createdObject, instanceRefs); + childNode.CreateInstance(world, createdObject, instanceRefs); } //Return the created object return createdObject; diff --git a/CorgEng.ContentLoading/DefinitionNodes/ParameterNode.cs b/CorgEng.ContentLoading/DefinitionNodes/ParameterNode.cs index 56465561..7900b6ea 100644 --- a/CorgEng.ContentLoading/DefinitionNodes/ParameterNode.cs +++ b/CorgEng.ContentLoading/DefinitionNodes/ParameterNode.cs @@ -1,4 +1,5 @@ using CorgEng.GenericInterfaces.ContentLoading.DefinitionNodes; +using CorgEng.GenericInterfaces.EntityComponentSystem; using System; using System.Collections.Generic; using System.Linq; @@ -13,9 +14,9 @@ public ParameterNode(DefinitionNode parent) : base(parent) { } - public override object CreateInstance(object parent, Dictionary instanceRefs) + public override object CreateInstance(IWorld world, object parent, Dictionary instanceRefs) { - object created = Children.First().CreateInstance(parent, instanceRefs); + object created = Children.First().CreateInstance(world, parent, instanceRefs); if (Key != null) { instanceRefs.Add(Key, created); diff --git a/CorgEng.ContentLoading/DefinitionNodes/PropertyNode.cs b/CorgEng.ContentLoading/DefinitionNodes/PropertyNode.cs index 0487095d..e651298f 100644 --- a/CorgEng.ContentLoading/DefinitionNodes/PropertyNode.cs +++ b/CorgEng.ContentLoading/DefinitionNodes/PropertyNode.cs @@ -1,5 +1,6 @@ using CorgEng.ContentLoading; using CorgEng.ContentLoading.DefinitionNodes; +using CorgEng.GenericInterfaces.EntityComponentSystem; using System; using System.Collections.Generic; using System.ComponentModel; @@ -70,7 +71,7 @@ protected void ParseValue(XmlNode node) } } - public override object CreateInstance(object parent, Dictionary instanceRefs) + public override object CreateInstance(IWorld world, object parent, Dictionary instanceRefs) { if (PropertyValue != null) { @@ -82,7 +83,7 @@ public override object CreateInstance(object parent, Dictionary } else { - object createdObject = Children[0].CreateInstance(null, instanceRefs); + object createdObject = Children[0].CreateInstance(world, null, instanceRefs); if (Key != null) { instanceRefs.Add(Key, createdObject); diff --git a/CorgEng.ContentLoading/DefinitionNodes/ValueNode.cs b/CorgEng.ContentLoading/DefinitionNodes/ValueNode.cs index c24083af..816ab172 100644 --- a/CorgEng.ContentLoading/DefinitionNodes/ValueNode.cs +++ b/CorgEng.ContentLoading/DefinitionNodes/ValueNode.cs @@ -1,4 +1,5 @@ using CorgEng.GenericInterfaces.ContentLoading.DefinitionNodes; +using CorgEng.GenericInterfaces.EntityComponentSystem; using System; using System.Collections.Generic; using System.Linq; diff --git a/CorgEng.ContentLoading/EntityCreator.cs b/CorgEng.ContentLoading/EntityCreator.cs index 302c30c7..75629b11 100644 --- a/CorgEng.ContentLoading/EntityCreator.cs +++ b/CorgEng.ContentLoading/EntityCreator.cs @@ -34,13 +34,13 @@ internal class EntityCreator : IEntityCreator /// /// /// - public IEntity CreateEntity(string entityName, Action preInitialisationEvents) + public IEntity CreateEntity(IWorld world, string entityName, Action preInitialisationEvents) { try { if (EntityNodesByName.ContainsKey(entityName)) { - IEntity createdEntity = EntityNodesByName[entityName].CreateEntity(preInitialisationEvents); + IEntity createdEntity = EntityNodesByName[entityName].CreateEntity(world, preInitialisationEvents); createdEntity.DefinitionName = entityName; return createdEntity; } @@ -60,13 +60,13 @@ public IEntity CreateEntity(string entityName, Action preInitialisation /// /// /// - public object CreateObject(string objectIdentifier) + public object CreateObject(IWorld world, string objectIdentifier) { try { if (EntityLoader.LoadedDefinitions.ContainsKey(objectIdentifier)) { - return EntityLoader.LoadedDefinitions[objectIdentifier].CreateInstance(null, new Dictionary()); + return EntityLoader.LoadedDefinitions[objectIdentifier].CreateInstance(world, null, new Dictionary()); } //Entity not found :( throw new Exception($"The object with name {objectIdentifier} could not be spawned as it doesn't exist."); diff --git a/CorgEng.Contents/Systems/ContentsSystem.cs b/CorgEng.Contents/Systems/ContentsSystem.cs index bd0641e3..76148cc6 100644 --- a/CorgEng.Contents/Systems/ContentsSystem.cs +++ b/CorgEng.Contents/Systems/ContentsSystem.cs @@ -28,7 +28,7 @@ internal class ContentsSystem : EntitySystem public override EntitySystemFlags SystemFlags => EntitySystemFlags.HOST_SYSTEM; - public override void SystemSetup() + public override void SystemSetup(IWorld world) { RegisterLocalEvent(OnContainedEntityDeleted); RegisterLocalEvent(OnContentHolderDeleted); @@ -55,7 +55,7 @@ private void ExitContainer(IEntity entity, ContainedComponent containedComponent TransformComponent? locatedTransform = containedComponent.Parent.FindComponent(); if (locatedTransform != null) { - new SetPositionEvent(locatedTransform.Position).Raise(entity); + new SetPositionEvent(locatedTransform.Position.Value).Raise(entity); } //Raise the left contents event new ContentsChangedEvent(containedComponent.Parent, null).Raise(entity); diff --git a/CorgEng.Core/Content/Shaders/CoreShader/CoreShader.frag b/CorgEng.Core/Content/Shaders/CoreShader/CoreShader.frag index b8850a0f..85d58bc3 100644 --- a/CorgEng.Core/Content/Shaders/CoreShader/CoreShader.frag +++ b/CorgEng.Core/Content/Shaders/CoreShader/CoreShader.frag @@ -5,11 +5,15 @@ in vec2 fragUV; out vec4 result; uniform sampler2D renderTexture; +uniform sampler2D depthTexture; + +uniform float depthIncrement = 0; void main() { result = texture(renderTexture, fragUV); - result.r = result.r > 1 ? 0 : result.r; - result.g = result.g > 1 ? 0 : result.g; - result.b = result.b > 1 ? 0 : result.b; + float depthValue = texture(depthTexture, fragUV).r + depthIncrement; + gl_FragDepth = depthValue; + //result = texture(depthTexture, fragUV); + //result.a *= depthValue; } diff --git a/CorgEng.Core/Content/Shaders/DepthShader/DepthShader.frag b/CorgEng.Core/Content/Shaders/DepthShader/DepthShader.frag new file mode 100644 index 00000000..62184d90 --- /dev/null +++ b/CorgEng.Core/Content/Shaders/DepthShader/DepthShader.frag @@ -0,0 +1,13 @@ +#version 330 core + +in vec2 fragUV; + +out vec4 result; + +uniform sampler2D renderTexture; +uniform sampler2D depthTexture; + +void main() +{ + result.rgb = vec3(gl_FragCoord.z, gl_FragCoord.z, gl_FragCoord.z); +} diff --git a/CorgEng.Core/Content/Shaders/DepthShader/DepthShader.vert b/CorgEng.Core/Content/Shaders/DepthShader/DepthShader.vert new file mode 100644 index 00000000..c55684e5 --- /dev/null +++ b/CorgEng.Core/Content/Shaders/DepthShader/DepthShader.vert @@ -0,0 +1,7 @@ +#version 330 core +layout (location = 0) in vec3 inVertexPos; + +void main() +{ + gl_Position = vec4(inVertexPos, 1.0); +} diff --git a/CorgEng.Core/CorgEng.Core.csproj b/CorgEng.Core/CorgEng.Core.csproj index a9aaf6a5..2dfc246a 100644 --- a/CorgEng.Core/CorgEng.Core.csproj +++ b/CorgEng.Core/CorgEng.Core.csproj @@ -28,6 +28,12 @@ Always + + Always + + + Always + Always diff --git a/CorgEng.Core/CorgEng.Core.sln b/CorgEng.Core/CorgEng.Core.sln index af705004..cbf8b97e 100644 --- a/CorgEng.Core/CorgEng.Core.sln +++ b/CorgEng.Core/CorgEng.Core.sln @@ -34,8 +34,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CorgEng.ContentLoading", ". EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CorgEng.MapLoading", "..\CorgEng.MapLoading\CorgEng.MapLoading.csproj", "{B324FB97-A466-4E19-BA67-9536023EE5B9}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AutoMap", "..\AutoMap\AutoMap.csproj", "{ADA9CC94-5705-499C-9C93-888E96F87DEA}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CorgEng.UserInterface", "..\CorgEng.UserInterface\CorgEng.UserInterface.csproj", "{19E048D9-1587-40CC-BEE3-E1883A21FEC8}" ProjectSection(ProjectDependencies) = postProject {BFC5583F-3A05-43E4-9D07-81D80B1EC685} = {BFC5583F-3A05-43E4-9D07-81D80B1EC685} @@ -77,6 +75,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CorgEng.Lighting", "..\Corg EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CorgEng.EntityQuery", "..\CorgEng.EntityQuery\CorgEng.EntityQuery.csproj", "{CD4E414F-536D-454B-8423-9631952EF705}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CorgEng.Functional", "..\CorgEng.Functional\CorgEng.Functional.csproj", "{0C23728A-E2A5-48A7-BF6F-C6EC865900D0}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -331,26 +331,6 @@ Global {B324FB97-A466-4E19-BA67-9536023EE5B9}.Release|x64.Build.0 = Release|x64 {B324FB97-A466-4E19-BA67-9536023EE5B9}.Release|x86.ActiveCfg = Release|x86 {B324FB97-A466-4E19-BA67-9536023EE5B9}.Release|x86.Build.0 = Release|x86 - {ADA9CC94-5705-499C-9C93-888E96F87DEA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {ADA9CC94-5705-499C-9C93-888E96F87DEA}.Debug|Any CPU.Build.0 = Debug|Any CPU - {ADA9CC94-5705-499C-9C93-888E96F87DEA}.Debug|ARM32.ActiveCfg = Debug|Any CPU - {ADA9CC94-5705-499C-9C93-888E96F87DEA}.Debug|ARM32.Build.0 = Debug|Any CPU - {ADA9CC94-5705-499C-9C93-888E96F87DEA}.Debug|ARM64.ActiveCfg = Debug|Any CPU - {ADA9CC94-5705-499C-9C93-888E96F87DEA}.Debug|ARM64.Build.0 = Debug|Any CPU - {ADA9CC94-5705-499C-9C93-888E96F87DEA}.Debug|x64.ActiveCfg = Debug|x64 - {ADA9CC94-5705-499C-9C93-888E96F87DEA}.Debug|x64.Build.0 = Debug|x64 - {ADA9CC94-5705-499C-9C93-888E96F87DEA}.Debug|x86.ActiveCfg = Debug|x86 - {ADA9CC94-5705-499C-9C93-888E96F87DEA}.Debug|x86.Build.0 = Debug|x86 - {ADA9CC94-5705-499C-9C93-888E96F87DEA}.Release|Any CPU.ActiveCfg = Release|Any CPU - {ADA9CC94-5705-499C-9C93-888E96F87DEA}.Release|Any CPU.Build.0 = Release|Any CPU - {ADA9CC94-5705-499C-9C93-888E96F87DEA}.Release|ARM32.ActiveCfg = Release|Any CPU - {ADA9CC94-5705-499C-9C93-888E96F87DEA}.Release|ARM32.Build.0 = Release|Any CPU - {ADA9CC94-5705-499C-9C93-888E96F87DEA}.Release|ARM64.ActiveCfg = Release|Any CPU - {ADA9CC94-5705-499C-9C93-888E96F87DEA}.Release|ARM64.Build.0 = Release|Any CPU - {ADA9CC94-5705-499C-9C93-888E96F87DEA}.Release|x64.ActiveCfg = Release|x64 - {ADA9CC94-5705-499C-9C93-888E96F87DEA}.Release|x64.Build.0 = Release|x64 - {ADA9CC94-5705-499C-9C93-888E96F87DEA}.Release|x86.ActiveCfg = Release|x86 - {ADA9CC94-5705-499C-9C93-888E96F87DEA}.Release|x86.Build.0 = Release|x86 {19E048D9-1587-40CC-BEE3-E1883A21FEC8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {19E048D9-1587-40CC-BEE3-E1883A21FEC8}.Debug|Any CPU.Build.0 = Debug|Any CPU {19E048D9-1587-40CC-BEE3-E1883A21FEC8}.Debug|ARM32.ActiveCfg = Debug|Any CPU @@ -671,6 +651,26 @@ Global {CD4E414F-536D-454B-8423-9631952EF705}.Release|x64.Build.0 = Release|Any CPU {CD4E414F-536D-454B-8423-9631952EF705}.Release|x86.ActiveCfg = Release|Any CPU {CD4E414F-536D-454B-8423-9631952EF705}.Release|x86.Build.0 = Release|Any CPU + {0C23728A-E2A5-48A7-BF6F-C6EC865900D0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0C23728A-E2A5-48A7-BF6F-C6EC865900D0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0C23728A-E2A5-48A7-BF6F-C6EC865900D0}.Debug|ARM32.ActiveCfg = Debug|Any CPU + {0C23728A-E2A5-48A7-BF6F-C6EC865900D0}.Debug|ARM32.Build.0 = Debug|Any CPU + {0C23728A-E2A5-48A7-BF6F-C6EC865900D0}.Debug|ARM64.ActiveCfg = Debug|Any CPU + {0C23728A-E2A5-48A7-BF6F-C6EC865900D0}.Debug|ARM64.Build.0 = Debug|Any CPU + {0C23728A-E2A5-48A7-BF6F-C6EC865900D0}.Debug|x64.ActiveCfg = Debug|Any CPU + {0C23728A-E2A5-48A7-BF6F-C6EC865900D0}.Debug|x64.Build.0 = Debug|Any CPU + {0C23728A-E2A5-48A7-BF6F-C6EC865900D0}.Debug|x86.ActiveCfg = Debug|Any CPU + {0C23728A-E2A5-48A7-BF6F-C6EC865900D0}.Debug|x86.Build.0 = Debug|Any CPU + {0C23728A-E2A5-48A7-BF6F-C6EC865900D0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0C23728A-E2A5-48A7-BF6F-C6EC865900D0}.Release|Any CPU.Build.0 = Release|Any CPU + {0C23728A-E2A5-48A7-BF6F-C6EC865900D0}.Release|ARM32.ActiveCfg = Release|Any CPU + {0C23728A-E2A5-48A7-BF6F-C6EC865900D0}.Release|ARM32.Build.0 = Release|Any CPU + {0C23728A-E2A5-48A7-BF6F-C6EC865900D0}.Release|ARM64.ActiveCfg = Release|Any CPU + {0C23728A-E2A5-48A7-BF6F-C6EC865900D0}.Release|ARM64.Build.0 = Release|Any CPU + {0C23728A-E2A5-48A7-BF6F-C6EC865900D0}.Release|x64.ActiveCfg = Release|Any CPU + {0C23728A-E2A5-48A7-BF6F-C6EC865900D0}.Release|x64.Build.0 = Release|Any CPU + {0C23728A-E2A5-48A7-BF6F-C6EC865900D0}.Release|x86.ActiveCfg = Release|Any CPU + {0C23728A-E2A5-48A7-BF6F-C6EC865900D0}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/CorgEng.Core/CorgEngMain.cs b/CorgEng.Core/CorgEngMain.cs index a43c4fa2..b0ab5967 100644 --- a/CorgEng.Core/CorgEngMain.cs +++ b/CorgEng.Core/CorgEngMain.cs @@ -4,6 +4,7 @@ using CorgEng.Core.Modules; using CorgEng.Core.Rendering; using CorgEng.Core.Rendering.Exceptions; +using CorgEng.GenericInterfaces.EntityComponentSystem; using CorgEng.GenericInterfaces.Logging; using CorgEng.GenericInterfaces.Rendering; using GLFW; @@ -14,6 +15,7 @@ using System.Linq; using System.Reflection; using System.Runtime.Loader; +using System.Threading; using System.Threading.Tasks; using System.Xml.Linq; @@ -79,6 +81,24 @@ public static class CorgEngMain public static event Action OnReadyEvents = null; + private static IWorld primaryWorld = null; + + /// + /// The main world to use for the game for when one isn't accessible + /// + public static IWorld PrimaryWorld + { + get => primaryWorld; + set { + primaryWorld = value; + WorldInit(primaryWorld); + } + } + /// + /// All current active worlds + /// + public static ConcurrentBag WorldList { get; } = new ConcurrentBag(); + /// /// List of actions queued to be execuetd on the main thread /// @@ -92,6 +112,8 @@ public static void Initialize(bool disableRendering = false) { try { + // Reset the world list + WorldList.Clear(); //Load priority modules (Logging) PriorityModuleInit(); Logger?.WriteLine("Starting CorgEng Application", LogType.DEBUG); @@ -167,6 +189,8 @@ public static void TransferToRenderingThread() } } } + // Trigger any deferred rendering thread code + CheckQueuedExecutions(); //Swap the framebuffers GameWindow.SwapFramebuffers(); //Poll for system events to prevent the program from showing as hanging @@ -226,6 +250,8 @@ public static void Shutdown() TriggerTerminateMethods(); //Terminate GLFW Glfw.Terminate(); + // Reset the world bag + WorldList.Clear(); } /// @@ -270,7 +296,7 @@ public static void LoadConfig(string filePath, bool embeddedResource = true, boo //Unit test support. if (Assembly.GetCallingAssembly() != null && Assembly.GetCallingAssembly() != Assembly.GetEntryAssembly()) loadedAssemblies.Add(Assembly.GetCallingAssembly()); - if(Assembly.GetExecutingAssembly() != null) + if (Assembly.GetExecutingAssembly() != null) loadedAssemblies.Add(Assembly.GetExecutingAssembly()); foreach (XElement dependency in childElement.Elements()) { @@ -303,7 +329,7 @@ public static void LoadConfig(string filePath, bool embeddedResource = true, boo Console.Error.WriteLine("Please reinstall the program and ensure that if developing a CorgEng game, the config is set as an embedded resource."); Console.Error.WriteLine("The application will now be terminated."); Console.Error.WriteLine("Press any key to continue..."); - if(awaitOnError) + if (awaitOnError) Console.ReadKey(); throw; } @@ -333,10 +359,11 @@ private static void PriorityModuleInit() ModuleLoadAttributes = LoadedAssemblyModules .SelectMany(assembly => assembly.GetTypes() .SelectMany(type => type.GetMethods(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static) - .Where(method => method.GetCustomAttribute() != null ))); - Parallel.ForEach(ModuleLoadAttributes, (MethodInfo) => { + .Where(method => method.GetCustomAttribute() != null))); + Parallel.ForEach(ModuleLoadAttributes, (MethodInfo) => + { Console.WriteLine(MethodInfo.Name); - if(MethodInfo.GetCustomAttribute().priority + if (MethodInfo.GetCustomAttribute().priority && !MethodInfo.GetCustomAttribute().mainThread) MethodInfo.Invoke(null, new object[] { }); }); @@ -353,7 +380,8 @@ private static void PriorityModuleInit() /// private static void ModuleInit() { - Parallel.ForEach(ModuleLoadAttributes, (MethodInfo) => { + Parallel.ForEach(ModuleLoadAttributes, (MethodInfo) => + { if (!MethodInfo.GetCustomAttribute().priority && !MethodInfo.GetCustomAttribute().mainThread) { @@ -370,6 +398,21 @@ private static void ModuleInit() ModuleLoadAttributes = null; } + /// + /// Calls priority method modules + /// + private static void WorldInit(IWorld world) + { + IEnumerable worldMethods = LoadedAssemblyModules + .SelectMany(assembly => assembly.GetTypes() + .SelectMany(type => type.GetMethods(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static) + .Where(method => method.GetCustomAttribute() != null))); + foreach (MethodInfo methodToInvoke in worldMethods) + { + methodToInvoke.Invoke(null, new object[] { world }); + } + } + /// /// Triggers termination methods /// @@ -403,5 +446,113 @@ public static void ExecuteOnRenderingThread(Action action) } } + /// + /// The lowest time of the thing that we want to fire + /// + private static double nextBucketFireTime = double.PositiveInfinity; + + /// + /// A heap of the things that we want to execute on this thread and the + /// time of when we want to execute them. + /// + private static PriorityQueue executeInQueue = new PriorityQueue(); + + private static Thread headlessExecutionThread = null; + + /// + /// Add something to a bucket + /// + /// + /// The time to wait in milliseconds + public static void ExecuteIn(Action action, double executeTime) + { + if (!IsRendering && headlessExecutionThread == null) + { + // Lock something random so that we can only enter this once + lock (executeInQueue) + { + if (headlessExecutionThread == null) + { + // This is horrible + headlessExecutionThread = new Thread(() => { + while (!Terminated) + { + CheckQueuedExecutions(); + Thread.Yield(); + } + lock (executeInQueue) + { + headlessExecutionThread = null; + } + }); + headlessExecutionThread.Start(); + } + } + } + double timeToFire = Time + executeTime * 0.001; + Logger.WriteLine($"Action queued to fire at {timeToFire}", LogType.WARNING); + // 0 or negative execution time + if (timeToFire <= Time) + { + //Rendering thread exists, queue the action + queuedActions.Enqueue(action); + return; + } + // Queue the action to be fired + lock (executeInQueue) + { + nextBucketFireTime = Math.Min(nextBucketFireTime, timeToFire); + executeInQueue.Enqueue(action, timeToFire); + } + } + + /// + /// Check the queue of things that we want to fire on this thread and fire them if + /// we are ready for them. + /// + internal static void CheckQueuedExecutions() + { + if (nextBucketFireTime > Time) + return; + lock (executeInQueue) + { + while (nextBucketFireTime <= Time) + { + // Invoke the action + Action lowest = executeInQueue.Dequeue(); + //Logger.WriteLine($"Action invoked at {Time}. NEXT BUCKET TIME: {nextBucketFireTime}", LogType.WARNING); + lowest.Invoke(); + // Move to the next + if (executeInQueue.TryPeek(out Action _, out double nextFireTime)) + { + nextBucketFireTime = nextFireTime; + } + else + { + nextBucketFireTime = double.PositiveInfinity; + return; + } + } + } + } + + /** + * Fully cleanup the CorgEng application, resetting it into a state + * of startup. + */ + public static void Cleanup() + { + MainCamera = null; + primaryWorld = null; + foreach (IWorld world in WorldList) + { + world.Cleanup(); + } + WorldList.Clear(); + queuedActions.Clear(); + MainRenderCore = null; + Logger.WriteLine("Full cleanup of CorgEng application completed.", LogType.DEBUG); + } + } } diff --git a/CorgEng.Core/Modules/WorldInitialiseAttribute.cs b/CorgEng.Core/Modules/WorldInitialiseAttribute.cs new file mode 100644 index 00000000..75eacd45 --- /dev/null +++ b/CorgEng.Core/Modules/WorldInitialiseAttribute.cs @@ -0,0 +1,15 @@ +using JetBrains.Annotations; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CorgEng.Core.Modules +{ + [MeansImplicitUse] + [AttributeUsage(AttributeTargets.Method)] + public class WorldInitialiseAttribute : Attribute + { + } +} diff --git a/CorgEng.Core/Rendering/RenderCore.cs b/CorgEng.Core/Rendering/RenderCore.cs index 6d8966da..43b1687d 100644 --- a/CorgEng.Core/Rendering/RenderCore.cs +++ b/CorgEng.Core/Rendering/RenderCore.cs @@ -1,14 +1,16 @@ using CorgEng.Core.Dependencies; using CorgEng.GenericInterfaces.Core; +using CorgEng.GenericInterfaces.EntityComponentSystem; using CorgEng.GenericInterfaces.Logging; using CorgEng.GenericInterfaces.Rendering.Shaders; using CorgEng.GenericInterfaces.UtilityTypes; using System; +using System.Collections.Generic; using static OpenGL.Gl; namespace CorgEng.Core.Rendering { - public abstract class RenderCore : IRenderCore + public abstract class RenderCore : WorldObject, IRenderCore { [UsingDependency] @@ -38,8 +40,14 @@ public abstract class RenderCore : IRenderCore private static IShaderSet shaderSet; + private static IShaderSet depthShaderSet; + private static int textureUniformLocation; + private static int depthUniformLocation; + + private static int depthIncrementUniformLocation; + //Create a program for rendering private static uint programUint; @@ -58,9 +66,14 @@ public abstract class RenderCore : IRenderCore /// public uint RenderTextureUint { get; } - public int Width { get; internal set; } = 1920; + /// + /// The ID of the depth texture generated by rendering this render core. + /// + public uint DepthTextureUint { get; } - public int Height { get; internal set; } = 1080; + public virtual int Width { get; internal set; } = 1920; + + public virtual int Height { get; internal set; } = 1080; private static IColour _currentBackColour; private static DepthModes _currentDepthMode = DepthModes.KEEP_DEPTH; @@ -82,6 +95,16 @@ public abstract class RenderCore : IRenderCore /// public virtual DepthModes DepthMode { get; } = DepthModes.KEEP_DEPTH; + /// + /// The scaling mode of this render core. + /// + public virtual ScalingModes ScalingMode { get; } = ScalingModes.NEAREST_NEIGHBOUR; + + /// + /// If this is set to true, the render core will be allowed to be resized through the Resize() function. + /// + public virtual bool CanResize { get; } = true; + /// /// The path of the shaders to use. /// Only accessed during initialisation. @@ -90,7 +113,15 @@ public abstract class RenderCore : IRenderCore public virtual IColour BackColour { get; } = ColourFactory.GetColour(0, 0, 0, 0); - public unsafe RenderCore() + /// + /// How much depth should we add to this render core? + /// + public virtual float DepthAdd { get; } = 0; + + [UsingDependency] + private static IWorldFactory WorldFactory; + + public unsafe RenderCore(IWorld world) : base(world) { if (!CorgEngMain.IsRendering) return; @@ -111,10 +142,24 @@ public unsafe RenderCore() //Set the texture parameters glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - //Bind the render depth buffer to the framebuffer - glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, RenderBufferUint); //Bind the framebuffer to the texture glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, RenderTextureUint, 0); + //glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, Width, Height, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL); + //Bind the render depth buffer to the framebuffer + //glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, RenderBufferUint); + //Bind the framebuffer to the texture + //glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, RenderTextureUint, 0); + //========================= + // Setup the depth texture + //========================= + glActiveTexture(GL_TEXTURE1); + DepthTextureUint = glGenTexture(); + glBindTexture(GL_TEXTURE_2D, DepthTextureUint); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, Width, Height, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, RenderBufferUint); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, DepthTextureUint, 0); //Check for issues if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { @@ -157,7 +202,21 @@ public unsafe static void SetupRendering() glLinkProgram(programUint); //Fetch uniform locations textureUniformLocation = glGetUniformLocation(programUint, "renderTexture"); + depthUniformLocation = glGetUniformLocation(programUint, "depthTexture"); + depthIncrementUniformLocation = glGetUniformLocation(programUint, "depthIncrement"); + } + } + + private static Dictionary loadedShaders = new Dictionary(); + + private IShaderSet GetShader(string name) + { + if (!loadedShaders.ContainsKey(name)) + { + IShaderSet createdShader = ShaderFactory.CreateShaderSet(name); + loadedShaders.Add(name, createdShader); } + return loadedShaders[name]; } public void PreRender() @@ -173,12 +232,13 @@ public void PreRender() /// /// Do rendering /// - public void DoRender() + public void DoRender(Action? preRenderAction = null) { RenderModes prev = SwitchBlendMode(BlendMode); DepthModes prevDepth = SwitchDepthMode(DepthMode); IColour prevColour = SwitchBackColour(BackColour); PreRender(); + preRenderAction?.Invoke(); PerformRender(); SwitchBlendMode(prev); SwitchDepthMode(prevDepth); @@ -251,7 +311,7 @@ public unsafe void Resize(int width, int height) Height = height; //Log Logger?.WriteLine($"Render core resized to {Width}x{Height}", LogType.DEBUG); - if (!CorgEngMain.IsRendering) + if (!CorgEngMain.IsRendering || !CanResize) return; //Update the tex image //Bind the created texture so we can modify it @@ -259,14 +319,22 @@ public unsafe void Resize(int width, int height) //Load the texture scale glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, Width, Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); //Set the texture parameters - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, ScalingMode == ScalingModes.NEAREST_NEIGHBOUR ? GL_NEAREST : GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, ScalingMode == ScalingModes.NEAREST_NEIGHBOUR ? GL_NEAREST : GL_LINEAR); + // Bind the depth texture + glBindTexture(GL_TEXTURE_2D, DepthTextureUint); + //Load the texture scale + glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, Width, Height, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL); + //Set the texture parameters + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, ScalingMode == ScalingModes.NEAREST_NEIGHBOUR ? GL_NEAREST : GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, ScalingMode == ScalingModes.NEAREST_NEIGHBOUR ? GL_NEAREST : GL_LINEAR); } public unsafe void DrawToBuffer(uint buffer, int drawX, int drawY, int bufferWidth, int bufferHeight) { //Reset the framebuffer (We want to draw to the screen, not a frame buffer) glBindFramebuffer(GL_FRAMEBUFFER, buffer); + //glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, RenderBufferUint); //Draw to full screen glViewport(drawX, drawY, bufferWidth, bufferHeight); @@ -278,7 +346,9 @@ public unsafe void DrawToBuffer(uint buffer, int drawX, int drawY, int bufferWid //Set the using program to our program uint glUseProgram(programUint); //Bind uniform variables - glUniform1ui(textureUniformLocation, 0); + glUniform1i(textureUniformLocation, 0); + glUniform1i(depthUniformLocation, 1); + glUniform1f(depthIncrementUniformLocation, DepthAdd); //Bind the vertex buffer glEnableVertexAttribArray(0); glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer); @@ -295,6 +365,10 @@ public unsafe void DrawToBuffer(uint buffer, int drawX, int drawY, int bufferWid //Bind the texture glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, RenderTextureUint); + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_2D, DepthTextureUint); + //glBindRenderbuffer(RenderBufferUint); + //glBindTexture(GL_RENDERBUFFER, RenderBufferUint); //Draw glDrawArrays(GL_TRIANGLES, 0, 6); //Disable the vertex attrib array @@ -304,5 +378,6 @@ public unsafe void DrawToBuffer(uint buffer, int drawX, int drawY, int bufferWid SwitchDepthMode(prevBlend); SwitchBackColour(prevColour); } + } } diff --git a/CorgEng.Core/Rendering/ScalingModes.cs b/CorgEng.Core/Rendering/ScalingModes.cs new file mode 100644 index 00000000..82330f02 --- /dev/null +++ b/CorgEng.Core/Rendering/ScalingModes.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CorgEng.Core.Rendering +{ + public enum ScalingModes + { + NEAREST_NEIGHBOUR, + LINEAR_SCALING, + } +} diff --git a/CorgEng.Debug/CorgEng.Debug.csproj b/CorgEng.Debug/CorgEng.Debug.csproj new file mode 100644 index 00000000..3fc0077e --- /dev/null +++ b/CorgEng.Debug/CorgEng.Debug.csproj @@ -0,0 +1,13 @@ + + + + net7.0 + enable + enable + + + + + + + diff --git a/CorgEng.Debug/RenderCores/DebugOverlayRenderCore.cs b/CorgEng.Debug/RenderCores/DebugOverlayRenderCore.cs new file mode 100644 index 00000000..b0a63d81 --- /dev/null +++ b/CorgEng.Debug/RenderCores/DebugOverlayRenderCore.cs @@ -0,0 +1,28 @@ +using CorgEng.Core.Rendering; +using CorgEng.GenericInterfaces.EntityComponentSystem; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CorgEng.Debug.RenderCores +{ + internal class DebugOverlayRenderCore : RenderCore + { + public DebugOverlayRenderCore(IWorld world) : base(world) + { + } + + public override void Initialize() + { + throw new NotImplementedException(); + } + + public override void PerformRender() + { + throw new NotImplementedException(); + } + + } +} diff --git a/CorgEng.DependencyInjection/Dependencies/NonDependencyAttribute.cs b/CorgEng.DependencyInjection/Dependencies/NonDependencyAttribute.cs new file mode 100644 index 00000000..f20ce4b1 --- /dev/null +++ b/CorgEng.DependencyInjection/Dependencies/NonDependencyAttribute.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CorgEng.DependencyInjection.Dependencies +{ + /// + /// Indicdates that an interface should not be used + /// as a dependency and will cause dependency injection + /// to fail. This is used when an interface that was a dependency + /// before is no longer being used as a dependency. + /// + [AttributeUsage(AttributeTargets.Interface)] + internal class NonDependencyAttribute : Attribute + { + + public bool fail = true; + + public string message; + + } +} diff --git a/CorgEng.EntityComponentSystem/Components/Component.cs b/CorgEng.EntityComponentSystem/Components/Component.cs index f7b649fa..16d78ac8 100644 --- a/CorgEng.EntityComponentSystem/Components/Component.cs +++ b/CorgEng.EntityComponentSystem/Components/Component.cs @@ -1,4 +1,5 @@ using CorgEng.Core.Dependencies; +using CorgEng.EntityComponentSystem.Components.ComponentVariables; using CorgEng.EntityComponentSystem.Entities; using CorgEng.EntityComponentSystem.Events; using CorgEng.EntityComponentSystem.Events.Events; @@ -9,17 +10,22 @@ using CorgEng.GenericInterfaces.UtilityTypes; using System; using System.Collections.Generic; +using System.Linq; +using System.Reflection; using static CorgEng.EntityComponentSystem.Entities.Entity; using static CorgEng.EntityComponentSystem.Systems.EntitySystem; +using static CorgEng.GenericInterfaces.EntityComponentSystem.IEntity; +using static CorgEng.GenericInterfaces.EntityComponentSystem.IEntitySystemManager; namespace CorgEng.EntityComponentSystem.Components { + public abstract class Component : IInstantiatable, IVersionSynced, IComponent { [UsingDependency] private static ILogger Logger = null!; - + /// /// The parent of this component /// @@ -32,29 +38,63 @@ public abstract class Component : IInstantiatable, IVersionSynced, IComponent //TODO: This is very memory expensive as its stored on ALL component instances, when it kind of works per-component. private List componentInjectionLambdas = new List(); + private static Dictionary cvarTypeCache = new Dictionary(); + + public Component() + { + if (!cvarTypeCache.ContainsKey(GetType())) + { + lock (cvarTypeCache) + { + if (!cvarTypeCache.ContainsKey(GetType())) + { + cvarTypeCache.Add(GetType(), GetType() + .GetProperties() + .Where(x => typeof(IComponentVariable).IsAssignableFrom(x.PropertyType)) + .ToArray()); + } + } + } + foreach (PropertyInfo cvarProperty in cvarTypeCache[GetType()]) + { + IComponentVariable attachedCVar = (IComponentVariable)cvarProperty.GetValue(this); + attachedCVar.AssociateTo(this); + } + } + + /// + /// Super cool optimisation + /// + /// + private sealed class CompTemplate + where T : Component + { + internal static readonly T Template = Activator.CreateInstance(); + } + + /// + /// Get the template component so that signals can be registered to its CVars + /// + /// + /// + public static T GetTemplate() + where T : Component + { + return CompTemplate.Template; + } + /// /// Register existing signals when we are added /// to an entity. /// internal void OnComponentAdded(Entity parent) { - //Check if we have any registered signals - if (!EventManager.RegisteredEvents.ContainsKey(GetType())) - { - //Send the component added event - new ComponentAddedEvent(this).Raise(parent); - return; - } //Locate all event types we are listening for - foreach (Type eventType in EventManager.RegisteredEvents[GetType()]) + foreach (Type eventType in parent.world.EntitySystemManager.GetRegisteredEventTypes(GetType())) { EventComponentPair key = new EventComponentPair(eventType, GetType()); //Locate the monitoring system's callback handler - if (!RegisteredSystemSignalHandlers.ContainsKey(key)) - { - continue; - } - List systemEventHandlers = RegisteredSystemSignalHandlers[key]; + IEnumerable systemEventHandlers = parent.world.EntitySystemManager.GetRegisteredSystemEventHandlers(key); //Create a lambda function that injects this component and relays it to the system InternalSignalHandleDelegate componentInjectionLambda = (IEntity entity, IEvent signal, bool synchronous, string callingFile, string callingMember, int callingLine) => { foreach(SystemEventHandlerDelegate systemEventHandler in systemEventHandlers) @@ -90,17 +130,12 @@ internal void OnComponentRemoved(Entity parent) { //Raise component removed event. new ComponentRemovedEvent(this).Raise(parent); - //Check if we have any registered signals - if (!EventManager.RegisteredEvents.ContainsKey(GetType())) - return; //Locate all event types we are listening for - foreach (Type eventType in EventManager.RegisteredEvents[GetType()]) + foreach (Type eventType in parent.world.EntitySystemManager.GetRegisteredEventTypes(GetType())) { EventComponentPair key = new EventComponentPair(eventType, GetType()); //Locate the monitoring system's callback handler - if (!RegisteredSystemSignalHandlers.ContainsKey(key)) - continue; - List systemEventHandlers = RegisteredSystemSignalHandlers[key]; + IEnumerable systemEventHandlers = parent.world.EntitySystemManager.GetRegisteredSystemEventHandlers(key); //Start listening for this event if (parent.EventListeners == null) //Probably shouldn't happen { diff --git a/CorgEng.EntityComponentSystem/Components/ComponentExtensions.cs b/CorgEng.EntityComponentSystem/Components/ComponentSignalInjector.cs similarity index 60% rename from CorgEng.EntityComponentSystem/Components/ComponentExtensions.cs rename to CorgEng.EntityComponentSystem/Components/ComponentSignalInjector.cs index c7620963..6eb51ac7 100644 --- a/CorgEng.EntityComponentSystem/Components/ComponentExtensions.cs +++ b/CorgEng.EntityComponentSystem/Components/ComponentSignalInjector.cs @@ -12,41 +12,36 @@ using System.Threading.Tasks; using static CorgEng.EntityComponentSystem.Entities.Entity; using static CorgEng.EntityComponentSystem.Systems.EntitySystem; +using static CorgEng.GenericInterfaces.EntityComponentSystem.IEntity; +using static CorgEng.GenericInterfaces.EntityComponentSystem.IEntitySystemManager; namespace CorgEng.EntityComponentSystem.Components { - internal static class ComponentExtensions + internal class ComponentSignalInjector : WorldObject, IComponentSignalInjector { [UsingDependency] private static ILogger Logger; - private static ConcurrentDictionary> componentInjectionLambdas = new ConcurrentDictionary>(); + private ConcurrentDictionary> componentInjectionLambdas = new ConcurrentDictionary>(); + + public ComponentSignalInjector(IWorld world) : base(world) + { + } /// /// Register existing signals when we are added /// to an entity. /// - internal static void OnComponentAdded(this IComponent component, Entity parent) + public void OnComponentAdded(IComponent component, IEntity parent) { component.Parent = parent; - //Check if we have any registered signals - if (!EventManager.RegisteredEvents.ContainsKey(component.GetType())) - { - //Send the component added event - new ComponentAddedEvent(component).Raise(parent); - return; - } //Locate all event types we are listening for - foreach (Type eventType in EventManager.RegisteredEvents[component.GetType()]) + foreach (Type eventType in world.EntitySystemManager.GetRegisteredEventTypes(component.GetType())) { EventComponentPair key = new EventComponentPair(eventType, component.GetType()); //Locate the monitoring system's callback handler - if (!RegisteredSystemSignalHandlers.ContainsKey(key)) - { - continue; - } - List systemEventHandlers = RegisteredSystemSignalHandlers[key]; + List systemEventHandlers = world.EntitySystemManager.GetRegisteredSystemEventHandlers(key); //Create a lambda function that injects this component and relays it to the system InternalSignalHandleDelegate componentInjectionLambda = (IEntity entity, IEvent signal, bool synchronous, string callingFile, string callingMember, int callingLine) => { @@ -64,12 +59,7 @@ internal static void OnComponentAdded(this IComponent component, Entity parent) componentInjectionLambdas[component].Add(componentInjectionLambda); } //Start listening for this event - if (parent.EventListeners == null) - parent.EventListeners = new Dictionary>(); - if (parent.EventListeners.ContainsKey(eventType)) - parent.EventListeners[eventType].Add(componentInjectionLambda); - else - parent.EventListeners.Add(eventType, new List() { componentInjectionLambda }); + parent.AddEventListener(eventType, componentInjectionLambda); } //Send the component added event ComponentAddedEvent componentAddedEvent = new ComponentAddedEvent(component); @@ -81,7 +71,7 @@ internal static void OnComponentAdded(this IComponent component, Entity parent) /// Cleanup registered signals and remove any static references to the entity. /// Allow for safe garbage collection /// - internal static void OnComponentRemoved(this IComponent component, Entity parent, bool silent) + public void OnComponentRemoved(IComponent component, IEntity parent, bool silent) { component.Parent = null; //Raise component removed event. @@ -89,36 +79,23 @@ internal static void OnComponentRemoved(this IComponent component, Entity parent { new ComponentRemovedEvent(component).Raise(parent); } - //Check if we have any registered signals - if (!EventManager.RegisteredEvents.ContainsKey(component.GetType())) - return; //Locate all event types we are listening for - foreach (Type eventType in EventManager.RegisteredEvents[component.GetType()]) + foreach (Type eventType in world.EntitySystemManager.GetRegisteredEventTypes(component.GetType())) { EventComponentPair key = new EventComponentPair(eventType, component.GetType()); //Locate the monitoring system's callback handler - if (!RegisteredSystemSignalHandlers.ContainsKey(key)) - continue; - List systemEventHandlers = RegisteredSystemSignalHandlers[key]; - //Start listening for this event - if (parent.EventListeners == null) //Probably shouldn't happen - { - Logger.WriteLine($"Parent event listeners is null.", LogType.WARNING); - continue; - } + List systemEventHandlers = world.EntitySystemManager.GetRegisteredSystemEventHandlers(key); //Locate and removed - if (parent.EventListeners.ContainsKey(eventType)) + if (componentInjectionLambdas.ContainsKey(component)) { lock (componentInjectionLambdas[component]) { for (int i = componentInjectionLambdas[component].Count - 1; i >= 0; i--) { InternalSignalHandleDelegate signalHandleDelegate = componentInjectionLambdas[component][i]; - if (parent.EventListeners[eventType].Contains(signalHandleDelegate)) - { - parent.EventListeners[eventType].Remove(signalHandleDelegate); + parent.RemoveEventListenter(eventType, signalHandleDelegate); + if (componentInjectionLambdas.ContainsKey(component)) componentInjectionLambdas[component].Remove(signalHandleDelegate); - } } if (componentInjectionLambdas[component].Count == 0) { @@ -126,8 +103,6 @@ internal static void OnComponentRemoved(this IComponent component, Entity parent } } } - else - Logger.WriteLine($"Parent event listener isn't listening for {eventType}", LogType.WARNING); } } diff --git a/CorgEng.EntityComponentSystem/Components/ComponentVariables/CVar.cs b/CorgEng.EntityComponentSystem/Components/ComponentVariables/CVar.cs new file mode 100644 index 00000000..6217bfc6 --- /dev/null +++ b/CorgEng.EntityComponentSystem/Components/ComponentVariables/CVar.cs @@ -0,0 +1,42 @@ +using CorgEng.UtilityTypes.BindableProperties; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CorgEng.EntityComponentSystem.Components.ComponentVariables +{ + /// + /// TODO: + /// - Automatic networking update + /// - Updating this variable on the proper threads + /// + /// + public class CVar : BindableProperty, IComponentVariable + { + + public Component Parent { get; set; } + + public CVar() : this(default) + { + } + + public CVar(TValueType value) : base(value) + { + // Determine our component type and prepare signal reaction handlers + } + + public void AssociateTo(Component component) + { + Parent = component; + } + + public CVar InitialValue(TValueType value) + { + Value = value; + return this; + } + + } +} diff --git a/CorgEng.EntityComponentSystem/Components/ComponentVariables/IComponentVariable.cs b/CorgEng.EntityComponentSystem/Components/ComponentVariables/IComponentVariable.cs new file mode 100644 index 00000000..d98293e0 --- /dev/null +++ b/CorgEng.EntityComponentSystem/Components/ComponentVariables/IComponentVariable.cs @@ -0,0 +1,21 @@ +using CorgEng.GenericInterfaces.EntityComponentSystem; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CorgEng.EntityComponentSystem.Components.ComponentVariables +{ + internal interface IComponentVariable + { + + /// + /// Associate this component variable with a specific component. + /// Called via reflection. + /// + /// + void AssociateTo(Component component); + + } +} diff --git a/CorgEng.EntityComponentSystem/Components/ComponentVariables/Networking/INetVar.cs b/CorgEng.EntityComponentSystem/Components/ComponentVariables/Networking/INetVar.cs new file mode 100644 index 00000000..e37f3aa2 --- /dev/null +++ b/CorgEng.EntityComponentSystem/Components/ComponentVariables/Networking/INetVar.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CorgEng.EntityComponentSystem.Components.ComponentVariables.Networking +{ + public interface INetVar + { + + Type GetStoredType(); + + object GetValue(); + + } +} diff --git a/CorgEng.EntityComponentSystem/Components/ComponentVariables/Networking/NetCVar.cs b/CorgEng.EntityComponentSystem/Components/ComponentVariables/Networking/NetCVar.cs new file mode 100644 index 00000000..70a2c617 --- /dev/null +++ b/CorgEng.EntityComponentSystem/Components/ComponentVariables/Networking/NetCVar.cs @@ -0,0 +1,132 @@ +using CorgEng.Core.Dependencies; +using CorgEng.GenericInterfaces.Logging; +using CorgEng.GenericInterfaces.Networking.Clients; +using CorgEng.GenericInterfaces.Networking.Config; +using CorgEng.GenericInterfaces.Networking.Serialisation; +using CorgEng.GenericInterfaces.Serialization; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CorgEng.EntityComponentSystem.Components.ComponentVariables.Networking +{ + + public static class NetVar + { + + [UsingDependency] + internal static INetworkConfig NetworkConfig; + + [UsingDependency] + internal static ILogger Logger; + + [UsingDependency] + internal static IAutoSerialiser AutoSerialiser; + + public static HashSet DirtyNetvars = new HashSet(); + } + + /** + * Networked version of a CVar + */ + public class NetCVar : CVar, INetVar, ICustomSerialisationBehaviour + { + + /// + /// The number of netvars active in the world, start from 1 as 0 represents an + /// uninitialised netvar. + /// + private static ulong _netVarCount = 1; + + public ulong NetVarID { get; private set; } + + public bool PrototypeSerialised { get; set; } + + public NetCVar() + { + if (NetVar.NetworkConfig == null || !NetVar.NetworkConfig.NetworkingActive) + throw new Exception("Attempted to initialise a netvar prior to network initialisation."); + if (!NetVar.NetworkConfig.ProcessServerSystems) + { + if (!NetVar.NetworkConfig.ProcessClientSystems) + throw new Exception("Attempted to initialise a netvar without active networking. This may result in issues."); + return; + } + // Set the NetVarID + NetVarID = _netVarCount++; + } + + public NetCVar(TValueType value) : base(value) + { + if (NetVar.NetworkConfig == null || !NetVar.NetworkConfig.NetworkingActive) + throw new Exception("Attempted to initialise a netvar prior to network initialisation."); + if (!NetVar.NetworkConfig.ProcessServerSystems) + { + if (!NetVar.NetworkConfig.ProcessClientSystems) + throw new Exception("Attempted to initialise a netvar without active networking. This may result in issues."); + return; + } + // Determine our component type and prepare signal reaction handlers + NetVarID = _netVarCount++; + } + + public override void TriggerChanged() + { + base.TriggerChanged(); + if (NetVar.NetworkConfig == null || !NetVar.NetworkConfig.NetworkingActive) + return; + MarkDirty(); + } + + public void MarkDirty() + { + if (NetVarID == 0) + throw new Exception("Attempting to mark an uninitialised NetVar as dirty."); + lock (NetVar.DirtyNetvars) + { + if (NetVar.DirtyNetvars.Contains(this)) + return; + NetVar.DirtyNetvars.Add(this); + } + } + + public NetCVar SetPrototypeSerialised(bool value) + { + PrototypeSerialised = value; + return this; + } + + public object GetValue() + { + if (NetVarID == 0) + throw new Exception("Attempting to access an uninitialised NetVar."); + return Value; + } + + public Type GetStoredType() + { + return typeof(TValueType); + } + + public int GetSerialisationLength() + { + return NetVar.AutoSerialiser.SerialisationLength(typeof(ulong), NetVarID) + + NetVar.AutoSerialiser.SerialisationLength(typeof(TValueType), Value); + } + + public void SerialiseInto(BinaryWriter binaryWriter) + { + NetVar.AutoSerialiser.SerializeInto(typeof(ulong), NetVarID, binaryWriter); + NetVar.AutoSerialiser.SerializeInto(typeof(TValueType), Value, binaryWriter); + } + + public void DeserialiseFrom(BinaryReader binaryReader) + { + NetVarID = (ulong)NetVar.AutoSerialiser.Deserialize(typeof(ulong), binaryReader); + Value = (TValueType)NetVar.AutoSerialiser.Deserialize(typeof(TValueType), binaryReader); + } + } +} diff --git a/CorgEng.EntityComponentSystem/CorgEng.EntityComponentSystem.csproj b/CorgEng.EntityComponentSystem/CorgEng.EntityComponentSystem.csproj index 2a6a070f..fe0ad3ae 100644 --- a/CorgEng.EntityComponentSystem/CorgEng.EntityComponentSystem.csproj +++ b/CorgEng.EntityComponentSystem/CorgEng.EntityComponentSystem.csproj @@ -22,7 +22,6 @@ - diff --git a/CorgEng.EntityComponentSystem/Entities/Entity.cs b/CorgEng.EntityComponentSystem/Entities/Entity.cs index 4ffeb131..06d3a466 100644 --- a/CorgEng.EntityComponentSystem/Entities/Entity.cs +++ b/CorgEng.EntityComponentSystem/Entities/Entity.cs @@ -3,22 +3,23 @@ using CorgEng.EntityComponentSystem.Events; using CorgEng.EntityComponentSystem.Events.Events; using CorgEng.EntityComponentSystem.Implementations.Deletion; +using CorgEng.EntityComponentSystem.Systems; using CorgEng.GenericInterfaces.EntityComponentSystem; using CorgEng.GenericInterfaces.Logging; using CorgEng.GenericInterfaces.UtilityTypes; +using CorgEng.UtilityTypes.Monads; using System; using System.Collections.Generic; using System.Reflection; using System.Threading; +using static CorgEng.GenericInterfaces.EntityComponentSystem.IEntity; namespace CorgEng.EntityComponentSystem.Entities { - public class Entity : IEntity + public class Entity : WorldObject, IEntity { - internal delegate void InternalSignalHandleDelegate(IEntity entity, IEvent signal, bool synchronous, string callingFile, string callingMember, int callingLine); - [UsingDependency] private static ILogger Logger; @@ -51,32 +52,32 @@ public class Entity : IEntity /// Components register themselves to this when needed, and this gets fired off to the component /// which then can fire off itself to the system. /// - internal Dictionary> EventListeners { get; set; } = null; + public Dictionary> EventListeners { get; set; } = null; /// /// Name of the entity definition /// public string DefinitionName { get; set; } - internal Entity() + internal Entity(IWorld world) : base(world) { - Identifier = EntityManager.GetNewEntityId(); - EntityManager.RegisterEntity(this); + Identifier = world.EntityManager.GetNewEntityId(); + world.EntityManager.RegisterEntity(this); //Entities are deletable by default AddComponent(new DeleteableComponent()); } - public Entity(uint identifier) + internal Entity(IWorld world, uint identifier) : base(world) { Identifier = identifier; - EntityManager.RegisterEntity(this); + world.EntityManager.RegisterEntity(this); //Entities are deletable by default AddComponent(new DeleteableComponent()); } ~Entity() { - Interlocked.Increment(ref EntityManager.GarbageCollectionCount); + Interlocked.Increment(ref ((EntityManager)world.EntityManager).GarbageCollectionCount); //Debug //Logger.WriteLine($"Entity GC'd. {EntityManager.GarbageCollectionCount}/{EntityManager.DeletionCount}", LogType.TEMP); } @@ -97,7 +98,7 @@ public void AddComponent(IComponent component) } #endif Components.Add(component); - component.OnComponentAdded(this); + world.ComponentSignalInjector.OnComponentAdded(component, this); } } @@ -109,7 +110,7 @@ public void RemoveComponent(IComponent component, bool networked, bool silent = { lock (Components) { - component.OnComponentRemoved(this, silent); + world.ComponentSignalInjector.OnComponentRemoved(component, this, silent); Components.Remove(component); } } @@ -200,5 +201,38 @@ public bool TryGetComponent(out T component) return false; } + public Result TryGetComponent() + { + //Get derived types too + lock (Components) + { + foreach (IComponent _component in Components) + { + if (_component is T componentAsT) + { + return new Result(componentAsT); + } + } + } + return new Failure(); + } + + public void AddEventListener(Type eventType, InternalSignalHandleDelegate eventHandler) + { + if (EventListeners == null) + EventListeners = new Dictionary>(); + if (EventListeners.ContainsKey(eventType)) + EventListeners[eventType].Add(eventHandler); + else + EventListeners.Add(eventType, new List() { eventHandler }); + } + + public void RemoveEventListenter(Type eventType, InternalSignalHandleDelegate eventHandler) + { + if (EventListeners[eventType].Contains(eventHandler)) + { + EventListeners[eventType].Remove(eventHandler); + } + } } } diff --git a/CorgEng.EntityComponentSystem/Entities/EntityFactory.cs b/CorgEng.EntityComponentSystem/Entities/EntityFactory.cs deleted file mode 100644 index f19ad5f0..00000000 --- a/CorgEng.EntityComponentSystem/Entities/EntityFactory.cs +++ /dev/null @@ -1,34 +0,0 @@ -using CorgEng.DependencyInjection.Dependencies; -using CorgEng.EntityComponentSystem.Events; -using CorgEng.EntityComponentSystem.Events.Events; -using CorgEng.GenericInterfaces.EntityComponentSystem; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace CorgEng.EntityComponentSystem.Entities -{ - [Dependency] - internal class EntityFactory : IEntityFactory - { - - public IEntity CreateEmptyEntity(Action preInitialisationEvents) - { - //Create the blank entity. - IEntity createdEntity = new Entity(); - //Run any events that need to happen before initialisation - preInitialisationEvents?.Invoke(createdEntity); - //Raise the initialise event - new InitialiseEvent().Raise(createdEntity, true); - //Return the entity that was created - return createdEntity; - } - - public IEntity CreateUninitialisedEntity() - { - return new Entity(); - } - } -} diff --git a/CorgEng.EntityComponentSystem/Entities/EntityManager.cs b/CorgEng.EntityComponentSystem/Entities/EntityManager.cs index 595f1af7..45da57c5 100644 --- a/CorgEng.EntityComponentSystem/Entities/EntityManager.cs +++ b/CorgEng.EntityComponentSystem/Entities/EntityManager.cs @@ -12,24 +12,31 @@ namespace CorgEng.EntityComponentSystem.Entities { - public static class EntityManager + public class EntityManager : IEntityManager { [UsingDependency] private static ILogger Logger; - private static IEntity[] entityList = new IEntity[1024]; + private IEntity[] entityList = new IEntity[1024]; - internal static int GarbageCollectionCount = 0; + internal int GarbageCollectionCount = 0; - internal static int DeletionCount = 0; + internal int DeletionCount = 0; /// /// Amount of created entities /// - private static uint CreatedEntityCount = 0; + private uint CreatedEntityCount = 0; - public static void RegisterEntity(IEntity entity) + private IWorld world; + + public EntityManager(IWorld world) + { + this.world = world; + } + + public void RegisterEntity(IEntity entity) { while (entityList.Length <= entity.Identifier) { @@ -40,20 +47,20 @@ public static void RegisterEntity(IEntity entity) } entityList[entity.Identifier] = entity; //Raise a new entity created event - new NewEntityEvent(entity.Identifier).RaiseGlobally(); + new NewEntityEvent(entity.Identifier).RaiseGlobally(world); } - public static uint GetNewEntityId() + public uint GetNewEntityId() { return CreatedEntityCount++; } - public static void RemoveEntity(IEntity entity) + public void RemoveEntity(IEntity entity) { entityList[entity.Identifier] = null; } - public static IEntity GetEntity(uint identifier) + public IEntity GetEntity(uint identifier) { if (identifier < 0 || identifier >= entityList.Length) return null; @@ -70,7 +77,7 @@ public static IEntity GetEntity(uint identifier) /// -> Delete() method /// -> Component local removal + EntityManager removal /// - internal static void Delete(this IEntity entity) + public void InternallyDelete(IEntity entity) { if ((entity.EntityFlags & EntityFlags.DESTROYED) != 0) { @@ -95,10 +102,31 @@ internal static void Delete(this IEntity entity) //Logger.WriteLine($"Entity deletion triggered. {GarbageCollectionCount}/{DeletionCount}", LogType.TEMP); } - public static IEntity[] GetEntityArrayUnsafe() + public IEntity[] GetEntityArrayUnsafe() { return entityList; } + public IEntity CreateEmptyEntity(Action preInitialisationEvents) + { + //Create the blank entity. + IEntity createdEntity = new Entity(world); + //Run any events that need to happen before initialisation + preInitialisationEvents?.Invoke(createdEntity); + //Raise the initialise event + new InitialiseEvent().Raise(createdEntity, true); + //Return the entity that was created + return createdEntity; + } + + public IEntity CreateUninitialisedEntity() + { + return new Entity(world); + } + + public IEntity CreateUninitialisedEntity(uint entityIdentifier) + { + return new Entity(world, entityIdentifier); + } } } diff --git a/CorgEng.EntityComponentSystem/Events/EventExtensions.cs b/CorgEng.EntityComponentSystem/Events/EventExtensions.cs index f2165161..cd061584 100644 --- a/CorgEng.EntityComponentSystem/Events/EventExtensions.cs +++ b/CorgEng.EntityComponentSystem/Events/EventExtensions.cs @@ -1,6 +1,7 @@ using CorgEng.Core.Dependencies; using CorgEng.EntityComponentSystem.Entities; using CorgEng.EntityComponentSystem.Events.Events; +using CorgEng.EntityComponentSystem.WorldManager; using CorgEng.GenericInterfaces.EntityComponentSystem; using CorgEng.GenericInterfaces.Logging; using CorgEng.GenericInterfaces.Networking.Config; @@ -14,6 +15,7 @@ using System.Threading.Tasks; using static CorgEng.EntityComponentSystem.Entities.Entity; using static CorgEng.EntityComponentSystem.Systems.EntitySystem; +using static CorgEng.GenericInterfaces.EntityComponentSystem.IEntitySystemManager; namespace CorgEng.EntityComponentSystem.Events { @@ -54,7 +56,6 @@ public static void Raise( [CallerMemberName] string callingMember = "", [CallerLineNumber] int callingLine = 0) { - Logger.WriteMetric("event_raised", signal.ToString()); //Handle the signal target.HandleSignal(signal, synchronous, callingFile, callingMember, callingLine); //Inform the entity that networked event was raised @@ -69,24 +70,18 @@ public static void Raise( /// public static void RaiseGlobally( this IEvent signal, + IWorld world, bool synchronous = false, [CallerFilePath] string callingFile = "", [CallerMemberName] string callingMember = "", [CallerLineNumber] int callingLine = 0) { - Logger.WriteMetric("global_event_raised", signal.ToString()); - //Check if we have any registered signals - if (!EventManager.RegisteredEvents.ContainsKey(typeof(GlobalEventComponent))) - return; //Locate all event types we are listening for EventComponentPair key = new EventComponentPair(signal.GetType(), typeof(GlobalEventComponent)); //Locate the monitoring system's callback handler - if (RegisteredSystemSignalHandlers.ContainsKey(key)) - { - List systemEventHandlers = RegisteredSystemSignalHandlers[key]; - for (int i = systemEventHandlers.Count - 1; i >= 0; i--) - systemEventHandlers[i].Invoke(null, null, signal, synchronous, callingFile, callingMember, callingLine); - } + List systemEventHandlers = world.EntitySystemManager.GetRegisteredSystemEventHandlers(key); + for (int i = systemEventHandlers.Count - 1; i >= 0; i--) + systemEventHandlers[i].Invoke(null, null, signal, synchronous, callingFile, callingMember, callingLine); } /// @@ -94,6 +89,7 @@ public static void RaiseGlobally( /// public static void RaiseGlobally( this INetworkedEvent signal, + IWorld world, bool sourcedLocally = true, bool synchronous = false, [CallerFilePath] string callingFile = "", @@ -101,18 +97,12 @@ public static void RaiseGlobally( [CallerLineNumber] int callingLine = 0) { Logger.WriteMetric("global_event_raised", signal.ToString()); - //Check if we have any registered signals - if (!EventManager.RegisteredEvents.ContainsKey(typeof(GlobalEventComponent))) - return; //Locate all event types we are listening for EventComponentPair key = new EventComponentPair(signal.GetType(), typeof(GlobalEventComponent)); //Locate the monitoring system's callback handler - if (RegisteredSystemSignalHandlers.ContainsKey(key)) - { - List systemEventHandlers = RegisteredSystemSignalHandlers[key]; - for (int i = systemEventHandlers.Count - 1; i >= 0; i--) - systemEventHandlers[i].Invoke(null, null, signal, synchronous, callingFile, callingMember, callingLine); - } + List systemEventHandlers = world.EntitySystemManager.GetRegisteredSystemEventHandlers(key); + for (int i = systemEventHandlers.Count - 1; i >= 0; i--) + systemEventHandlers[i].Invoke(null, null, signal, synchronous, callingFile, callingMember, callingLine); //Don't relay messages coming from other clients already if (!sourcedLocally) return; @@ -124,11 +114,7 @@ public static void RaiseGlobally( //Locate all event types we are listening for EventComponentPair networkKey = new EventComponentPair(typeof(NetworkedEventRaisedEvent), typeof(GlobalEventComponent)); //Locate the monitoring system's callback handler - if (!RegisteredSystemSignalHandlers.ContainsKey(networkKey)) - { - return; - } - List networkedEventHandlers = RegisteredSystemSignalHandlers[networkKey]; + List networkedEventHandlers = world.EntitySystemManager.GetRegisteredSystemEventHandlers(networkKey); for (int i = networkedEventHandlers.Count - 1; i >= 0; i--) networkedEventHandlers[i].Invoke(null, null, new NetworkedEventRaisedEvent(signal), synchronous, callingFile, callingMember, callingLine); } diff --git a/CorgEng.EntityComponentSystem/Events/EventManager.cs b/CorgEng.EntityComponentSystem/Events/EventManager.cs deleted file mode 100644 index 9c6aa102..00000000 --- a/CorgEng.EntityComponentSystem/Events/EventManager.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace CorgEng.EntityComponentSystem.Events -{ - internal static class EventManager - { - - /// - /// Matches component type to types of registered events - /// - internal static Dictionary> RegisteredEvents = new Dictionary>(); - - } -} diff --git a/CorgEng.EntityComponentSystem/Implementations/Deletion/EntityDeletionSystem.cs b/CorgEng.EntityComponentSystem/Implementations/Deletion/EntityDeletionSystem.cs index 9e7ddfd0..33e4c25f 100644 --- a/CorgEng.EntityComponentSystem/Implementations/Deletion/EntityDeletionSystem.cs +++ b/CorgEng.EntityComponentSystem/Implementations/Deletion/EntityDeletionSystem.cs @@ -17,7 +17,7 @@ internal class EntityDeletionSystem : EntitySystem //entities when requested. public override EntitySystemFlags SystemFlags { get; } = EntitySystemFlags.HOST_SYSTEM | EntitySystemFlags.CLIENT_SYSTEM; - public override void SystemSetup() + public override void SystemSetup(IWorld world) { RegisterLocalEvent(OnEntityDeleted); } @@ -25,7 +25,7 @@ public override void SystemSetup() private void OnEntityDeleted(IEntity entity, DeleteableComponent deleteableComponent, DeleteEntityEvent entityDeletedEvent) { //Delete the entity - entity.Delete(); + world.EntityManager.InternallyDelete(entity); } } diff --git a/CorgEng.EntityComponentSystem/Implementations/Initialisation/EntityInitialisationSystem.cs b/CorgEng.EntityComponentSystem/Implementations/Initialisation/EntityInitialisationSystem.cs index 31902436..793696f6 100644 --- a/CorgEng.EntityComponentSystem/Implementations/Initialisation/EntityInitialisationSystem.cs +++ b/CorgEng.EntityComponentSystem/Implementations/Initialisation/EntityInitialisationSystem.cs @@ -16,7 +16,7 @@ internal class EntityInitialisationSystem : EntitySystem public override EntitySystemFlags SystemFlags => EntitySystemFlags.HOST_SYSTEM | EntitySystemFlags.CLIENT_SYSTEM; - public override void SystemSetup() + public override void SystemSetup(IWorld world) { RegisterLocalEvent(OnInitialisation); } diff --git a/CorgEng.EntityComponentSystem/Implementations/Transform/MoveEvent.cs b/CorgEng.EntityComponentSystem/Implementations/Transform/MoveEvent.cs index 4ebbabb8..43069851 100644 --- a/CorgEng.EntityComponentSystem/Implementations/Transform/MoveEvent.cs +++ b/CorgEng.EntityComponentSystem/Implementations/Transform/MoveEvent.cs @@ -10,6 +10,7 @@ namespace CorgEng.EntityComponentSystem.Implementations.Transform { + [Obsolete("MoveEvent has been replaced by CVar events.", error:false)] public class MoveEvent : INetworkedEvent { diff --git a/CorgEng.EntityComponentSystem/Implementations/Transform/RotationEvent.cs b/CorgEng.EntityComponentSystem/Implementations/Transform/RotationEvent.cs index 43179a86..270f4816 100644 --- a/CorgEng.EntityComponentSystem/Implementations/Transform/RotationEvent.cs +++ b/CorgEng.EntityComponentSystem/Implementations/Transform/RotationEvent.cs @@ -9,7 +9,7 @@ namespace CorgEng.EntityComponentSystem.Implementations.Transform { - internal class RotationEvent : INetworkedEvent + public class RotationEvent : INetworkedEvent { public bool CanBeRaisedByClient => false; diff --git a/CorgEng.EntityComponentSystem/Implementations/Transform/TransformComponent.cs b/CorgEng.EntityComponentSystem/Implementations/Transform/TransformComponent.cs index 954ad2a9..a889fcd7 100644 --- a/CorgEng.EntityComponentSystem/Implementations/Transform/TransformComponent.cs +++ b/CorgEng.EntityComponentSystem/Implementations/Transform/TransformComponent.cs @@ -1,4 +1,5 @@ using CorgEng.EntityComponentSystem.Components; +using CorgEng.EntityComponentSystem.Components.ComponentVariables.Networking; using CorgEng.GenericInterfaces.ContentLoading; using CorgEng.GenericInterfaces.Networking.Attributes; using CorgEng.UtilityTypes.Vectors; @@ -28,13 +29,15 @@ public TransformComponent() : base("_world") /// Start with a zero value /// [NetworkSerialized(prototypeInclude = false)] - public Vector Position { get; internal set; } = new Vector(0, 0); + public NetCVar> Position { get; internal set; } = new NetCVar>(new Vector(0, 0)) + .SetPrototypeSerialised(false); /// /// Rotation property of this transform /// [NetworkSerialized(prototypeInclude = false)] - public Vector Rotation { get; internal set; } = new Vector(0); + public NetCVar> Rotation { get; internal set; } = new NetCVar>(new Vector(0)) + .SetPrototypeSerialised(false); public override TransformComponent Transform => this; diff --git a/CorgEng.EntityComponentSystem/Implementations/Transform/TransformSystem.cs b/CorgEng.EntityComponentSystem/Implementations/Transform/TransformSystem.cs index dcdc31a5..81fddffa 100644 --- a/CorgEng.EntityComponentSystem/Implementations/Transform/TransformSystem.cs +++ b/CorgEng.EntityComponentSystem/Implementations/Transform/TransformSystem.cs @@ -22,13 +22,13 @@ public sealed class TransformSystem : EntitySystem [UsingDependency] private static ILogger Logger; - public override void SystemSetup() + public override void SystemSetup(IWorld world) { RegisterLocalEvent(SetEntityPosition); RegisterLocalEvent(TranslateEntity); RegisterLocalEvent(SetEntityRotation); RegisterLocalEvent((e, c, s) => { - c.Position = s.NewPosition; + c.Position.Value = s.NewPosition; }); } @@ -40,20 +40,20 @@ public override void SystemSetup() /// private void SetEntityRotation(IEntity entity, TransformComponent transform, SetRotationEvent setRotationEvent) { - RotationEvent rotationEvent = new RotationEvent(transform.Rotation, setRotationEvent.NewRotation); + RotationEvent rotationEvent = new RotationEvent(transform.Rotation.Value, setRotationEvent.NewRotation); //Update the rotation of the transform component - transform.Rotation = setRotationEvent.NewRotation; + transform.Rotation.Value = setRotationEvent.NewRotation; //Raise a rotationEvent rotationEvent.Raise(entity); } private void TranslateEntity(IEntity entity, TransformComponent transform, TranslateEvent translationEvent) { - Vector newPosition = transform.Position + translationEvent.TranslationDelta; + Vector newPosition = transform.Position.Value + translationEvent.TranslationDelta; //Create a new on move event - MoveEvent moveEvent = new MoveEvent(transform.Position, newPosition); + MoveEvent moveEvent = new MoveEvent(transform.Position.Value, newPosition); //Update the position of the transform - transform.Position = newPosition; + transform.Position.Value = newPosition; //Trigger the on move event moveEvent.Raise(entity); } @@ -61,9 +61,9 @@ private void TranslateEntity(IEntity entity, TransformComponent transform, Trans private void SetEntityPosition(IEntity entity, TransformComponent transform, SetPositionEvent setPositionEvent) { //Create a new on move event - MoveEvent moveEvent = new MoveEvent(transform.Position, setPositionEvent.NewPosition); + MoveEvent moveEvent = new MoveEvent(transform.Position.Value, setPositionEvent.NewPosition); //Update the position of the transform - transform.Position = setPositionEvent.NewPosition; + transform.Position.Value = setPositionEvent.NewPosition; //Trigger the on move event moveEvent.Raise(entity); } diff --git a/CorgEng.EntityComponentSystem/Systems/EntitySystem.cs b/CorgEng.EntityComponentSystem/Systems/EntitySystem.cs index 01146322..66be1183 100644 --- a/CorgEng.EntityComponentSystem/Systems/EntitySystem.cs +++ b/CorgEng.EntityComponentSystem/Systems/EntitySystem.cs @@ -12,16 +12,23 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; +using System.Diagnostics.Metrics; using System.Linq; using System.Reflection; +using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; +using static CorgEng.GenericInterfaces.EntityComponentSystem.IEntitySystemManager; namespace CorgEng.EntityComponentSystem.Systems { public abstract class EntitySystem { + public static int EntitySystemCount = 0; + + private string name; + /// /// Internal global event component specifically for handling global signals /// @@ -38,36 +45,11 @@ internal class GlobalEventComponent : Component [UsingDependency] protected static INetworkConfig NetworkConfig; - internal delegate void SystemEventHandlerDelegate(IEntity entity, IComponent component, IEvent signal, bool synchronous, string file, string member, int lineNumber); - - /// - /// Matches event and component types to registered signal handlers on systems - /// - internal static Dictionary> RegisteredSystemSignalHandlers { get; } = new Dictionary>(); - - /// - /// Wait handler. This will cause the thread to pause until an event that needs handling is raised. - /// - protected readonly AutoResetEvent waitHandle = new AutoResetEvent(false); - - protected volatile bool isWaiting = false; - - /// - /// A queue of actions that other parts of the code are waiting for the action's completion. - /// These will be execution before anything in the invokation queue. - /// - protected readonly ConcurrentQueue priorityInvokationQueue = new ConcurrentQueue(); - /// /// The invokation queue. A queue of actions that need to be triggered (Raised events) /// protected readonly ConcurrentQueue invokationQueue = new ConcurrentQueue(); - /// - /// A static list of all entity systems in use - /// - private static ConcurrentDictionary EntitySystems = new ConcurrentDictionary(); - /// /// The flags of this system /// @@ -79,117 +61,219 @@ internal class GlobalEventComponent : Component protected bool assassinated = false; /// - /// has setup been completed? + /// The world that we belong to /// - public static bool SetupCompleted = false; + protected IWorld world; /// - /// Actions to run after setup + /// The thread manager flags. /// - public static event Action postSetupAction; + internal ThreadManagerFlags threadManagerFlags; + + private EntitySystemThreadManager threadManager; + + internal bool calledFromProcess = false; public EntitySystem() { - Task task = new Task(SystemThread); - //task.Name = $"{this} thread"; - task.Start(); + name = $"{GetType().Name}-{Interlocked.Increment(ref EntitySystemCount)}"; + } + + public void JoinWorld(IWorld world) + { + this.world = world; //Register the global signal to handle closing the game RegisterGlobalEvent((GameClosedEvent e) => { }); } - public abstract void SystemSetup(); + public void JoinEntitySystemManager(EntitySystemThreadManager threadManager) + { + this.threadManager = threadManager; + // Queue the system for a single fire, in case it is a processing system + QueueProcessing(); + } + + public abstract void SystemSetup(IWorld world); + + /// + /// Amount of runs we should perform before stopping + /// + private int tickRunsRemaining = 0; /// - /// Called when the ECS module is loaded. - /// Creates all System types and tracks the to prevent GC + /// The system thread. Waits until an invokation is required and then triggers it + /// on the system's thread. + /// Returns true if the run was completed, returns false if the system should be + /// requeued for further processing. /// - [ModuleLoad] - private static void CreateAllSystems() + public virtual bool PerformRun(EntitySystemThreadManager threadManager) { - Logger?.WriteLine($"Setting up systems...", LogType.LOG); - //Locate all system types using reflection. - //Note that we need all systems in all loaded modules - IEnumerable locatedSystems = CorgEngMain.LoadedAssemblyModules - .SelectMany(assembly => assembly.GetTypes() - .Where(type => typeof(EntitySystem).IsAssignableFrom(type) && !type.IsAbstract)); - locatedSystems.Select((type) => + // Refresh the tick runs that we need to + if (tickRunsRemaining <= 0) + { + tickRunsRemaining = invokationQueue.Count; + } + while (!invokationQueue.IsEmpty && tickRunsRemaining-- > 0) + { + InvokationAction firstInvokation; + invokationQueue.TryDequeue(out firstInvokation); + if (firstInvokation != null) { - Logger?.WriteLine($"Initializing {type}...", LogType.LOG); - EntitySystem createdSystem = (EntitySystem)type.GetConstructor(BindingFlags.Public | BindingFlags.Instance, null, CallingConventions.HasThis, new Type[0], null).Invoke(new object[0]); - EntitySystems.TryAdd(createdSystem.GetType(), createdSystem); - return createdSystem; - }) - // Very important that we fully resolve the enumerator and don't evaluate it lazilly - .ToList() - // Now do the foreach after they have all been created - .ForEach(entitySystem => entitySystem.SystemSetup()); - SetupCompleted = true; - // Run post-setup actions - postSetupAction?.Invoke(); - postSetupAction = null; - //Trigger the event when this is all done and loaded - CorgEngMain.OnReadyEvents += () => { - new GameReadyEvent().RaiseGlobally(); - }; - Logger?.WriteLine($"Successfully created and setup all systems!", LogType.LOG); + try + { + //Invoke the provided action + firstInvokation.Action(); + } + catch (Exception e) + { + Logger?.WriteLine($"Event Called From: {firstInvokation.CallingMemberName}:{firstInvokation.CallingLineNumber} in {firstInvokation.CallingFile}\n{e}", LogType.ERROR); + } + } + // Check to see if someone else is doing something more important than us + if (CheckRelinquishControl()) + { + return false; + } + } + return invokationQueue.IsEmpty; } + private object _controlLockObject = new object(); + /// - /// Gets a specific entity system + /// Check if we should relinquish control of this system to another source. + /// This will happen if we are a low priority processing thread and a high priority + /// lock has been requested. /// - /// /// - public static T GetSingleton() + internal bool CheckRelinquishControl() { - return (T)(object)EntitySystems[typeof(T)]; + if ((threadManagerFlags & ThreadManagerFlags.REQUESTED_HIGH) == ThreadManagerFlags.REQUESTED_HIGH) + { + // Release our thread manager lock + lock (this) + { + threadManagerFlags &= ~ThreadManagerFlags.LOCKED_LOW; + lock (_controlLockObject) + { + Monitor.PulseAll(_controlLockObject); + } + } + return true; + } + return false; } - [ModuleTerminate] - private static void TerminateSubsystems() + /// + /// Try to acquire a low priority lock for this processing system. + /// Returns false if we were unable to acquire the lock due to this system + /// already processing. + /// + /// + internal bool TryAcquireLowPriorityLock() { - new GameClosedEvent().RaiseGlobally(); + lock (this) + { + if ((threadManagerFlags & (ThreadManagerFlags.LOCKED_HIGH | ThreadManagerFlags.LOCKED_LOW | ThreadManagerFlags.REQUESTED_HIGH)) != 0) + return false; + threadManagerFlags |= ThreadManagerFlags.LOCKED_LOW; + return true; + } } /// - /// The system thread. Waits until an invokation is required and then triggers it - /// on the system's thread. + /// Release the lock currently held by this thread. + /// Ownership of the lock is not enforced, I trust the internal + /// code to actually have ownership of a lock. /// - protected virtual void SystemThread() + public void ReleaseLock() { - while (!CorgEngMain.Terminated && !assassinated) + lock (this) { - //Wait until we are awoken again - if (invokationQueue.Count == 0 && priorityInvokationQueue.Count == 0) + threadManagerFlags &= ~ThreadManagerFlags.LOCKED_LOW; + threadManagerFlags &= ~ThreadManagerFlags.LOCKED_HIGH; + // If we aren't requested, then add ourselves to the processing queue + if ((threadManagerFlags & (ThreadManagerFlags.REQUESTED_HIGH)) == 0) { - isWaiting = true; - //Protection from concurrency dangers - if (invokationQueue.Count == 0 && priorityInvokationQueue.Count == 0) - { - waitHandle.WaitOne(); - } - isWaiting = false; + QueueProcessing(); } - InvokationAction firstInvokation; - if (priorityInvokationQueue.Count != 0) - priorityInvokationQueue.TryDequeue(out firstInvokation); + } + lock (_controlLockObject) + { + Monitor.PulseAll(_controlLockObject); + } + } + + internal void ReleaseInternalLock() + { + lock (this) + { + threadManagerFlags &= ~ThreadManagerFlags.LOCKED_LOW; + } + lock (_controlLockObject) + { + Monitor.PulseAll(_controlLockObject); + } + } + + /// + /// Acquire a high priority lock. This action is blocking + /// until the lock is acquired. + /// Always returns true. + /// + public bool AcquireHighPriorityLock() + { + // Request access control + bool isControlled = false; + lock (this) + { + isControlled = (threadManagerFlags & (ThreadManagerFlags.LOCKED_LOW | ThreadManagerFlags.LOCKED_HIGH)) != 0; + if (isControlled) + threadManagerFlags |= ThreadManagerFlags.REQUESTED_HIGH; else - invokationQueue.TryDequeue(out firstInvokation); - if (firstInvokation != null) + threadManagerFlags |= ThreadManagerFlags.LOCKED_HIGH; + } + while (isControlled) + { + // Wait until the thread manager flags are changed + lock (_controlLockObject) { - try - { - //Invoke the provided action - firstInvokation.Action(); - } - catch (Exception e) + // Add the flag to indicate that we want to claim the system + lock (this) { - Logger?.WriteLine($"Event Called From: {firstInvokation.CallingMemberName}:{firstInvokation.CallingLineNumber} in {firstInvokation.CallingFile}\n{e}", LogType.ERROR); + // Check to see if we are controlled + isControlled = (threadManagerFlags & (ThreadManagerFlags.LOCKED_LOW | ThreadManagerFlags.LOCKED_HIGH)) != 0; + if (!isControlled) + { + // Claim the system and release our request + threadManagerFlags |= ThreadManagerFlags.LOCKED_HIGH; + threadManagerFlags &= ~ThreadManagerFlags.REQUESTED_HIGH; + return true; + } + // Request high priority + threadManagerFlags |= ThreadManagerFlags.REQUESTED_HIGH; } + Monitor.Wait(_controlLockObject); } } - //Terminated - Logger?.WriteLine($"Terminated EntitySystem thread: {this}", LogType.LOG); + // We now have a high priority lock over this system + return true; + } + + /// + /// Queue the entity system to process if it isn't already queued to process. + /// + internal void QueueProcessing() + { + lock (this) + { + // If the system is not queued, queue it. + if ((threadManagerFlags & ThreadManagerFlags.QUEUED_SYSTEM) != 0) + return; + threadManagerFlags |= ThreadManagerFlags.QUEUED_SYSTEM; + threadManager.EnqueueSystemForProcessing(this); + } } /// @@ -200,8 +284,7 @@ public void Kill() //Effective assassinated = true; //Tell the system to process its death - if (isWaiting) - waitHandle.Set(); + QueueProcessing(); } private Dictionary _globalLinkedHandlers = new Dictionary(); @@ -214,61 +297,58 @@ public void Kill() public void RegisterGlobalEvent(Action eventHandler) where GEvent : IEvent { - //Register the component to recieve the target event on the event manager - lock (EventManager.RegisteredEvents) - { - if (!EventManager.RegisteredEvents.ContainsKey(typeof(GlobalEventComponent))) - EventManager.RegisteredEvents.Add(typeof(GlobalEventComponent), new List()); - if (!EventManager.RegisteredEvents[typeof(GlobalEventComponent)].Contains(typeof(GEvent))) - EventManager.RegisteredEvents[typeof(GlobalEventComponent)].Add(typeof(GEvent)); - } + world.EntitySystemManager.RegisterEventType(typeof(GlobalEventComponent), typeof(GEvent)); //Register the system to receieve the event EventComponentPair eventComponentPair = new EventComponentPair(typeof(GEvent), typeof(GlobalEventComponent)); - lock (RegisteredSystemSignalHandlers) + // Everything inside this is an extremely hot path since its the game engine's function call. + // This should be pretty cheap (although it probably isn't very cheap right now). + SystemEventHandlerDelegate createdEventHandler = (IEntity entity, IComponent component, IEvent signal, bool synchronous, string file, string member, int lineNumber) => { - if (!RegisteredSystemSignalHandlers.ContainsKey(eventComponentPair)) - RegisteredSystemSignalHandlers.Add(eventComponentPair, new List()); - SystemEventHandlerDelegate createdEventHandler = (IEntity entity, IComponent component, IEvent signal, bool synchronous, string file, string member, int lineNumber) => + //Check to see if we were processing when the event was fired + if (NetworkConfig != null + && NetworkConfig.NetworkingActive + && ((SystemFlags & EntitySystemFlags.HOST_SYSTEM) == 0 || !NetworkConfig.ProcessServerSystems) + && ((SystemFlags & EntitySystemFlags.CLIENT_SYSTEM) == 0 || !NetworkConfig.ProcessClientSystems)) + return; + // Attempt to gain ownership of the system + // There is a potential issue here: Synchronous event deadlock. + if ((synchronous && AcquireHighPriorityLock()) || ((SystemFlags & EntitySystemFlags.NO_IMMEDIATE_ASYNCHRONOUS_EVENTS) == 0 && TryAcquireLowPriorityLock())) { - ConcurrentQueue targetQueue = synchronous ? priorityInvokationQueue : invokationQueue; - AutoResetEvent synchronousWaitEvent = synchronous ? new AutoResetEvent(false) : null; + // Fire the event on the current thread + try + { + // Now that the current thread has the processing key, we can immediately invoke the requested action + eventHandler.Invoke((GEvent)signal); + } + catch (Exception e) + { + Logger?.WriteLine($"Event Called (and fired immediately) from: {Environment.StackTrace}\n{e}", LogType.ERROR); + } + finally + { + // Ensure that we definitely release this lock + ReleaseLock(); + } + } + else + { + // If this is not a synchronous event, just queue the event + ConcurrentQueue targetQueue = invokationQueue; targetQueue.Enqueue(new InvokationAction(() => { - try - { - //Check if we don't process - if (NetworkConfig != null - && NetworkConfig.NetworkingActive - && ((SystemFlags & EntitySystemFlags.HOST_SYSTEM) == 0 || !NetworkConfig.ProcessServerSystems) - && ((SystemFlags & EntitySystemFlags.CLIENT_SYSTEM) == 0 || !NetworkConfig.ProcessClientSystems)) - return; - eventHandler.Invoke((GEvent)signal); - } - finally - { - //If we were synchronous, indicate that the queuer can continue - if (synchronous) - { - synchronousWaitEvent.Set(); - Thread.Yield(); - } - } + eventHandler.Invoke((GEvent)signal); }, file, member, lineNumber)); //Wake up the system if its sleeping - if (isWaiting) - waitHandle.Set(); - //If this event is synchronous, wait for completion - if (synchronous) - synchronousWaitEvent.WaitOne(); - }; - lock (_globalLinkedHandlers) - { - if (_globalLinkedHandlers.ContainsKey(eventHandler)) - throw new Exception("Attempting to register a global signal handler that is already registered to this system."); - _globalLinkedHandlers.Add(eventHandler, createdEventHandler); + QueueProcessing(); } - RegisteredSystemSignalHandlers[eventComponentPair].Add(createdEventHandler); + }; + lock (_globalLinkedHandlers) + { + if (_globalLinkedHandlers.ContainsKey(eventHandler)) + throw new Exception("Attempting to register a global signal handler that is already registered to this system."); + _globalLinkedHandlers.Add(eventHandler, createdEventHandler); } + world.EntitySystemManager.RegisterSystemEventHandler(eventComponentPair, createdEventHandler); } /// @@ -280,28 +360,16 @@ public void RegisterGlobalEvent(Action eventHandler) public void UnregisterGlobalEvent(Action eventHandler) where GEvent : IEvent { - //Register the component to recieve the target event on the event manager - lock (EventManager.RegisteredEvents) - { - if (!EventManager.RegisteredEvents.ContainsKey(typeof(GlobalEventComponent))) - throw new Exception("Attempted to unregister an event that was not present on the target entity system. (Component is not registered, are you using the right generic types?)"); - if (!EventManager.RegisteredEvents[typeof(GlobalEventComponent)].Contains(typeof(GEvent))) - throw new Exception("Attempted to unregister an event that was not present on the target entity system. (Event was not registered, are you using the right generic types?)"); - } + world.EntitySystemManager.UnregisterEventType(typeof(GlobalEventComponent), typeof(GEvent)); //Register the system to receieve the event EventComponentPair eventComponentPair = new EventComponentPair(typeof(GEvent), typeof(GlobalEventComponent)); - lock (RegisteredSystemSignalHandlers) + //Hnadle unregistering + lock (_globalLinkedHandlers) { - if (!RegisteredSystemSignalHandlers.ContainsKey(eventComponentPair)) - RegisteredSystemSignalHandlers.Add(eventComponentPair, new List()); - //Hnadle unregistering - lock (_globalLinkedHandlers) - { - if (!_globalLinkedHandlers.ContainsKey(eventHandler)) - throw new Exception("Attempting to unregister a global event handler that isn't currently registered."); - RegisteredSystemSignalHandlers[eventComponentPair].Remove(_globalLinkedHandlers[eventHandler]); - _globalLinkedHandlers.Remove(eventHandler); - } + if (!_globalLinkedHandlers.ContainsKey(eventHandler)) + throw new Exception("Attempting to unregister a global event handler that isn't currently registered."); + world.EntitySystemManager.UnregisterSystemEventHandler(eventComponentPair, _globalLinkedHandlers[eventHandler]); + _globalLinkedHandlers.Remove(eventHandler); } } @@ -328,64 +396,57 @@ public void RegisterLocalEvent(Action()); - if (!EventManager.RegisteredEvents[typeToRegister].Contains(typeof(GEvent))) - EventManager.RegisteredEvents[typeToRegister].Add(typeof(GEvent)); - } + world.EntitySystemManager.RegisterEventType(typeToRegister, typeof(GEvent)); //Register the system to receieve the event EventComponentPair eventComponentPair = new EventComponentPair(typeof(GEvent), typeToRegister); - lock (RegisteredSystemSignalHandlers) + //Create and return an event handler so that it can be + createdEventHandler = (IEntity entity, IComponent component, IEvent signal, bool synchronous, string callingFile, string callingMember, int callingLine) => { - if (!RegisteredSystemSignalHandlers.ContainsKey(eventComponentPair)) - RegisteredSystemSignalHandlers.Add(eventComponentPair, new List()); - //Create and return an event handler so that it can be - createdEventHandler = (IEntity entity, IComponent component, IEvent signal, bool synchronous, string callingFile, string callingMember, int callingLine) => + //Check to see if we were processing when the event was fired + if (NetworkConfig != null + && NetworkConfig.NetworkingActive + && ((SystemFlags & EntitySystemFlags.HOST_SYSTEM) == 0 || !NetworkConfig.ProcessServerSystems) + && ((SystemFlags & EntitySystemFlags.CLIENT_SYSTEM) == 0 || !NetworkConfig.ProcessClientSystems)) + return; + // Attempt to gain ownership of the system + // There is a potential issue here: Synchronous event deadlock. + if ((synchronous && AcquireHighPriorityLock()) || ((SystemFlags & EntitySystemFlags.NO_IMMEDIATE_ASYNCHRONOUS_EVENTS) == 0 && TryAcquireLowPriorityLock())) + { + // Fire the event on the current thread + try + { + // Now that the current thread has the processing key, we can immediately invoke the requested action + eventHandler.Invoke(entity, (GComponent)component, (GEvent)signal); + } + catch (Exception e) + { + Logger?.WriteLine($"Event Called (and fired immediately) from: {Environment.StackTrace}\n{e}", LogType.ERROR); + } + finally + { + // Ensure that we definitely release this lock + ReleaseLock(); + } + } + else { - //Check if we don't process - if (NetworkConfig != null - && NetworkConfig.NetworkingActive - && ((SystemFlags & EntitySystemFlags.HOST_SYSTEM) == 0 || !NetworkConfig.ProcessServerSystems) - && ((SystemFlags & EntitySystemFlags.CLIENT_SYSTEM) == 0 || !NetworkConfig.ProcessClientSystems)) - return; - ConcurrentQueue targetQueue = synchronous ? priorityInvokationQueue : invokationQueue; - AutoResetEvent synchronousWaitEvent = synchronous ? new AutoResetEvent(false) : null; + // If this is not a synchronous event, just queue the event + ConcurrentQueue targetQueue = invokationQueue; targetQueue.Enqueue(new InvokationAction(() => { - try - { - eventHandler(entity, (GComponent)component, (GEvent)signal); - } - catch (Exception e) - { - throw new Exception($"An exception occurred handling the signal type {typeof(GEvent)} registered on component {typeof(GComponent)} at system {GetType()}.", e); - } - finally - { - //If we were synchronous, indicate that the queuer can continue - if (synchronous) - { - synchronousWaitEvent.Set(); - } - } + eventHandler.Invoke(entity, (GComponent)component, (GEvent)signal); }, callingFile, callingMember, callingLine)); - if (isWaiting) - waitHandle.Set(); - //If this event is synchronous, wait for completion - if (synchronous) - synchronousWaitEvent.WaitOne(); - }; - lock (_linkedHandlers) - { - if (_linkedHandlers.ContainsKey((typeToRegister, eventHandler))) - throw new Exception("Attempting to register an event that is already registered."); - _linkedHandlers.Add((typeToRegister, eventHandler), createdEventHandler); + // Wake up the system if its sleeping + QueueProcessing(); } - RegisteredSystemSignalHandlers[eventComponentPair].Add(createdEventHandler); + }; + lock (_linkedHandlers) + { + if (_linkedHandlers.ContainsKey((typeToRegister, eventHandler))) + throw new Exception("Attempting to register an event that is already registered."); + _linkedHandlers.Add((typeToRegister, eventHandler), createdEventHandler); } + world.EntitySystemManager.RegisterSystemEventHandler(eventComponentPair, createdEventHandler); } } @@ -409,34 +470,39 @@ public void UnregisterLocalEvent(Action + /// Queues an action for execution, which will be executed on the correct thread for this system. + /// + /// + /// + /// + /// + public void QueueAction(Action action, + [CallerFilePath] string callingFile = "", + [CallerMemberName] string callingMember = "", + [CallerLineNumber] int callingLine = 0) + { + invokationQueue.Enqueue(new InvokationAction(action, callingFile, callingMember, callingLine)); + QueueProcessing(); + } + + public override string ToString() + { + return name; + } } } diff --git a/CorgEng.EntityComponentSystem/Systems/EntitySystemFlags.cs b/CorgEng.EntityComponentSystem/Systems/EntitySystemFlags.cs index 1433a814..df322815 100644 --- a/CorgEng.EntityComponentSystem/Systems/EntitySystemFlags.cs +++ b/CorgEng.EntityComponentSystem/Systems/EntitySystemFlags.cs @@ -12,5 +12,7 @@ public enum EntitySystemFlags HOST_SYSTEM = 1 << 0, //Does the entity system run while the client is running? CLIENT_SYSTEM = 1 << 1, + //Force asynchronous events to be multi-threaded even if we can execute them immediately + NO_IMMEDIATE_ASYNCHRONOUS_EVENTS = 1 << 2, } } diff --git a/CorgEng.EntityComponentSystem/Systems/EntitySystemManager.cs b/CorgEng.EntityComponentSystem/Systems/EntitySystemManager.cs new file mode 100644 index 00000000..e191e46a --- /dev/null +++ b/CorgEng.EntityComponentSystem/Systems/EntitySystemManager.cs @@ -0,0 +1,196 @@ +using CorgEng.Core.Modules; +using CorgEng.Core; +using CorgEng.GenericInterfaces.Logging; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; +using CorgEng.Core.Dependencies; +using CorgEng.EntityComponentSystem.Events; +using CorgEng.EntityComponentSystem.Events.Events; +using CorgEng.GenericInterfaces.EntityComponentSystem; +using static CorgEng.EntityComponentSystem.Systems.EntitySystem; +using static CorgEng.GenericInterfaces.EntityComponentSystem.IEntitySystemManager; + +namespace CorgEng.EntityComponentSystem.Systems +{ + + internal class EntitySystemManager : IEntitySystemManager + { + + [UsingDependency] + protected static ILogger Logger; + + /// + /// A static list of all entity systems in use + /// + public ConcurrentDictionary EntitySystems { get; } + = new ConcurrentDictionary(); + + /// + /// has setup been completed? + /// + public bool SetupCompleted { get; private set; } = false; + + /// + /// Actions to run after setup + /// + public event Action postSetupAction; + + /// + /// Matches component type to types of registered events + /// + internal Dictionary> RegisteredEvents = new Dictionary>(); + + /// + /// Matches event and component types to registered signal handlers on systems + /// + internal Dictionary> RegisteredSystemSignalHandlers { get; } = new Dictionary>(); + + private EntitySystemThreadManager entitySystemThreadManager = new EntitySystemThreadManager(4); + + private IWorld world; + + public EntitySystemManager(IWorld world) + { + this.world = world; + } + + /// + /// Called when the attached world process is created. + /// Creates all System types and tracks the to prevent GC + /// + internal void CreateAllSystems() + { + Logger?.WriteLine($"Setting up systems...", LogType.LOG); + //Locate all system types using reflection. + //Note that we need all systems in all loaded modules + IEnumerable locatedSystems = CorgEngMain.LoadedAssemblyModules + .SelectMany(assembly => assembly.GetTypes() + .Where(type => typeof(EntitySystem).IsAssignableFrom(type) && !type.IsAbstract)); + int setupAmount = 0; + locatedSystems.Select((type) => + { + Logger?.WriteLine($"Initializing {type}...", LogType.LOG); + EntitySystem createdSystem = (EntitySystem)type.GetConstructor(BindingFlags.Public | BindingFlags.Instance, null, CallingConventions.HasThis, new Type[0], null).Invoke(new object[0]); + createdSystem.JoinWorld(world); + createdSystem.JoinEntitySystemManager(entitySystemThreadManager); + EntitySystems.TryAdd(createdSystem.GetType(), createdSystem); + return createdSystem; + }) + // Very important that we fully resolve the enumerator and don't evaluate it lazilly + .ToList() + // Now do the foreach after they have all been created + .ForEach(entitySystem => + { + setupAmount++; + entitySystem.SystemSetup(world); + }); + SetupCompleted = true; + // Run post-setup actions + postSetupAction?.Invoke(); + postSetupAction = null; + //Trigger the event when this is all done and loaded + CorgEngMain.OnReadyEvents += () => + { + new GameReadyEvent().RaiseGlobally(world); + }; + Logger?.WriteLine($"Successfully created and setup {setupAmount} systems!", LogType.LOG); + } + + /// + /// Gets a specific entity system + /// + /// + /// + public T GetSingleton() + { + return (T)(object)EntitySystems[typeof(T)]; + } + + /// + /// Shut down all subsystems that are running inside this entity system + /// manager. + /// + internal void TerminateSubsystems() + { + new GameClosedEvent().RaiseGlobally(world); + } + + public void RegisterEventType(Type componentType, Type eventType) + { + lock (RegisteredEvents) + { + if (!RegisteredEvents.ContainsKey(componentType)) + RegisteredEvents.Add(componentType, new List()); + if (!RegisteredEvents[componentType].Contains(eventType)) + RegisteredEvents[componentType].Add(eventType); + } + } + + //TODO: Does this work? + //TODO: Create unit tests for the expected behaviour of unregister event type. + public void UnregisterEventType(Type componentType, Type eventType) + { + lock (RegisteredEvents) + { + if (!RegisteredEvents.ContainsKey(componentType)) + throw new Exception("Attempted to unregister an event that was not present on the target entity system. (Component is not registered, are you using the right generic types?)"); + if (!RegisteredEvents[componentType].Contains(eventType)) + throw new Exception("Attempted to unregister an event that was not present on the target entity system. (Event was not registered, are you using the right generic types?)"); + } + } + + public IEnumerable GetRegisteredEventTypes(Type componentType) + { + lock (RegisteredEvents) + { + if (RegisteredEvents.ContainsKey(componentType)) + return RegisteredEvents[componentType]; + } + return Enumerable.Empty(); + } + + public void RegisterSystemEventHandler(EventComponentPair registeredEventComponentType, SystemEventHandlerDelegate eventAction) + { + lock (RegisteredSystemSignalHandlers) + { + if (!RegisteredSystemSignalHandlers.ContainsKey(registeredEventComponentType)) + RegisteredSystemSignalHandlers.Add(registeredEventComponentType, new List()); + RegisteredSystemSignalHandlers[registeredEventComponentType].Add(eventAction); + } + } + + public void UnregisterSystemEventHandler(EventComponentPair registeredEventComponentType, SystemEventHandlerDelegate eventAction) + { + lock (RegisteredSystemSignalHandlers) + { + RegisteredSystemSignalHandlers[registeredEventComponentType].Remove(eventAction); + if (RegisteredSystemSignalHandlers[registeredEventComponentType].Count == 0) + { + RegisteredSystemSignalHandlers.Remove(registeredEventComponentType); + } + } + } + + public List GetRegisteredSystemEventHandlers(EventComponentPair registeredEventComponentType) + { + lock (RegisteredSystemSignalHandlers) + { + if (!RegisteredSystemSignalHandlers.ContainsKey(registeredEventComponentType)) + return new List(); + return RegisteredSystemSignalHandlers[registeredEventComponentType]; + } + } + + public void Cleanup() + { + TerminateSubsystems(); + EntitySystems.Clear(); + entitySystemThreadManager.Cleanup(); + } + } +} diff --git a/CorgEng.EntityComponentSystem/Systems/EntitySystemThreadManager.cs b/CorgEng.EntityComponentSystem/Systems/EntitySystemThreadManager.cs new file mode 100644 index 00000000..99ccdc1c --- /dev/null +++ b/CorgEng.EntityComponentSystem/Systems/EntitySystemThreadManager.cs @@ -0,0 +1,201 @@ +using CorgEng.Core; +using CorgEng.Core.Dependencies; +using CorgEng.Core.Modules; +using CorgEng.GenericInterfaces.Logging; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace CorgEng.EntityComponentSystem.Systems +{ + public enum ThreadManagerFlags + { + QUEUED_SYSTEM = 1 << 0, + LOCKED_LOW = 1 << 1, + REQUESTED_HIGH = 1 << 2, + LOCKED_HIGH = 1 << 3, + } + + public class EntitySystemThreadManager + { + + private static ConcurrentBag activeBags = new ConcurrentBag(); + + [UsingDependency] + private static ILogger Logger; + + private EntitySystemThread[] runningWorkers; + + internal bool working = true; + + internal ConcurrentQueue queuedSystems = new ConcurrentQueue(); + + internal ConcurrentQueue sleepingThreads = new ConcurrentQueue(); + + public EntitySystemThreadManager(int workerThreads) + { + if (workerThreads < 1) + { + workerThreads = 1; + } + List threadNumbers = new List(); + runningWorkers = new EntitySystemThread[workerThreads]; + for (int i = 0; i < workerThreads; i++) + { + runningWorkers[i] = new EntitySystemThread(this, i); + threadNumbers.Add("Thread: " + runningWorkers[i].thread.ManagedThreadId); + } + Logger.WriteLine($"Entity system thread manager created with {workerThreads} maximum threads. Thread IDs: {string.Join(", ", threadNumbers)}", LogType.DEBUG); + activeBags.Add(this); + } + + [ModuleTerminate] + public static void ShutdownEntitySystemManagers() + { + lock (activeBags) + { + foreach (EntitySystemThreadManager thread in activeBags) + { + thread.working = false; + } + activeBags.Clear(); + } + } + + public void Cleanup() + { + lock (activeBags) + { + working = false; + } + } + + /// + /// Enqueue a system for processing. + /// + /// + public void EnqueueSystemForProcessing(EntitySystem system) + { + queuedSystems.Enqueue(system); + // Wake up a single thread to handle this + if (sleepingThreads.TryDequeue(out EntitySystemThread result)) + { + result.WakeUp(); + } + } + + public void FireSystemIn(EntitySystem system, double fireTime) + { + CorgEngMain.ExecuteIn(() => { + Logger.WriteLine($"Attempting to queue system {system} for processing fire at {CorgEngMain.Time}", LogType.TEMP); + system.QueueProcessing(); + }, fireTime); + } + + } + + public class EntitySystemThread + { + + [UsingDependency] + private static ILogger Logger; + + public int identifier; + + private bool isRunning = false; + + private EntitySystemThreadManager threadManager; + + internal Thread thread; + + public EntitySystemThread(EntitySystemThreadManager threadManager, int identifier) + { + this.identifier = identifier; + this.threadManager = threadManager; + thread = new Thread(WorkerThread); + thread.Start(); + WakeUp(); + } + + /// + /// Attempt to wakeup the entity system thread. + /// + public void WakeUp() + { + lock (this) + { + if (isRunning) + return; + Monitor.Pulse(this); + isRunning = true; + //Logger.WriteLine($"Entity System Thread {identifier} waking up...", LogType.DEBUG); + } + } + + /// + /// The entity system worker thread. + /// Will process any subsystems that are not currently + /// owned by another process. + /// The thread will terminate once this is completed. + /// + private void WorkerThread() + { + try + { + while (threadManager.working) + { + // Begin working through the systems + while (threadManager.queuedSystems.TryDequeue(out EntitySystem entitySystem)) + { + lock (entitySystem) + { + entitySystem.threadManagerFlags &= ~ThreadManagerFlags.QUEUED_SYSTEM; + if (!entitySystem.TryAcquireLowPriorityLock()) + { + // We will be re-queued when the lock is released. + continue; + } + } + try + { + entitySystem.calledFromProcess = true; + if (!entitySystem.PerformRun(threadManager)) + { + // Requeue the system for further processing + entitySystem.QueueProcessing(); + } + } + // Release our low priority lock at the end without side effects. + finally + { + entitySystem.calledFromProcess = false; + entitySystem.ReleaseInternalLock(); + } + } + // Check if we were requested to wakeup while trying to sleep + if (threadManager.queuedSystems.IsEmpty) + { + lock (this) + { + // We can spin down now + isRunning = false; + threadManager.sleepingThreads.Enqueue(this); + // Wait until we are woken up again + //Logger.WriteLine($"Entity System Thread {identifier} entering sleep mode...", LogType.DEBUG); + Monitor.Wait(this); + } + } + } + } + catch (Exception e) + { + Logger.WriteLine($"FATAL EXCEPTION: THREAD MANAGER CRASHED\n{e}.", LogType.ERROR); + } + } + + } +} diff --git a/CorgEng.EntityComponentSystem/Systems/ProcessingSystem.cs b/CorgEng.EntityComponentSystem/Systems/ProcessingSystem.cs index 3c3b83bd..a3007db2 100644 --- a/CorgEng.EntityComponentSystem/Systems/ProcessingSystem.cs +++ b/CorgEng.EntityComponentSystem/Systems/ProcessingSystem.cs @@ -1,4 +1,6 @@ -using CorgEng.Core; +//#define PROCESSING_SYSTEM_DEBUG + +using CorgEng.Core; using CorgEng.Core.Dependencies; using CorgEng.EntityComponentSystem.Events.Events; using CorgEng.EntityComponentSystem.Implementations.Deletion; @@ -9,6 +11,7 @@ using System.Collections.Generic; using System.Linq; using System.Text; +using System.Threading; using System.Threading.Tasks; namespace CorgEng.EntityComponentSystem.Systems @@ -53,18 +56,66 @@ public abstract class ProcessingSystem : EntitySystem /// public static double DeltaTimeMultiplier { get; set; } = 1; - private ConcurrentQueue WorkingQueue - { - get => priorityInvokationQueue.Count > 0 ? priorityInvokationQueue : invokationQueue; - } + private int tickRunsRemaining = 0; + private bool isRunningProcesses = false; + + private double nextProcessTime; /// - /// Override initial behaviour to also be able to handle processing at regular intervals + /// Override initial behaviour to also be able to handle processing at regular intervals. + /// This works slightly differently as it needs to allow for adequate time to process + /// the things it needs to process while still letting signals pass. /// - protected override void SystemThread() + public override bool PerformRun(EntitySystemThreadManager threadManager) { - while (!CorgEngMain.Terminated && !assassinated) + if (!isRunningProcesses) + { + // Refresh the tick runs that we need to + if (tickRunsRemaining <= 0) + { + tickRunsRemaining = invokationQueue.Count; + } + // Run the processes that we need to + while (invokationQueue.Count > 0 && tickRunsRemaining-- > 0) + { + InvokationAction firstInvokation; + invokationQueue.TryDequeue(out firstInvokation); + if (firstInvokation != null) + { + try + { + //Invoke the provided action + firstInvokation.Action(); + } + catch (Exception e) + { + Logger?.WriteLine($"Event Called From: {firstInvokation.CallingMemberName}:{firstInvokation.CallingLineNumber} in {firstInvokation.CallingFile}\n{e}", LogType.ERROR); + } + } + // Check to see if someone else is doing something more important than us + if (CheckRelinquishControl()) + { + return false; + } + } + // Move to the next step (processes) + isRunningProcesses = true; + } + if (isRunningProcesses) { + // We are not ready to re-fire at this time + if (!continued && nextProcessTime > CorgEngMain.Time) + { + isRunningProcesses = false; +#if PROCESSING_SYSTEM_DEBUG + Logger.WriteLine($"{this} is not ready to fire, fire at: {nextProcessTime}, current time: {CorgEngMain.Time}", LogType.DEBUG); + if (calledFromProcess) + { + Logger.WriteLine($"wtf, we were claled from runningProcess::: {this}", LogType.TEMP); + } +#endif + return invokationQueue.IsEmpty; + } //If we aren't continued, fetch the new queue if (!continued) { @@ -73,6 +124,9 @@ protected override void SystemThread() deltaTime = CorgEngMain.Time - lastFireTime; //Mark the last fire time as now lastFireTime = CorgEngMain.Time; +#if PROCESSING_SYSTEM_DEBUG + Logger.WriteLine($"Firing {ToString()} processing system at {CorgEngMain.Time} (Delay: {ProcessDelay}ms)", LogType.DEBUG); +#endif } //Mark the system as being completed, unless we have to break out early continued = false; @@ -92,47 +146,34 @@ protected override void SystemThread() { Logger.WriteLine(e, LogType.ERROR); } - //Check to see if a new signal that needs to be handled has come in - if (WorkingQueue.Count > 0) + // Check to see if someone else is doing something more important than us + if (CheckRelinquishControl()) { - //We have an invokation to handle, we will continue processing on the next cycle round continued = true; - break; - } - } - //Allocate time to processing signal handles - InvokationAction current; - while (WorkingQueue.TryDequeue(out current)) - { - try - { - current.Action(); - } - catch (Exception e) - { - Logger?.WriteLine($"Event Called From: {current.CallingMemberName}:{current.CallingLineNumber} in {current.CallingFile}\n{e}", LogType.ERROR); + return false; } } - //If we completed processing, wait for the specified time, or until another signal handler comes in - //If a signal comes in when we loop round, we will go straight back to handling signals again - if (!continued) + // Move to the first step again + isRunningProcesses = false; + //Calculate how long we have to wait for, based on the last fire time, our current time + //and the system wait time. + //Stay in sync! if this value is less than 0, we are overtiming. + int waitTime = (int)Math.Max(1000 * (lastFireTime - CorgEngMain.Time) + ProcessDelay, 0); + nextProcessTime = CorgEngMain.Time + (waitTime * 0.001); +#if PROCESSING_SYSTEM_DEBUG + Logger.WriteLine($"{ToString()} queued to fire in {waitTime}ms. It should fire at {nextProcessTime}, it is currently {CorgEngMain.Time}", LogType.DEBUG); +#endif + //If we recieve a signal, loop straight round to processing signal handlers. + //Otherwise, we reached the next processor step, so do processing + if (waitTime > 0) { - isWaiting = true; - //Calculate how long we have to wait for, based on the last fire time, our current time - //and the system wait time. - //Stay in sync! if this value is less than 0, we are overtiming. - int waitTime = (int)Math.Max(1000 * (lastFireTime - CorgEngMain.Time) + ProcessDelay, 0); - //If we recieve a signal, loop straight round to processing signal handlers. - //Otherwise, we reached the next processor step, so do processing - if(waitTime > 0) - continued = waitHandle.WaitOne(waitTime); - else - continued = false; - isWaiting = false; + threadManager.FireSystemIn(this, waitTime); } + // We need to immediately re-fire to process the next tick + else + return false; } - //Terminated - Logger?.WriteLine($"Terminated EntitySystem thread: {this}", LogType.LOG); + return invokationQueue.IsEmpty; } private HashSet registeredDeletionHandlers = new HashSet(); diff --git a/CorgEng.EntityComponentSystem/WorldManager/World.cs b/CorgEng.EntityComponentSystem/WorldManager/World.cs new file mode 100644 index 00000000..d1810a89 --- /dev/null +++ b/CorgEng.EntityComponentSystem/WorldManager/World.cs @@ -0,0 +1,109 @@ +using CorgEng.Core; +using CorgEng.Core.Dependencies; +using CorgEng.EntityComponentSystem.Components; +using CorgEng.EntityComponentSystem.Entities; +using CorgEng.EntityComponentSystem.Systems; +using CorgEng.GenericInterfaces.EntityComponentSystem; +using CorgEng.GenericInterfaces.Logging; +using CorgEng.GenericInterfaces.Networking.Networking.Client; +using CorgEng.GenericInterfaces.Networking.Networking.Server; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CorgEng.EntityComponentSystem.WorldManager +{ + /// + /// Represents an entity group container. + /// Different worlds represent different processes + /// that do not directly interact with each other. + /// Primarilly used for unit testing. + /// + internal class World : IWorld + { + + [UsingDependency] + private static INetworkServerFactory NetworkServerFactory = null!; + + [UsingDependency] + private static INetworkClientFactory NetworkClientFactory = null!; + + [UsingDependency] + private static ILogger Logger = null!; + + /// + /// The entity manager associated with this world process + /// + public IEntityManager EntityManager { get; } + + /// + /// The entity system manager associated with this world process + /// + public IEntitySystemManager EntitySystemManager { get; } + + + /// + /// The server instance, if it doesn't exist for this world, one will be + /// created. + /// + private INetworkServer _serverInstance; + public INetworkServer ServerInstance + { + get + { + if (_serverInstance == null) + { + _serverInstance = NetworkServerFactory.CreateNetworkServer(this); + } + return _serverInstance; + } + } + + /// + /// The client instance, if one doesn't exist for this world, one will be + /// created. + /// + private INetworkClient _clientInstance; + public INetworkClient ClientInstance + { + get + { + if (_clientInstance == null) + _clientInstance = NetworkClientFactory.CreateNetworkClient(this); + return _clientInstance; + } + } + + public IComponentSignalInjector ComponentSignalInjector { get; } + + public World() + { + EntityManager = new EntityManager(this); + ComponentSignalInjector = new ComponentSignalInjector(this); + EntitySystemManager systemManager = new EntitySystemManager(this); + EntitySystemManager = systemManager; + // Do this last + systemManager.CreateAllSystems(); + CorgEngMain.WorldList.Add(this); + Logger.WriteLine("World created and initialised", LogType.LOG); + } + + /// + /// Cleanup the server and client instance when the world goes out of testing scope. + /// + ~World() + { + Cleanup(); + Logger.WriteLine("World went out of scope and was cleaned up.", LogType.DEBUG); + } + + public void Cleanup() + { + _serverInstance?.Cleanup(); + _clientInstance?.Cleanup(); + EntitySystemManager?.Cleanup(); + } + } +} diff --git a/CorgEng.EntityComponentSystem/WorldManager/WorldFactory.cs b/CorgEng.EntityComponentSystem/WorldManager/WorldFactory.cs new file mode 100644 index 00000000..c8ddbb1e --- /dev/null +++ b/CorgEng.EntityComponentSystem/WorldManager/WorldFactory.cs @@ -0,0 +1,21 @@ +using CorgEng.DependencyInjection.Dependencies; +using CorgEng.GenericInterfaces.EntityComponentSystem; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CorgEng.EntityComponentSystem.WorldManager +{ + [Dependency] + internal class WorldFactory : IWorldFactory + { + + public IWorld CreateWorld() + { + return new World(); + } + + } +} diff --git a/CorgEng.EntityQuery/EntityQuery.cs b/CorgEng.EntityQuery/EntityQuery.cs index 898818ed..ffc99898 100644 --- a/CorgEng.EntityQuery/EntityQuery.cs +++ b/CorgEng.EntityQuery/EntityQuery.cs @@ -3,6 +3,7 @@ using CorgEng.EntityComponentSystem.Entities; using CorgEng.EntityComponentSystem.Events.Events; using CorgEng.EntityComponentSystem.Systems; +using CorgEng.GenericInterfaces.EntityComponentSystem; using CorgEng.GenericInterfaces.Logging; using System; using System.Collections.Generic; @@ -13,10 +14,14 @@ namespace CorgEng.EntityQuery { - public class EntityQuery + public class EntityQuery : WorldObject { [UsingDependency] internal static ILogger Logger = default!; + + public EntityQuery(IWorld world) : base(world) + { + } } public class EntityQuery : EntityQuery @@ -41,7 +46,7 @@ public class EntityQuery : EntityQuery /// /// Fetch the query if it already exists /// - public EntityQuery() + public EntityQuery(IWorld world) : base(world) { // Lock the entity query lookup so that we don't have issues with threading. lock (EntityQueryLookup) @@ -57,25 +62,25 @@ public EntityQuery() { // The entity lookup doesn't exist, perform an expensive search over all entities #pragma warning disable CS8619 // Nullability of reference types in value doesn't match target type. - components = EntityManager.GetEntityArrayUnsafe() + components = world.EntityManager.GetEntityArrayUnsafe() .Select(entity => entity?.FindComponent()) .Where(component => component != null) .ToHashSet(); #pragma warning restore CS8619 // Nullability of reference types in value doesn't match target type. if (components.Count > 0) { - EntityQuery.Logger.WriteLine($"Performed an entity query locating {components.Count} components. " + + Logger.WriteLine($"Performed an entity query locating {components.Count} components. " + $"It would be far more efficient to initialise the query at runtime.", LogType.WARNING); } // Force ourselves into the entity query system - EntitySystem.GetSingleton() + world.EntitySystemManager.GetSingleton() .RegisterLocalEvent((entity, component, signal) => { if (component != signal.Component) return; components.Add(component); }); - EntitySystem.GetSingleton() + world.EntitySystemManager.GetSingleton() .RegisterLocalEvent((entity, component, signal) => { if (component != signal.Component) @@ -83,10 +88,10 @@ public EntityQuery() components.Remove(component); }); }; - if (EntitySystem.SetupCompleted) + if (world.EntitySystemManager.SetupCompleted) setupFunction(); else - EntitySystem.postSetupAction += setupFunction; + world.EntitySystemManager.postSetupAction += setupFunction; } } diff --git a/CorgEng.EntityQuery/EntityQuerySystem.cs b/CorgEng.EntityQuery/EntityQuerySystem.cs index 40a26876..6854ba0e 100644 --- a/CorgEng.EntityQuery/EntityQuerySystem.cs +++ b/CorgEng.EntityQuery/EntityQuerySystem.cs @@ -1,5 +1,6 @@ using CorgEng.EntityComponentSystem.Events.Events; using CorgEng.EntityComponentSystem.Systems; +using CorgEng.GenericInterfaces.EntityComponentSystem; using System; using System.Collections.Generic; using System.Linq; @@ -13,7 +14,7 @@ public class EntityQuerySystem : EntitySystem public override EntitySystemFlags SystemFlags => EntitySystemFlags.CLIENT_SYSTEM; - public override void SystemSetup() + public override void SystemSetup(IWorld world) { } } } diff --git a/CorgEng.Example.Server/CorgEng.Example.Server.csproj b/CorgEng.Example.Server/CorgEng.Example.Server.csproj index 37461709..23abb752 100644 --- a/CorgEng.Example.Server/CorgEng.Example.Server.csproj +++ b/CorgEng.Example.Server/CorgEng.Example.Server.csproj @@ -31,7 +31,6 @@ - diff --git a/CorgEng.Example.Server/Program.cs b/CorgEng.Example.Server/Program.cs index e96c09e8..76d6b079 100644 --- a/CorgEng.Example.Server/Program.cs +++ b/CorgEng.Example.Server/Program.cs @@ -44,15 +44,9 @@ class Program [UsingDependency] private static ILogger Logger; - [UsingDependency] - private static INetworkingServer NetworkingServer; - [UsingDependency] private static IPrototypeManager PrototypeManager; - [UsingDependency] - private static IEntityFactory EntityFactory; - #if DEBUG_RENDERING [UsingDependency] private static IIsometricCameraFactory IsometricCameraFactory; @@ -61,10 +55,14 @@ class Program [UsingDependency] private static IIconFactory IconFactory; - [UsingDependency] private static INetworkConfig NetworkConfig; + [UsingDependency] + private static IWorldFactory WorldFactory; + + public static IWorld ServerWorld; + static void Main(string[] args) { //Load the application config @@ -76,9 +74,19 @@ static void Main(string[] args) #else CorgEngMain.Initialize(); - ExampleRenderCore erc = new ExampleRenderCore(); + // Create the program world + ServerWorld = WorldFactory.CreateWorld(); + CorgEngMain.PrimaryWorld = ServerWorld; + + //Start networking server + ServerWorld.ServerInstance.StartHosting(5000); + + //Debug + NetworkConfig.ProcessClientSystems = true; + + ExampleRenderCore erc = new ExampleRenderCore(ServerWorld); CorgEngMain.SetRenderCore(erc); - EntityFactory.CreateEmptyEntity(entity => { + ServerWorld.EntityManager.CreateEmptyEntity(entity => { IIsometricCamera camera = IsometricCameraFactory.CreateCamera(); camera.Width = 30; camera.Height = 30; @@ -90,7 +98,7 @@ static void Main(string[] args) }); // Create a lighting debugger - EntityFactory.CreateEmptyEntity(entity => { + ServerWorld.EntityManager.CreateEmptyEntity(entity => { entity.AddComponent(new TransformComponent()); entity.AddComponent(new FollowCursorComponent()); entity.AddComponent(new SpriteRenderComponent()); @@ -98,17 +106,12 @@ static void Main(string[] args) new SetSpriteEvent(IconFactory.CreateIcon("rock", 5, Constants.RenderingConstants.DEFAULT_RENDERER_PLANE)).Raise(entity); new SetSpriteRendererEvent(1).Raise(entity); }); - - //Debug - NetworkConfig.ProcessClientSystems = true; #endif BuildWorld(); //Set the default player prototype SetPlayerPrototype(); - //Start networking server - NetworkingServer.StartHosting(5000); #if DEBUG_RENDERING //Transfer control of the main thread to the CorgEng @@ -161,7 +164,7 @@ private static void BuildWorld() { for (int yv = Math.Min(start_y, end_y); yv <= Math.Max(start_y, end_y); yv++) { - EntityFactory.CreateEmptyEntity(testingEntity => { + ServerWorld.EntityManager.CreateEmptyEntity(testingEntity => { //Add components testingEntity.AddComponent(new NetworkTransformComponent()); testingEntity.AddComponent(new SpriteRenderComponent()); @@ -178,7 +181,7 @@ private static void BuildWorld() }); //Create a testing entity - EntityFactory.CreateEmptyEntity(testingEntity => { + ServerWorld.EntityManager.CreateEmptyEntity(testingEntity => { //Add components testingEntity.AddComponent(new NetworkTransformComponent()); testingEntity.AddComponent(new SpriteRenderComponent()); @@ -192,13 +195,13 @@ private static void BuildWorld() private static void SetPlayerPrototype() { - EntityFactory.CreateEmptyEntity(playerPrototype => { + ServerWorld.EntityManager.CreateEmptyEntity(playerPrototype => { playerPrototype.AddComponent(new ClientComponent()); playerPrototype.AddComponent(new NetworkTransformComponent()); playerPrototype.AddComponent(new SpriteRenderComponent() { Sprite = IconFactory.CreateIcon("human.ghost", 5, Constants.RenderingConstants.DEFAULT_RENDERER_PLANE), SpriteRendererIdentifier = 1 }); playerPrototype.AddComponent(new PlayerMovementComponent()); IPrototype prototype = PrototypeManager.GetPrototype(playerPrototype); - NetworkingServer.SetClientPrototype(prototype); + ServerWorld.ServerInstance.SetClientPrototype(prototype); new DeleteEntityEvent().Raise(playerPrototype); }); } diff --git a/CorgEng.Example.Shared/Common/Components/Camera/CameraSystem.cs b/CorgEng.Example.Shared/Common/Components/Camera/CameraSystem.cs index 978c9a7e..914c5a8a 100644 --- a/CorgEng.Example.Shared/Common/Components/Camera/CameraSystem.cs +++ b/CorgEng.Example.Shared/Common/Components/Camera/CameraSystem.cs @@ -23,7 +23,7 @@ public class CameraSystem : EntitySystem //Contains camera logic which only the client knows about public override EntitySystemFlags SystemFlags { get; } = EntitySystemFlags.CLIENT_SYSTEM; - public override void SystemSetup() + public override void SystemSetup(IWorld world) { RegisterLocalEvent(OnCameraMoved); RegisterLocalEvent(OnComponentAdded); diff --git a/CorgEng.Example.Shared/Components/FollowMouseComponent/FollowCursorComponent.cs b/CorgEng.Example.Shared/Components/FollowMouseComponent/FollowCursorComponent.cs index afcb77de..cc56f010 100644 --- a/CorgEng.Example.Shared/Components/FollowMouseComponent/FollowCursorComponent.cs +++ b/CorgEng.Example.Shared/Components/FollowMouseComponent/FollowCursorComponent.cs @@ -5,6 +5,7 @@ using CorgEng.EntityComponentSystem.Implementations.Transform; using CorgEng.EntityComponentSystem.Systems; using CorgEng.EntityQuery; +using CorgEng.GenericInterfaces.EntityComponentSystem; using CorgEng.GenericInterfaces.InputHandler; using CorgEng.GenericInterfaces.UtilityTypes; using CorgEng.GenericInterfaces.World; @@ -28,14 +29,15 @@ internal class FollowCursorSystem : EntitySystem private static IInputHandler InputHandler = null!; [UsingDependency] - private static IWorld World; + private static IEntityPositionTracker World; - private static EntityQuery CursorComponents = new EntityQuery(); + private static EntityQuery CursorComponents; public override EntitySystemFlags SystemFlags => EntitySystemFlags.CLIENT_SYSTEM; - public override void SystemSetup() + public override void SystemSetup(IWorld world) { + CursorComponents = new EntityQuery(world); InputHandler.AddMouseMoveBind("cursor_moved"); InputHandler.AddMouseMoveAction("cursor_moved", (deltaTime, mouseX, mouseY) => { Vector worldCoordinates = (Vector)CorgEngMain.MainCamera.ScreenToWorldCoordinates(mouseX * 2 - 1, mouseY * 2 - 1, CorgEngMain.GameWindow.Width, CorgEngMain.GameWindow.Height); diff --git a/CorgEng.Example.Shared/Components/Gravity/SandSystem.cs b/CorgEng.Example.Shared/Components/Gravity/SandSystem.cs index 8100dc3e..5c642898 100644 --- a/CorgEng.Example.Shared/Components/Gravity/SandSystem.cs +++ b/CorgEng.Example.Shared/Components/Gravity/SandSystem.cs @@ -19,7 +19,7 @@ public class SandSystem : ProcessingSystem { [UsingDependency] - private static IWorld WorldAccess = default!; + private static IEntityPositionTracker WorldAccess = default!; public override EntitySystemFlags SystemFlags => EntitySystemFlags.HOST_SYSTEM; @@ -30,7 +30,7 @@ public class SandSystem : ProcessingSystem /// private WorldPathCellQueryer SolidCellQuery = new WorldPathCellQueryer(); - public override void SystemSetup() + public override void SystemSetup(IWorld world) { RegisterLocalEvent(OnSandInitialise); RegisterLocalEvent(OnSandDestroyed); @@ -49,7 +49,7 @@ private void OnSandDestroyed(IEntity entity, SandComponent sandComponent, Delete private void ProcessSand(IEntity sandEntity, SandComponent sandComponent, double deltaTime) { TransformComponent sandTransform = sandEntity.GetComponent(); - Vector nextPosition = new Vector(sandTransform.Position.X, sandTransform.Position.Y - 1); + Vector nextPosition = new Vector(sandTransform.Position.Value.X, sandTransform.Position.Value.Y - 1); if (SolidCellQuery.EnterPositionCost(nextPosition, GenericInterfaces.Core.Direction.SOUTH) > 0) { new SetPositionEvent(nextPosition).Raise(sandEntity); diff --git a/CorgEng.Example.Shared/Components/PlayerMovement/PlayerMovementSystem.cs b/CorgEng.Example.Shared/Components/PlayerMovement/PlayerMovementSystem.cs index e4911a50..2e0544a4 100644 --- a/CorgEng.Example.Shared/Components/PlayerMovement/PlayerMovementSystem.cs +++ b/CorgEng.Example.Shared/Components/PlayerMovement/PlayerMovementSystem.cs @@ -30,7 +30,7 @@ public class PlayerMovementSystem : EntitySystem //Logic executed on the server that moves players when requested. public override EntitySystemFlags SystemFlags { get; } = EntitySystemFlags.HOST_SYSTEM; - public override void SystemSetup() + public override void SystemSetup(IWorld world) { RegisterLocalEvent(OnComponentAdded); RegisterLocalEvent(OnComponentRemoved); diff --git a/CorgEng.Example.Shared/Components/SandFactory/SandFactorySystem.cs b/CorgEng.Example.Shared/Components/SandFactory/SandFactorySystem.cs index 50d5874b..a871e448 100644 --- a/CorgEng.Example.Shared/Components/SandFactory/SandFactorySystem.cs +++ b/CorgEng.Example.Shared/Components/SandFactory/SandFactorySystem.cs @@ -10,6 +10,7 @@ using CorgEng.Lighting.Components; using CorgEng.Networking.Components; using CorgEng.Pathfinding.Components; +using CorgEng.Rendering.ComponentSystem.DepthParallax; using CorgEng.UtilityTypes.Colours; using CorgEng.UtilityTypes.Vectors; using System; @@ -23,9 +24,6 @@ namespace CorgEng.Example.Shared.Components.SandFactory internal class SandFactorySystem : ProcessingSystem { - [UsingDependency] - private static IEntityFactory EntityFactory = default!; - [UsingDependency] private static IIconFactory IconFactory = default!; @@ -35,23 +33,24 @@ internal class SandFactorySystem : ProcessingSystem protected override int ProcessDelay => 2000; - public override void SystemSetup() + public override void SystemSetup(IWorld world) { RegisterLocalEvent((entity, component, signal) => { TransformComponent transform = entity.GetComponent(); RegisterProcess(entity, (entity, component, deltaTime) => { - EntityFactory.CreateEmptyEntity(entity => { + world.EntityManager.CreateEmptyEntity(entity => { //Add components entity.AddComponent(new NetworkTransformComponent()); - entity.AddComponent(new SpriteRenderComponent()); + entity.AddComponent(new DepthSpriteRenderComponent()); + //entity.AddComponent(new SpriteRenderComponent()); entity.AddComponent(new SandComponent()); entity.AddComponent(new SolidComponent()); entity.AddComponent(new LightingComponent() { Colour = new Colour(DebugRandom.NextSingle(), DebugRandom.NextSingle(), DebugRandom.NextSingle(), DebugRandom.NextSingle()), }); //Update the entity - new SetPositionEvent(new Vector(transform.Position.X, transform.Position.Y)).Raise(entity); - new SetSpriteEvent(IconFactory.CreateIcon("sand", 5, Constants.RenderingConstants.DEFAULT_RENDERER_PLANE)).Raise(entity); + new SetPositionEvent(new Vector(transform.Position.Value.X, transform.Position.Value.Y)).Raise(entity); + new SetSpriteEvent(IconFactory.CreateIcon("sand", DebugRandom.Next(10, 300) / 10.0f, Constants.RenderingConstants.DEFAULT_RENDERER_PLANE)).Raise(entity); new SetSpriteRendererEvent(1).Raise(entity); }); }); diff --git a/CorgEng.Example.Shared/CorgEng.Example.Shared.csproj b/CorgEng.Example.Shared/CorgEng.Example.Shared.csproj index 387df563..20252659 100644 --- a/CorgEng.Example.Shared/CorgEng.Example.Shared.csproj +++ b/CorgEng.Example.Shared/CorgEng.Example.Shared.csproj @@ -20,6 +20,7 @@ + diff --git a/CorgEng.Example.Shared/RenderCores/ExampleRenderCore.cs b/CorgEng.Example.Shared/RenderCores/ExampleRenderCore.cs index 5e618a2f..3a92d1db 100644 --- a/CorgEng.Example.Shared/RenderCores/ExampleRenderCore.cs +++ b/CorgEng.Example.Shared/RenderCores/ExampleRenderCore.cs @@ -2,6 +2,7 @@ using CorgEng.Core.Dependencies; using CorgEng.Core.Rendering; using CorgEng.EntityComponentSystem.Entities; +using CorgEng.GenericInterfaces.EntityComponentSystem; using CorgEng.GenericInterfaces.Font.Fonts; using CorgEng.GenericInterfaces.Rendering.Renderers.SpriteRendering; using CorgEng.GenericInterfaces.Rendering.RenderObjects.SpriteRendering; @@ -10,6 +11,7 @@ using CorgEng.GenericInterfaces.UserInterface.Components; using CorgEng.GenericInterfaces.UserInterface.Generators; using CorgEng.Lighting.RenderCores; +using CorgEng.Rendering.DepthParallax; using System; using System.Collections.Generic; using System.Linq; @@ -40,9 +42,16 @@ public class ExampleRenderCore : RenderCore [UsingDependency] public static ITextObjectFactory TextObjectFactory; - public override void Initialize() + private LightingRenderCore lightingRenderCore; + + private ParallaxLayerRenderCore parallaxLayerRenderCore; + + public ExampleRenderCore(IWorld world) : base(world) { + } + public override void Initialize() + { spriteRenderer = SpriteRendererFactory.CreateSpriteRenderer(1); spriteRenderer?.Initialize(); @@ -50,16 +59,20 @@ public override void Initialize() ITextObject textObject = TextObjectFactory.CreateTextObject(spriteRenderer, font, "CorgEng.Font"); textObject.StartRendering(); - LightingRenderCore.Resolve(); - + parallaxLayerRenderCore = new ParallaxLayerRenderCore(world, 1, 30); + parallaxLayerRenderCore?.Initialize(); + lightingRenderCore = new LightingRenderCore(world); + lightingRenderCore.Initialize(); } public override void PerformRender() { spriteRenderer?.Render(CorgEngMain.MainCamera); - LightingRenderCore.Singleton.DoRender(); - LightingRenderCore.Singleton.DrawToBuffer(FrameBufferUint, 0, 0, Width, Height); + parallaxLayerRenderCore.DoRender(); + parallaxLayerRenderCore.DrawToBuffer(FrameBufferUint, 0, 0, Width, Height); + //lightingRenderCore.DoRender(); + //lightingRenderCore.DrawToBuffer(FrameBufferUint, 0, 0, Width, Height); } } } diff --git a/CorgEng.Example/Modules/CameraScroll/CameraScrollSystem.cs b/CorgEng.Example/Modules/CameraScroll/CameraScrollSystem.cs index d4f04bf2..88ae8479 100644 --- a/CorgEng.Example/Modules/CameraScroll/CameraScrollSystem.cs +++ b/CorgEng.Example/Modules/CameraScroll/CameraScrollSystem.cs @@ -1,5 +1,6 @@ using CorgEng.Core; using CorgEng.EntityComponentSystem.Systems; +using CorgEng.GenericInterfaces.EntityComponentSystem; using CorgEng.GenericInterfaces.Rendering.Cameras.Isometric; using CorgEng.InputHandling.Events; using System; @@ -17,7 +18,7 @@ internal class CameraScrollSystem : EntitySystem public override EntitySystemFlags SystemFlags => EntitySystemFlags.CLIENT_SYSTEM; - public override void SystemSetup() + public override void SystemSetup(IWorld world) { //RegisterGlobalEvent(OnMouseScroll); } diff --git a/CorgEng.Example/Program.cs b/CorgEng.Example/Program.cs index a9c1686f..42b4e6fe 100644 --- a/CorgEng.Example/Program.cs +++ b/CorgEng.Example/Program.cs @@ -2,6 +2,7 @@ using CorgEng.Core.Dependencies; using CorgEng.Example.Modules.CameraScroll; using CorgEng.Example.Shared.RenderCores; +using CorgEng.GenericInterfaces.EntityComponentSystem; using CorgEng.GenericInterfaces.Networking.Networking.Client; using CorgEng.GenericInterfaces.Rendering.Cameras.Isometric; using System.Threading; @@ -13,10 +14,10 @@ class Program { [UsingDependency] - private static IIsometricCameraFactory isometricCameraFactory; + private static IIsometricCameraFactory isometricCameraFactory = null!; [UsingDependency] - private static INetworkingClient NetworkingClient; + private static IWorldFactory WorldFactory = null!; static void Main(string[] args) { @@ -27,6 +28,10 @@ static void Main(string[] args) //modules that are dependencies CorgEngMain.Initialize(); + // Create the world + IWorld world = WorldFactory.CreateWorld(); + CorgEngMain.PrimaryWorld = world; + //Set the render core // Prevent failures @@ -36,11 +41,11 @@ static void Main(string[] args) IIsometricCamera camera = isometricCameraFactory.CreateCamera(); CameraScrollSystem.IsometricCamera = camera; - ExampleRenderCore erc = new ExampleRenderCore(); + ExampleRenderCore erc = new ExampleRenderCore(world); CorgEngMain.SetRenderCore(erc); //Connect to our server - NetworkingClient.AttemptConnection("127.0.0.1", 5000); + world.ClientInstance.AttemptConnection("127.0.0.1", 5000); //Create the entity to hold and move the camera /*Entity mainCameraEntity = new Entity(); diff --git a/CorgEng.Functional/CorgEng.Functional.csproj b/CorgEng.Functional/CorgEng.Functional.csproj new file mode 100644 index 00000000..132c02c5 --- /dev/null +++ b/CorgEng.Functional/CorgEng.Functional.csproj @@ -0,0 +1,9 @@ + + + + net6.0 + enable + enable + + + diff --git a/CorgEng.Functional/Monads/Result.cs b/CorgEng.Functional/Monads/Result.cs new file mode 100644 index 00000000..8af271b8 --- /dev/null +++ b/CorgEng.Functional/Monads/Result.cs @@ -0,0 +1,47 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CorgEng.Functional.Monads +{ + public class Result + { + + public virtual bool HasValue { get; } = true; + + private T value; + + public Result(T value) + { + this.value = value; + } + + public Result Fail(Action failureAction) + { + if (HasValue) + return this; + failureAction(); + return this; + } + + public Result Then(Action successAction) + { + if (!HasValue) + return this; + successAction(value); + return this; + } + + } + + public class Failure : Result + { + + public Failure() : base(default) + { } + + public override bool HasValue => false; + } +} diff --git a/CorgEng.GenericInterfaces/AiBehaviours/IBehaviourManagerFactory.cs b/CorgEng.GenericInterfaces/AiBehaviours/IBehaviourManagerFactory.cs index d7e8e8ed..ea91b3d3 100644 --- a/CorgEng.GenericInterfaces/AiBehaviours/IBehaviourManagerFactory.cs +++ b/CorgEng.GenericInterfaces/AiBehaviours/IBehaviourManagerFactory.cs @@ -15,7 +15,7 @@ public interface IBehaviourManagerFactory /// /// /// - IBehaviourManager CreateBehaviourManager(IEntity attachedPawn, params IBehaviourNode[] nodes); + IBehaviourManager CreateBehaviourManager(IWorld world, IEntity attachedPawn, params IBehaviourNode[] nodes); } } diff --git a/CorgEng.GenericInterfaces/ContentLoading/IEntityCreator.cs b/CorgEng.GenericInterfaces/ContentLoading/IEntityCreator.cs index 014d253c..e1daeead 100644 --- a/CorgEng.GenericInterfaces/ContentLoading/IEntityCreator.cs +++ b/CorgEng.GenericInterfaces/ContentLoading/IEntityCreator.cs @@ -18,14 +18,14 @@ public interface IEntityCreator /// /// /// Returns an uninitialised entity. - IEntity CreateEntity(string entityName, Action preInitialisationEvents); + IEntity CreateEntity(IWorld world, string entityName, Action preInitialisationEvents); /// /// Create an object with the specified identifier. /// /// The identifier of the object to create. /// Returns an object created based on the definition file. - object CreateObject(string objectIdentifier); + object CreateObject(IWorld world, string objectIdentifier); } } diff --git a/CorgEng.GenericInterfaces/ContentLoading/IEntityDefinition.cs b/CorgEng.GenericInterfaces/ContentLoading/IEntityDefinition.cs index 6e6fb0f8..3eb2a19f 100644 --- a/CorgEng.GenericInterfaces/ContentLoading/IEntityDefinition.cs +++ b/CorgEng.GenericInterfaces/ContentLoading/IEntityDefinition.cs @@ -14,7 +14,7 @@ public interface IEntityDefinition /// Create the entity /// /// - IEntity CreateEntity(Action setupAction); + IEntity CreateEntity(IWorld world, Action setupAction); } } diff --git a/CorgEng.GenericInterfaces/Core/IRenderCore.cs b/CorgEng.GenericInterfaces/Core/IRenderCore.cs index db9077a4..51020625 100644 --- a/CorgEng.GenericInterfaces/Core/IRenderCore.cs +++ b/CorgEng.GenericInterfaces/Core/IRenderCore.cs @@ -29,9 +29,11 @@ public interface IRenderCore void Initialize(); /// - /// Perform rendering + /// Render this render core to its render texture. /// - void DoRender(); + /// The depth texture of the thing that we are drawing on top of + /// + void DoRender(Action? preRenderAction = null); /// /// Draws the rendered frame buffer to the screen (Framebuffer 0) diff --git a/CorgEng.GenericInterfaces/DependencyInjection/NonDependencyAttribute.cs b/CorgEng.GenericInterfaces/DependencyInjection/NonDependencyAttribute.cs new file mode 100644 index 00000000..d427ee32 --- /dev/null +++ b/CorgEng.GenericInterfaces/DependencyInjection/NonDependencyAttribute.cs @@ -0,0 +1,32 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CorgEng.DependencyInjection.Dependencies +{ + /// + /// Indicdates that an interface should not be used + /// as a dependency and will cause dependency injection + /// to fail. This is used when an interface that was a dependency + /// before is no longer being used as a dependency. + /// + [AttributeUsage(AttributeTargets.Interface)] + internal class NonDependencyAttribute : Attribute + { + + public bool fail = true; + + public string message; + + public NonDependencyAttribute() + { + } + + public NonDependencyAttribute(string message) + { + this.message = message; + } + } +} diff --git a/CorgEng.EntityComponentSystem/Events/EventComponentPair.cs b/CorgEng.GenericInterfaces/EntityComponentSystem/EventComponentPair.cs similarity index 59% rename from CorgEng.EntityComponentSystem/Events/EventComponentPair.cs rename to CorgEng.GenericInterfaces/EntityComponentSystem/EventComponentPair.cs index 37d09918..c623544e 100644 --- a/CorgEng.EntityComponentSystem/Events/EventComponentPair.cs +++ b/CorgEng.GenericInterfaces/EntityComponentSystem/EventComponentPair.cs @@ -1,8 +1,12 @@ using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; -namespace CorgEng.EntityComponentSystem.Events +namespace CorgEng.GenericInterfaces.EntityComponentSystem { - internal struct EventComponentPair + public struct EventComponentPair { public Type EventType { get; } diff --git a/CorgEng.GenericInterfaces/EntityComponentSystem/IComponentSignalInjector.cs b/CorgEng.GenericInterfaces/EntityComponentSystem/IComponentSignalInjector.cs new file mode 100644 index 00000000..bd293991 --- /dev/null +++ b/CorgEng.GenericInterfaces/EntityComponentSystem/IComponentSignalInjector.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CorgEng.GenericInterfaces.EntityComponentSystem +{ + public interface IComponentSignalInjector + { + + void OnComponentAdded(IComponent component, IEntity parent); + + void OnComponentRemoved(IComponent component, IEntity parent, bool silent); + + } +} diff --git a/CorgEng.GenericInterfaces/EntityComponentSystem/IEntity.cs b/CorgEng.GenericInterfaces/EntityComponentSystem/IEntity.cs index b289f29c..138d1e7a 100644 --- a/CorgEng.GenericInterfaces/EntityComponentSystem/IEntity.cs +++ b/CorgEng.GenericInterfaces/EntityComponentSystem/IEntity.cs @@ -11,6 +11,8 @@ namespace CorgEng.GenericInterfaces.EntityComponentSystem public interface IEntity { + public delegate void InternalSignalHandleDelegate(IEntity entity, IEvent signal, bool synchronous, string callingFile, string callingMember, int callingLine); + /// /// The name of the definition used to spawn this entity. /// Null if the entity was created without using EntityCreator. @@ -73,5 +75,20 @@ public interface IEntity /// bool HasComponent(); + /// + /// Add an event listener to a specific type + /// + /// + /// + void AddEventListener(Type eventType, InternalSignalHandleDelegate eventHandler); + + /// + /// Remove an event listener from a provided event type. + /// + /// + /// + /// + void RemoveEventListenter(Type eventType, InternalSignalHandleDelegate eventHandler); + } } diff --git a/CorgEng.GenericInterfaces/EntityComponentSystem/IEntityFactory.cs b/CorgEng.GenericInterfaces/EntityComponentSystem/IEntityFactory.cs deleted file mode 100644 index 07bd9da7..00000000 --- a/CorgEng.GenericInterfaces/EntityComponentSystem/IEntityFactory.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace CorgEng.GenericInterfaces.EntityComponentSystem -{ - public interface IEntityFactory - { - - /// - /// Creates an empty entity with no components - /// - /// - IEntity CreateEmptyEntity(Action preInitialisationEvents); - - /// - /// Creates an empty entity with no components - /// - /// - IEntity CreateUninitialisedEntity(); - - } -} diff --git a/CorgEng.GenericInterfaces/EntityComponentSystem/IEntityManager.cs b/CorgEng.GenericInterfaces/EntityComponentSystem/IEntityManager.cs new file mode 100644 index 00000000..829f3f12 --- /dev/null +++ b/CorgEng.GenericInterfaces/EntityComponentSystem/IEntityManager.cs @@ -0,0 +1,34 @@ +using CorgEng.EntityComponentSystem.Entities; +using CorgEng.GenericInterfaces.Logging; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace CorgEng.GenericInterfaces.EntityComponentSystem +{ + public interface IEntityManager + { + + void RegisterEntity(IEntity entity); + + uint GetNewEntityId(); + + void RemoveEntity(IEntity entity); + + void InternallyDelete(IEntity entity); + + IEntity GetEntity(uint identifier); + + IEntity[] GetEntityArrayUnsafe(); + + IEntity CreateEmptyEntity(Action preInitialisationEvents); + + IEntity CreateUninitialisedEntity(); + + IEntity CreateUninitialisedEntity(uint entityIdentifier); + + } +} diff --git a/CorgEng.GenericInterfaces/EntityComponentSystem/IEntitySystemManager.cs b/CorgEng.GenericInterfaces/EntityComponentSystem/IEntitySystemManager.cs new file mode 100644 index 00000000..80e6043b --- /dev/null +++ b/CorgEng.GenericInterfaces/EntityComponentSystem/IEntitySystemManager.cs @@ -0,0 +1,98 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CorgEng.GenericInterfaces.EntityComponentSystem +{ + public interface IEntitySystemManager + { + + /// + /// The delegate used for handling system events. + /// + /// The entity target of the signal handler. + /// The component that is affected by the raised signal. + /// The signal that was raised. + /// Boolean value which represents if the event was raised synchronously. Synchronous events + /// will halt the curren threads execution until it is handled. + /// The name of the file that the event was called from. + /// The name of the member that raised the event. + /// The line number that the event was raised on. + public delegate void SystemEventHandlerDelegate(IEntity entity, IComponent component, IEvent signal, bool synchronous, string file, string member, int lineNumber); + + /// + /// A boolean value representing wether or not the entity systems have been + /// setup or not. + /// + bool SetupCompleted { get; } + + /// + /// An event that is raised once the entity systems are registered and have had + /// their SetupSystem function called. This will only ever be called once in normal + /// operations, once it has been called adding to this event will be pointless as + /// it will never be called again. + /// + event Action postSetupAction; + + /// + /// Get the singleton subsystem with the type T, + /// running within this world instance. + /// + /// + /// + T GetSingleton(); + + /// + /// Register an event with a specific type + /// + void RegisterEventType(Type componentType, Type eventType); + + /// + /// Unregister an event with a given type for a given component. + /// + /// + /// + void UnregisterEventType(Type componentType, Type eventType); + + /// + /// Returns the enumable set of types registered to the + /// provided component type. + /// + /// + /// + IEnumerable GetRegisteredEventTypes(Type componentType); + + /// + /// Register a system event handler to this system manager group. When an event + /// is raised on an object that contains a component of the types specified in the + /// EventComponentPair, then the provided SystemEventHandlerDelegate will be raised. + /// + /// + /// + void RegisterSystemEventHandler(EventComponentPair registeredEventComponentType, SystemEventHandlerDelegate eventAction); + + /// + /// Unregister a system event handler attached to this system group. See RegisterSystemEventHandler + /// for details. + /// + /// + /// + void UnregisterSystemEventHandler(EventComponentPair registeredEventComponentType, SystemEventHandlerDelegate eventAction); + + /// + /// Get an enumerable set of the registered system event handlers registered to a + /// specified event component pair. + /// + /// + List GetRegisteredSystemEventHandlers(EventComponentPair registeredEventComponentType); + + /** + * Shutdown and cleanup + */ + void Cleanup(); + + } +} diff --git a/CorgEng.GenericInterfaces/EntityComponentSystem/IWorld.cs b/CorgEng.GenericInterfaces/EntityComponentSystem/IWorld.cs new file mode 100644 index 00000000..35d72615 --- /dev/null +++ b/CorgEng.GenericInterfaces/EntityComponentSystem/IWorld.cs @@ -0,0 +1,27 @@ +using CorgEng.GenericInterfaces.Networking.Networking.Client; +using CorgEng.GenericInterfaces.Networking.Networking.Server; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CorgEng.GenericInterfaces.EntityComponentSystem +{ + public interface IWorld + { + + IEntityManager EntityManager { get; } + + IEntitySystemManager EntitySystemManager { get; } + + IComponentSignalInjector ComponentSignalInjector { get; } + + INetworkServer ServerInstance { get; } + + INetworkClient ClientInstance { get; } + + void Cleanup(); + + } +} diff --git a/CorgEng.GenericInterfaces/EntityComponentSystem/IWorldFactory.cs b/CorgEng.GenericInterfaces/EntityComponentSystem/IWorldFactory.cs new file mode 100644 index 00000000..163c5750 --- /dev/null +++ b/CorgEng.GenericInterfaces/EntityComponentSystem/IWorldFactory.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CorgEng.GenericInterfaces.EntityComponentSystem +{ + public interface IWorldFactory + { + + IWorld CreateWorld(); + + } +} diff --git a/CorgEng.GenericInterfaces/EntityComponentSystem/WorldObject.cs b/CorgEng.GenericInterfaces/EntityComponentSystem/WorldObject.cs new file mode 100644 index 00000000..4615939b --- /dev/null +++ b/CorgEng.GenericInterfaces/EntityComponentSystem/WorldObject.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CorgEng.GenericInterfaces.EntityComponentSystem +{ + public class WorldObject + { + + public IWorld world { get; } + + public WorldObject(IWorld world) + { + this.world = world; + } + + } +} diff --git a/CorgEng.GenericInterfaces/World/IContentsHolder.cs b/CorgEng.GenericInterfaces/EntityPositionTracker/IContentsHolder.cs similarity index 100% rename from CorgEng.GenericInterfaces/World/IContentsHolder.cs rename to CorgEng.GenericInterfaces/EntityPositionTracker/IContentsHolder.cs diff --git a/CorgEng.GenericInterfaces/World/IWorld.cs b/CorgEng.GenericInterfaces/EntityPositionTracker/IEntityPositionTracker.cs similarity index 99% rename from CorgEng.GenericInterfaces/World/IWorld.cs rename to CorgEng.GenericInterfaces/EntityPositionTracker/IEntityPositionTracker.cs index fb428cfb..dbcb6174 100644 --- a/CorgEng.GenericInterfaces/World/IWorld.cs +++ b/CorgEng.GenericInterfaces/EntityPositionTracker/IEntityPositionTracker.cs @@ -9,7 +9,7 @@ namespace CorgEng.GenericInterfaces.World { - public interface IWorld + public interface IEntityPositionTracker { /// diff --git a/CorgEng.GenericInterfaces/World/IWorldTrackComponent.cs b/CorgEng.GenericInterfaces/EntityPositionTracker/IWorldTrackComponent.cs similarity index 100% rename from CorgEng.GenericInterfaces/World/IWorldTrackComponent.cs rename to CorgEng.GenericInterfaces/EntityPositionTracker/IWorldTrackComponent.cs diff --git a/CorgEng.GenericInterfaces/Networking/Networking/Client/INetworkingClient.cs b/CorgEng.GenericInterfaces/Networking/Networking/Client/INetworkClient.cs similarity index 97% rename from CorgEng.GenericInterfaces/Networking/Networking/Client/INetworkingClient.cs rename to CorgEng.GenericInterfaces/Networking/Networking/Client/INetworkClient.cs index a9e458cb..49a8aa85 100644 --- a/CorgEng.GenericInterfaces/Networking/Networking/Client/INetworkingClient.cs +++ b/CorgEng.GenericInterfaces/Networking/Networking/Client/INetworkClient.cs @@ -14,7 +14,7 @@ namespace CorgEng.GenericInterfaces.Networking.Networking.Client public delegate void ConnectionFailed(IPAddress ipAddress, DisconnectReason disconnectReason, string reasonText); - public interface INetworkingClient + public interface INetworkClient { /// diff --git a/CorgEng.GenericInterfaces/Networking/Networking/Client/INetworkClientFactory.cs b/CorgEng.GenericInterfaces/Networking/Networking/Client/INetworkClientFactory.cs new file mode 100644 index 00000000..93e4bbea --- /dev/null +++ b/CorgEng.GenericInterfaces/Networking/Networking/Client/INetworkClientFactory.cs @@ -0,0 +1,16 @@ +using CorgEng.GenericInterfaces.EntityComponentSystem; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CorgEng.GenericInterfaces.Networking.Networking.Client +{ + public interface INetworkClientFactory + { + + INetworkClient CreateNetworkClient(IWorld world); + + } +} diff --git a/CorgEng.GenericInterfaces/Networking/Networking/Server/IEntityCommunicatorFactory.cs b/CorgEng.GenericInterfaces/Networking/Networking/Server/IEntityCommunicatorFactory.cs new file mode 100644 index 00000000..7e2054aa --- /dev/null +++ b/CorgEng.GenericInterfaces/Networking/Networking/Server/IEntityCommunicatorFactory.cs @@ -0,0 +1,16 @@ +using CorgEng.GenericInterfaces.EntityComponentSystem; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CorgEng.GenericInterfaces.Networking.Networking.Server +{ + public interface IEntityCommunicatorFactory + { + + IEntityCommunicator CreateEntityCommunicator(IWorld world); + + } +} diff --git a/CorgEng.GenericInterfaces/Networking/Networking/Server/INetworkingServer.cs b/CorgEng.GenericInterfaces/Networking/Networking/Server/INetworkServer.cs similarity index 97% rename from CorgEng.GenericInterfaces/Networking/Networking/Server/INetworkingServer.cs rename to CorgEng.GenericInterfaces/Networking/Networking/Server/INetworkServer.cs index 226ddbc7..b2d07e57 100644 --- a/CorgEng.GenericInterfaces/Networking/Networking/Server/INetworkingServer.cs +++ b/CorgEng.GenericInterfaces/Networking/Networking/Server/INetworkServer.cs @@ -9,7 +9,7 @@ namespace CorgEng.GenericInterfaces.Networking.Networking.Server { - public interface INetworkingServer + public interface INetworkServer { /// diff --git a/CorgEng.GenericInterfaces/Networking/Networking/Server/INetworkServerFactory.cs b/CorgEng.GenericInterfaces/Networking/Networking/Server/INetworkServerFactory.cs new file mode 100644 index 00000000..4fbe4b6f --- /dev/null +++ b/CorgEng.GenericInterfaces/Networking/Networking/Server/INetworkServerFactory.cs @@ -0,0 +1,16 @@ +using CorgEng.GenericInterfaces.EntityComponentSystem; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CorgEng.GenericInterfaces.Networking.Networking.Server +{ + public interface INetworkServerFactory + { + + INetworkServer CreateNetworkServer(IWorld world); + + } +} diff --git a/CorgEng.GenericInterfaces/Networking/Packets/PacketHeaders.cs b/CorgEng.GenericInterfaces/Networking/Packets/PacketHeaders.cs index 7153d982..d3168f9b 100644 --- a/CorgEng.GenericInterfaces/Networking/Packets/PacketHeaders.cs +++ b/CorgEng.GenericInterfaces/Networking/Packets/PacketHeaders.cs @@ -37,5 +37,7 @@ public enum PacketHeaders PING_RESPONSE, //Request information about an entity REQUEST_ENTITY, + //Update a netvar value + NETVAR_VALUE_UPDATED, } } diff --git a/CorgEng.GenericInterfaces/Networking/PrototypeManager/IPrototype.cs b/CorgEng.GenericInterfaces/Networking/PrototypeManager/IPrototype.cs index 92b94009..7c2889dd 100644 --- a/CorgEng.GenericInterfaces/Networking/PrototypeManager/IPrototype.cs +++ b/CorgEng.GenericInterfaces/Networking/PrototypeManager/IPrototype.cs @@ -28,13 +28,13 @@ public interface IPrototype /// Create a new entity from a prototype, with a new and unique identifier. /// /// - IEntity CreateEntityFromPrototype(); + IEntity CreateEntityFromPrototype(IWorld world); /// /// Create an existing entity based on the information in this prototype /// /// An instantiated entity based on the contents of this prototype. - IEntity CreateEntityFromPrototype(uint entityIdentifier); + IEntity CreateEntityFromPrototype(IWorld world, uint entityIdentifier); /// /// Serializes the prototype into a byte array, allowing it to be transmitted through the networking. diff --git a/CorgEng.GenericInterfaces/UserInterface/Components/IUserInterfaceComponent.cs b/CorgEng.GenericInterfaces/UserInterface/Components/IUserInterfaceComponent.cs index 99cf1e25..9da41708 100644 --- a/CorgEng.GenericInterfaces/UserInterface/Components/IUserInterfaceComponent.cs +++ b/CorgEng.GenericInterfaces/UserInterface/Components/IUserInterfaceComponent.cs @@ -71,6 +71,11 @@ public interface IUserInterfaceComponent /// double BottomOffset { get; } + /// + /// The world relevant to this user interface component + /// + IWorld World { get; } + /// /// Raw list of children /// diff --git a/CorgEng.GenericInterfaces/UserInterface/Components/IUserInterfaceComponentFactory.cs b/CorgEng.GenericInterfaces/UserInterface/Components/IUserInterfaceComponentFactory.cs index d274598c..98402cd5 100644 --- a/CorgEng.GenericInterfaces/UserInterface/Components/IUserInterfaceComponentFactory.cs +++ b/CorgEng.GenericInterfaces/UserInterface/Components/IUserInterfaceComponentFactory.cs @@ -1,4 +1,5 @@ -using CorgEng.GenericInterfaces.UserInterface.Anchors; +using CorgEng.GenericInterfaces.EntityComponentSystem; +using CorgEng.GenericInterfaces.UserInterface.Anchors; using System; using System.Collections.Generic; using System.Linq; @@ -16,7 +17,7 @@ public interface IUserInterfaceComponentFactory /// The parent of this component. /// The anchor of this component. Defines where the user interface component will be rendered relative to the parent. /// The created blank user interface component. - IUserInterfaceComponent CreateGenericUserInterfaceComponent(IUserInterfaceComponent parent, IAnchor anchorDetails, Action preInitialiseAction); + IUserInterfaceComponent CreateGenericUserInterfaceComponent(IWorld world, IUserInterfaceComponent parent, IAnchor anchorDetails, Action preInitialiseAction); /// /// Creates a generic user interface component with no parent. @@ -24,7 +25,7 @@ public interface IUserInterfaceComponentFactory /// /// The anchor details of this component. /// The created blank user interface component. - IUserInterfaceComponent CreateGenericUserInterfaceComponent(IAnchor anchorDetails, Action preInitialiseAction); + IUserInterfaceComponent CreateGenericUserInterfaceComponent(IWorld world, IAnchor anchorDetails, Action preInitialiseAction); /// /// @@ -34,7 +35,7 @@ public interface IUserInterfaceComponentFactory /// /// /// - IUserInterfaceComponent CreateUserInterfaceComponent(string componentType, IUserInterfaceComponent parent, IAnchor anchorDetails, IDictionary arguments, Action preInitialiseAction); + IUserInterfaceComponent CreateUserInterfaceComponent(IWorld world, string componentType, IUserInterfaceComponent parent, IAnchor anchorDetails, IDictionary arguments, Action preInitialiseAction); /// /// @@ -43,7 +44,7 @@ public interface IUserInterfaceComponentFactory /// /// /// - IUserInterfaceComponent CreateUserInterfaceComponent(string componentType, IAnchor anchorDetails, IDictionary arguments, Action preInitialiseAction); + IUserInterfaceComponent CreateUserInterfaceComponent(IWorld world, string componentType, IAnchor anchorDetails, IDictionary arguments, Action preInitialiseAction); } } diff --git a/CorgEng.GenericInterfaces/UserInterface/Generators/IUserInterfaceXmlLoader.cs b/CorgEng.GenericInterfaces/UserInterface/Generators/IUserInterfaceXmlLoader.cs index fb395431..85f177b1 100644 --- a/CorgEng.GenericInterfaces/UserInterface/Generators/IUserInterfaceXmlLoader.cs +++ b/CorgEng.GenericInterfaces/UserInterface/Generators/IUserInterfaceXmlLoader.cs @@ -1,4 +1,5 @@ -using CorgEng.GenericInterfaces.UserInterface.Components; +using CorgEng.GenericInterfaces.EntityComponentSystem; +using CorgEng.GenericInterfaces.UserInterface.Components; using System; using System.Collections.Generic; using System.Linq; @@ -15,7 +16,7 @@ public interface IUserInterfaceXmlLoader /// /// The path to the XML file to load the user interface from. /// Returns the root component of the user interface. - IUserInterfaceComponent LoadUserInterface(string filepath); + IUserInterfaceComponent LoadUserInterface(IWorld world, string filepath); } } diff --git a/CorgEng.GenericInterfaces/UserInterface/Popups/IPopupManager.cs b/CorgEng.GenericInterfaces/UserInterface/Popups/IPopupManager.cs index 7a763479..09f8fccd 100644 --- a/CorgEng.GenericInterfaces/UserInterface/Popups/IPopupManager.cs +++ b/CorgEng.GenericInterfaces/UserInterface/Popups/IPopupManager.cs @@ -1,4 +1,5 @@ -using CorgEng.GenericInterfaces.UserInterface.Components; +using CorgEng.DependencyInjection.Dependencies; +using CorgEng.GenericInterfaces.UserInterface.Components; using System; using System.Collections.Generic; using System.Linq; @@ -7,6 +8,7 @@ namespace CorgEng.GenericInterfaces.UserInterface.Popups { + [NonDependency("IPopupManager is no longer used as a dependency and should be accessed from IWorld instead.")] public interface IPopupManager { diff --git a/CorgEng.GenericInterfaces/UtilityTypes/IBindableProperty.cs b/CorgEng.GenericInterfaces/UtilityTypes/IBindableProperty.cs index a7e59c0d..7cdc1ad5 100644 --- a/CorgEng.GenericInterfaces/UtilityTypes/IBindableProperty.cs +++ b/CorgEng.GenericInterfaces/UtilityTypes/IBindableProperty.cs @@ -2,12 +2,9 @@ namespace CorgEng.GenericInterfaces.UtilityTypes { - public interface IBindableProperty + public interface IBindableProperty : IListenable { - //Callback for when the value is changed - event EventHandler ValueChanged; - //The actual value of the bindable property T Value { get; set; } diff --git a/CorgEng.GenericInterfaces/UtilityTypes/IListenable.cs b/CorgEng.GenericInterfaces/UtilityTypes/IListenable.cs new file mode 100644 index 00000000..f7002329 --- /dev/null +++ b/CorgEng.GenericInterfaces/UtilityTypes/IListenable.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CorgEng.GenericInterfaces.UtilityTypes +{ + public interface IListenable + { + + //Callback for when the value is changed + event EventHandler ValueChanged; + + } +} diff --git a/CorgEng.GenericInterfaces/UtilityTypes/IVector.cs b/CorgEng.GenericInterfaces/UtilityTypes/IVector.cs index a01f2243..64110ef9 100644 --- a/CorgEng.GenericInterfaces/UtilityTypes/IVector.cs +++ b/CorgEng.GenericInterfaces/UtilityTypes/IVector.cs @@ -6,11 +6,9 @@ namespace CorgEng.GenericInterfaces.UtilityTypes { - public interface IVector + public interface IVector : IListenable { - event EventHandler OnChange; - T this[int x] { get; set; } T X { get; set; } diff --git a/CorgEng.IconSmoothing/CorgEng.IconSmoothing.csproj b/CorgEng.IconSmoothing/CorgEng.IconSmoothing.csproj index 757ddd99..daef2c60 100644 --- a/CorgEng.IconSmoothing/CorgEng.IconSmoothing.csproj +++ b/CorgEng.IconSmoothing/CorgEng.IconSmoothing.csproj @@ -8,6 +8,7 @@ + diff --git a/CorgEng.IconSmoothing/Systems/SmoothIconSystem.cs b/CorgEng.IconSmoothing/Systems/SmoothIconSystem.cs index e7d57e17..15aa125f 100644 --- a/CorgEng.IconSmoothing/Systems/SmoothIconSystem.cs +++ b/CorgEng.IconSmoothing/Systems/SmoothIconSystem.cs @@ -23,11 +23,11 @@ internal class SmoothIconSystem : EntitySystem { [UsingDependency] - private static IWorld WorldAccess; + private static IEntityPositionTracker WorldAccess; public override EntitySystemFlags SystemFlags => EntitySystemFlags.HOST_SYSTEM; - public override void SystemSetup() + public override void SystemSetup(IWorld world) { RegisterLocalEvent(OnEntityCreated); RegisterLocalEvent(OnEntityMoved); @@ -39,7 +39,7 @@ private void SmoothEntity(IEntity entity, SmoothIconComponent smoothIconComponen { if ((entity.EntityFlags & EntityComponentSystem.Entities.EntityFlags.DESTROYED) != 0) return; - IVector position = WorldAccess.GetGridPosition(smoothIconComponent.TransformComponent.Position); + IVector position = WorldAccess.GetGridPosition(smoothIconComponent.TransformComponent.Position.Value); IVector above = new Vector(position.X, position.Y + 1); IVector below = new Vector(position.X, position.Y - 1); IVector right = new Vector(position.X + 1, position.Y); @@ -102,7 +102,7 @@ private void OnComponentRemoved(IEntity entity, SmoothIconComponent trackCompone private void SmoothAround(SmoothIconComponent smoothIconComponent) { - IVector position = WorldAccess.GetGridPosition(smoothIconComponent.TransformComponent.Position); + IVector position = WorldAccess.GetGridPosition(smoothIconComponent.TransformComponent.Position.Value); IVector above = new Vector(position.X, position.Y + 1); IVector below = new Vector(position.X, position.Y - 1); IVector right = new Vector(position.X + 1, position.Y); diff --git a/CorgEng.InputHandling/ClickHandler/SelectableSystem.cs b/CorgEng.InputHandling/ClickHandler/SelectableSystem.cs index d15a37c6..01031db9 100644 --- a/CorgEng.InputHandling/ClickHandler/SelectableSystem.cs +++ b/CorgEng.InputHandling/ClickHandler/SelectableSystem.cs @@ -28,14 +28,11 @@ public class SelectableSystem : EntitySystem { [UsingDependency] - private static IWorld World = default!; + private static IEntityPositionTracker World = default!; [UsingDependency] private static IIconFactory IconFactory = default!; - [UsingDependency] - private static IEntityFactory EntityFactory = default!; - [UsingDependency] private static IInputHandler InputHandler = default!; @@ -50,7 +47,7 @@ public class SelectableSystem : EntitySystem private static SelectedComponent currentSelectedComponent; public static IEntity SelectedEntity { get; private set; } - public override void SystemSetup() + public override void SystemSetup(IWorld world) { selectorOverlay = IconFactory.CreateIcon("selector", 100, 1); RegisterLocalEvent(OnComponentAdded); diff --git a/CorgEng.InputHandling/CorgEng.InputHandling.csproj b/CorgEng.InputHandling/CorgEng.InputHandling.csproj index 2a7bc99c..a917bbf4 100644 --- a/CorgEng.InputHandling/CorgEng.InputHandling.csproj +++ b/CorgEng.InputHandling/CorgEng.InputHandling.csproj @@ -31,6 +31,7 @@ + diff --git a/CorgEng.InputHandling/InputHandler.cs b/CorgEng.InputHandling/InputHandler.cs index a26cb73d..352c7e7a 100644 --- a/CorgEng.InputHandling/InputHandler.cs +++ b/CorgEng.InputHandling/InputHandler.cs @@ -76,7 +76,7 @@ private void HandleScroll(IntPtr window, double x, double y) { //Synchronous to prevent subsystem overloading, will not render the //next frame until this is handled. - new MouseScrollEvent(y).RaiseGlobally(synchronous: true); + new MouseScrollEvent(y).RaiseGlobally(CorgEngMain.PrimaryWorld, synchronous: true); //Trigger the new actions foreach (string actionName in boundScrollActions) { @@ -96,7 +96,7 @@ private void HandleCursorMove(IntPtr window, double x, double y) { //Synchronous to prevent subsystem overloading, will not render the //next frame until this is handled. - new MouseMoveEvent(x / CorgEngMain.GameWindow.Width, y / CorgEngMain.GameWindow.Height).RaiseGlobally(synchronous: true); + new MouseMoveEvent(x / CorgEngMain.GameWindow.Width, y / CorgEngMain.GameWindow.Height).RaiseGlobally(CorgEngMain.PrimaryWorld, synchronous: true); //Trigger the new actions foreach (string actionName in boundMouseMoveActions) { @@ -134,7 +134,7 @@ private void HandleMousePress(IntPtr window, MouseButton button, InputState stat { return; } - mousePressEvent.RaiseGlobally(); + mousePressEvent.RaiseGlobally(CorgEngMain.PrimaryWorld); mouseDownAt = CorgEngMain.Time; //Trigger the action if (!boundMouseActions.TryGetValue(button, out action)) @@ -153,7 +153,7 @@ private void HandleMousePress(IntPtr window, MouseButton button, InputState stat MouseReleaseEvent mouseReleaseEvent = new MouseReleaseEvent(x / width, y / height, button, modifiers); mouseReleaseEvent.HeldTime = CorgEngMain.Time - mouseDownAt; //Raise synchronously, so we can determine if the event was handled - mouseReleaseEvent.RaiseGlobally(true); + mouseReleaseEvent.RaiseGlobally(CorgEngMain.PrimaryWorld, true); //Handle world clicks if (!mouseReleaseEvent.Handled && mouseReleaseEvent.MouseButton == MouseButton.Left) { @@ -192,7 +192,7 @@ private void HandleKeyboardPress(IntPtr window, Keys key, int scanCode, InputSta { case InputState.Press: KeyPressEvent keyPressEvent = new KeyPressEvent(key, mods); - keyPressEvent.RaiseGlobally(); + keyPressEvent.RaiseGlobally(CorgEngMain.PrimaryWorld); lock (heldKeysDownAt) { heldKeysDownAt.Add(key, CorgEngMain.Time); @@ -214,7 +214,7 @@ private void HandleKeyboardPress(IntPtr window, Keys key, int scanCode, InputSta try { KeyReleaseEvent keyReleaseEvent = new KeyReleaseEvent(key, mods); - keyReleaseEvent.RaiseGlobally(); + keyReleaseEvent.RaiseGlobally(CorgEngMain.PrimaryWorld); //Trigger the action if (!boundKeyActions.TryGetValue(key, out action)) return; @@ -270,57 +270,84 @@ public void WindowUpdate(Window targetWindow) public void AddKeybind(string action, Keys key) { - boundKeyActions.Add(key, action); + lock (boundKeyActions) + { + boundKeyActions.Add(key, action); + } } public void AddMouseButtonbind(string action, MouseButton key) { - boundMouseActions.Add(key, action); + lock (boundMouseActions) + { + boundMouseActions.Add(key, action); + } } public void AddMouseMoveBind(string action) { - boundMouseMoveActions.Add(action); + lock (boundMouseMoveActions) + { + boundMouseMoveActions.Add(action); + } } public void AddMouseScrollBind(string action) { - boundScrollActions.Add(action); + lock (boundScrollActions) + { + boundScrollActions.Add(action); + } } public void AddButtonPressAction(string actionKey, Func actionDelegate, int priority) { - if (!buttonDownActions.ContainsKey(actionKey)) - buttonDownActions.Add(actionKey, new SortedList>()); - buttonDownActions[actionKey].Add(priority, actionDelegate); + lock (buttonDownActions) + { + if (!buttonDownActions.ContainsKey(actionKey)) + buttonDownActions.Add(actionKey, new SortedList>()); + buttonDownActions[actionKey].Add(priority, actionDelegate); + } } public void AddButtonReleaseAction(string actionKey, Func actionDelegate, int priority) { - if (!buttonUpActions.ContainsKey(actionKey)) - buttonUpActions.Add(actionKey, new SortedList>()); - buttonUpActions[actionKey].Add(priority, actionDelegate); + lock (buttonUpActions) + { + if (!buttonUpActions.ContainsKey(actionKey)) + buttonUpActions.Add(actionKey, new SortedList>()); + buttonUpActions[actionKey].Add(priority, actionDelegate); + } } public void AddButtonHoldAction(string actionKey, Func actionDelegate, int priority) { - if (!buttonHoldActions.ContainsKey(actionKey)) - buttonHoldActions.Add(actionKey, new SortedList>()); - buttonHoldActions[actionKey].Add(priority, actionDelegate); + lock (buttonHoldActions) + { + if (!buttonHoldActions.ContainsKey(actionKey)) + buttonHoldActions.Add(actionKey, new SortedList>()); + buttonHoldActions[actionKey].Add(priority, actionDelegate); + } } public void AddMouseMoveAction(string actionKey, Func actionDelegate, int priority) { - if (!mouseMoveActions.ContainsKey(actionKey)) - mouseMoveActions.Add(actionKey, new SortedList>()); - mouseMoveActions[actionKey].Add(priority, actionDelegate); + lock (mouseMoveActions) + { + if (!mouseMoveActions.ContainsKey(actionKey)) + mouseMoveActions.Add(actionKey, new SortedList>()); + mouseMoveActions[actionKey].Add(priority, actionDelegate); + } } public void AddMouseScrollAction(string actionKey, Func actionDelegate, int priority) { - if (!scrollActions.ContainsKey(actionKey)) - scrollActions.Add(actionKey, new SortedList>()); - scrollActions[actionKey].Add(priority, actionDelegate); + lock (scrollActions) + { + if (!scrollActions.ContainsKey(actionKey)) + scrollActions.Add(actionKey, new SortedList>()); + scrollActions[actionKey].Add(priority, actionDelegate); + } } public void GetCursorPosition(out double x, out double y) diff --git a/CorgEng.Lighting/Components/LightingComponent.cs b/CorgEng.Lighting/Components/LightingComponent.cs index 0a9ccde5..35dedb5f 100644 --- a/CorgEng.Lighting/Components/LightingComponent.cs +++ b/CorgEng.Lighting/Components/LightingComponent.cs @@ -1,5 +1,4 @@ using CorgEng.EntityComponentSystem.Components; -using CorgEng.EntityComponentSystem.Implementations.Rendering.SpriteRendering; using CorgEng.GenericInterfaces.UtilityTypes; using CorgEng.UtilityTypes.Colours; using System; diff --git a/CorgEng.Lighting/CorgEng.Lighting.csproj b/CorgEng.Lighting/CorgEng.Lighting.csproj index 5cc306df..1179ac2c 100644 --- a/CorgEng.Lighting/CorgEng.Lighting.csproj +++ b/CorgEng.Lighting/CorgEng.Lighting.csproj @@ -9,6 +9,7 @@ + diff --git a/CorgEng.Lighting/RenderCores/LightingRenderCore.cs b/CorgEng.Lighting/RenderCores/LightingRenderCore.cs index e0599351..7f609cc2 100644 --- a/CorgEng.Lighting/RenderCores/LightingRenderCore.cs +++ b/CorgEng.Lighting/RenderCores/LightingRenderCore.cs @@ -1,6 +1,7 @@ using CorgEng.Core; using CorgEng.Core.Dependencies; using CorgEng.Core.Rendering; +using CorgEng.GenericInterfaces.EntityComponentSystem; using CorgEng.GenericInterfaces.Rendering.Renderers.SpriteRendering; using CorgEng.GenericInterfaces.UtilityTypes; using static OpenGL.Gl; @@ -15,28 +16,6 @@ public class LightingRenderCore : RenderCore [UsingDependency] private static ISpriteRendererFactory SpriteRendererFactory = default!; - private static LightingRenderCore? _singleton; - public static LightingRenderCore Singleton - { - get { - if (_singleton == null) - { - _singleton = new LightingRenderCore(); - _singleton.Initialize(); - } - return _singleton; - } - } - - public static void Resolve() - { - if (_singleton == null) - { - _singleton = new LightingRenderCore(); - _singleton.Initialize(); - } - } - public override RenderModes DrawMode => RenderModes.MULTIPLY; public override RenderModes BlendMode => RenderModes.ADDITIVE; @@ -45,6 +24,10 @@ public static void Resolve() public ISpriteRenderer lightRenderer = null!; + public LightingRenderCore(IWorld world) : base(world) + { + } + public override IColour BackColour => ColourFactory.GetColour(0.4f, 0.4f, 0.4f, 1); public override void Initialize() diff --git a/CorgEng.Lighting/Systems/LightingSystem.cs b/CorgEng.Lighting/Systems/LightingSystem.cs index 11a78ea3..aeb5b166 100644 --- a/CorgEng.Lighting/Systems/LightingSystem.cs +++ b/CorgEng.Lighting/Systems/LightingSystem.cs @@ -4,6 +4,7 @@ using CorgEng.EntityComponentSystem.Implementations.Rendering.SpriteRendering; using CorgEng.EntityComponentSystem.Systems; using CorgEng.GenericInterfaces.ContentLoading; +using CorgEng.GenericInterfaces.EntityComponentSystem; using CorgEng.GenericInterfaces.Rendering.Icons; using CorgEng.Lighting.Components; using CorgEng.Lighting.RenderCores; @@ -23,12 +24,9 @@ internal class LightingSystem : EntitySystem [UsingDependency] private static IIconFactory IconFactory = default!; - [UsingDependency] - private static IEntityCreator EntityCreator = default!; - public override EntitySystemFlags SystemFlags => EntitySystemFlags.CLIENT_SYSTEM; - public override void SystemSetup() + public override void SystemSetup(IWorld world) { //When the lighting object initialises, set it to start rendering on the specified system RegisterLocalEvent((entity, lightingComponent, signal) => { diff --git a/CorgEng.Networking/Components/ComponentExtensions.cs b/CorgEng.Networking/Components/ComponentExtensions.cs index c0a7dadf..4bc55479 100644 --- a/CorgEng.Networking/Components/ComponentExtensions.cs +++ b/CorgEng.Networking/Components/ComponentExtensions.cs @@ -2,6 +2,7 @@ using CorgEng.Core.Dependencies; using CorgEng.Core.Modules; using CorgEng.EntityComponentSystem.Components; +using CorgEng.GenericInterfaces.EntityComponentSystem; using CorgEng.GenericInterfaces.Logging; using CorgEng.GenericInterfaces.Networking.Attributes; using CorgEng.GenericInterfaces.Networking.Serialisation; @@ -48,7 +49,7 @@ public static void LoadPropertyInfoCache() //Locate all component types IEnumerable locatedComponentTypes = CorgEngMain.LoadedAssemblyModules .SelectMany(x => x.GetTypes()) - .Where(type => typeof(Component).IsAssignableFrom(type) && !type.IsAbstract); + .Where(type => typeof(IComponent).IsAssignableFrom(type) && !type.IsAbstract); //Populate the property info cache foreach (Type componentType in locatedComponentTypes) { diff --git a/CorgEng.Networking/Components/NetworkTransformComponent.cs b/CorgEng.Networking/Components/NetworkTransformComponent.cs index f09d11e2..d743e313 100644 --- a/CorgEng.Networking/Components/NetworkTransformComponent.cs +++ b/CorgEng.Networking/Components/NetworkTransformComponent.cs @@ -1,4 +1,7 @@ -using CorgEng.EntityComponentSystem.Implementations.Transform; +using CorgEng.Core.Dependencies; +using CorgEng.EntityComponentSystem.Implementations.Transform; +using CorgEng.GenericInterfaces.Networking.Clients; +using CorgEng.GenericInterfaces.Networking.Config; using System; using System.Collections.Generic; using System.Linq; @@ -13,5 +16,45 @@ namespace CorgEng.Networking.Components /// public sealed class NetworkTransformComponent : TransformComponent { + + [UsingDependency] + private static INetworkConfig NetworkConfig; + + /// + /// Automatically assign ownership on creation if we are the server. + /// + private bool _hasLocalOwnership = !NetworkConfig.NetworkingActive || NetworkConfig.ProcessServerSystems; + + /// + /// Are we the management of ownership? + /// + private bool _isOwnershipManager = !NetworkConfig.NetworkingActive || NetworkConfig.ProcessServerSystems; + + /// + /// The current owner of this transform component as determined by the ownership manager. + /// + internal IClient currentOwner; + + /// + /// Returns true if the local application has ownership of this network transform. + /// + public bool HasOwnership + { + get + { + return _hasLocalOwnership; + } + } + + /// + /// Are we the set ownership manager? + /// The ownership manager will be the server, they are the one that + /// verifies any attempted modifications of the entity. + /// + public bool IsOwnershipManager + { + get { return _isOwnershipManager; } + } + } } diff --git a/CorgEng.Networking/EntitySystems/ClientSystem.cs b/CorgEng.Networking/EntitySystems/ClientSystem.cs index 7e45ab61..f9a7bc2d 100644 --- a/CorgEng.Networking/EntitySystems/ClientSystem.cs +++ b/CorgEng.Networking/EntitySystems/ClientSystem.cs @@ -11,6 +11,7 @@ using CorgEng.GenericInterfaces.World; using CorgEng.Networking.Components; using CorgEng.Networking.Events; +using CorgEng.Networking.Networking; using System; using System.Collections.Generic; using System.IO; @@ -31,7 +32,9 @@ internal class ClientSystem : EntitySystem private static ILogger Logger; [UsingDependency] - private static IEntityCommunicator EntityCommunicator; + private static IEntityCommunicatorFactory EntityCommunicatorFactory; + + private IEntityCommunicator EntityCommunicator; [UsingDependency] private static IServerCommunicator ServerCommunicator; @@ -40,12 +43,13 @@ internal class ClientSystem : EntitySystem private static INetworkMessageFactory NetworkMessageFactory; [UsingDependency] - private static IWorld WorldAccess; + private static IEntityPositionTracker WorldAccess; public override EntitySystemFlags SystemFlags { get; } = EntitySystemFlags.HOST_SYSTEM; - public override void SystemSetup() + public override void SystemSetup(IWorld world) { + EntityCommunicator = EntityCommunicatorFactory.CreateEntityCommunicator(world); RegisterLocalEvent(OnClientConnected); RegisterLocalEvent(OnClientMoved); RegisterLocalEvent(OnEntityDeleted); diff --git a/CorgEng.Networking/EntitySystems/DelayedEventSystem.cs b/CorgEng.Networking/EntitySystems/DelayedEventSystem.cs index 4d65c1a7..a57d0ffb 100644 --- a/CorgEng.Networking/EntitySystems/DelayedEventSystem.cs +++ b/CorgEng.Networking/EntitySystems/DelayedEventSystem.cs @@ -26,7 +26,7 @@ internal static void AddDelayedEvent(uint identifier, Action callbackAc } } - public override void SystemSetup() + public override void SystemSetup(IWorld world) { RegisterLocalEvent((entity, component, signal) => { List> actionsToPerform; diff --git a/CorgEng.Networking/EntitySystems/NetworkSystem.cs b/CorgEng.Networking/EntitySystems/NetworkSystem.cs index 3cea5d11..68f0bb94 100644 --- a/CorgEng.Networking/EntitySystems/NetworkSystem.cs +++ b/CorgEng.Networking/EntitySystems/NetworkSystem.cs @@ -12,6 +12,7 @@ using CorgEng.GenericInterfaces.Networking.Networking.Server; using CorgEng.GenericInterfaces.Networking.Packets; using CorgEng.Networking.Components; +using CorgEng.Networking.Networking; using CorgEng.Networking.Networking.Server; using CorgEng.Networking.VersionSync; using CorgEng.UtilityTypes.Vectors; @@ -40,19 +41,19 @@ public class NetworkSystem : EntitySystem private static INetworkMessageFactory NetworkMessageFactory; [UsingDependency] - private static IEntityCommunicator EntityCommunicator; + private static IEntityCommunicatorFactory EntityCommunicatorFactory; - [UsingDependency] - private static INetworkingServer NetworkingServer; + private IEntityCommunicator EntityCommunicator; public override EntitySystemFlags SystemFlags { get; } = EntitySystemFlags.HOST_SYSTEM | EntitySystemFlags.CLIENT_SYSTEM; - public override void SystemSetup() + public override void SystemSetup(IWorld world) { + EntityCommunicator = EntityCommunicatorFactory.CreateEntityCommunicator(world); RegisterLocalEvent(OnComponentAdded); RegisterLocalEvent(OnNetworkedEventRaised); RegisterLocalEvent((e, c, s) => { - foreach (IClient client in ((NetworkingServer)NetworkingServer).connectedClients.Values) + foreach (IClient client in ((NetworkServer)world.ServerInstance).connectedClients.Values) EntityCommunicator.CommunicateEntity(e, client); }); RegisterGlobalEvent(OnGlobalNetworkedEventRaised); @@ -100,7 +101,7 @@ private void OnNetworkedEventRaised(IEntity entity, NetworkTransformComponent tr { ServerCommunicator?.SendToReleventClients( NetworkMessageFactory.CreateMessage(PacketHeaders.LOCAL_EVENT_RAISED, InjectEventCode(networkedEventRaisedEvent.RaisedEvent, entity)), - transformComponent.Position, + transformComponent.Position.Value, new Vector(1, 1, 1) ); } diff --git a/CorgEng.Networking/Networking/Client/ClientCommunicator.cs b/CorgEng.Networking/Networking/Client/ClientCommunicator.cs index 4488aafd..a0c897dd 100644 --- a/CorgEng.Networking/Networking/Client/ClientCommunicator.cs +++ b/CorgEng.Networking/Networking/Client/ClientCommunicator.cs @@ -1,4 +1,6 @@ -using CorgEng.DependencyInjection.Dependencies; +using CorgEng.Core.Dependencies; +using CorgEng.DependencyInjection.Dependencies; +using CorgEng.GenericInterfaces.Logging; using CorgEng.GenericInterfaces.Networking.Networking.Client; using CorgEng.GenericInterfaces.Networking.Packets; using System; @@ -13,10 +15,18 @@ namespace CorgEng.Networking.Networking.Client internal class ClientCommunicator : IClientCommunicator { - public static NetworkingClient client; + [UsingDependency] + private static ILogger Logger; + + public static NetworkClient client; public void SendToServer(INetworkMessage networkMessage) { + if (client == null) + { + Logger.WriteLine("Attempted to send a message to the server while not connected.", LogType.WARNING); + return; + } client.QueueMessage(networkMessage); } diff --git a/CorgEng.Networking/Networking/Client/NetworkingClient.cs b/CorgEng.Networking/Networking/Client/NetworkClient.cs similarity index 95% rename from CorgEng.Networking/Networking/Client/NetworkingClient.cs rename to CorgEng.Networking/Networking/Client/NetworkClient.cs index c446db1e..4888a860 100644 --- a/CorgEng.Networking/Networking/Client/NetworkingClient.cs +++ b/CorgEng.Networking/Networking/Client/NetworkClient.cs @@ -39,8 +39,7 @@ namespace CorgEng.Networking.Networking.Client /// This is absolutely awful and I hate it, will probably rewrite it in the future /// to iron out some of the oversights. /// - [Dependency] - internal class NetworkingClient : NetworkCommunicator, INetworkingClient + public class NetworkClient : NetworkCommunicator, INetworkClient { [UsingDependency] @@ -56,7 +55,9 @@ internal class NetworkingClient : NetworkCommunicator, INetworkingClient private static INetworkConfig NetworkConfig; [UsingDependency] - private static IEntityCommunicator EntityCommunicator; + private static IEntityCommunicatorFactory EntityCommunicatorFactory; + + private IEntityCommunicator EntityCommunicator; [UsingDependency] private static IPrototypeManager PrototypeManager; @@ -68,6 +69,14 @@ internal class NetworkingClient : NetworkCommunicator, INetworkingClient public event ConnectionFailed OnConnectionFailed; + private IWorld attachedWorld; + + public NetworkClient(IWorld world) + { + attachedWorld = world; + EntityCommunicator = EntityCommunicatorFactory.CreateEntityCommunicator(world); + } + /// /// Attempt connection to a network address /// @@ -262,7 +271,7 @@ protected override void HandleMessage(IPEndPoint sender, PacketHeaders header, b ushort networkedIdentifier = reader.ReadUInt16(); //Read the target entity uint entityIdentifier = reader.ReadUInt32(); - IEntity entityTarget = EntityManager.GetEntity(entityIdentifier); + IEntity entityTarget = attachedWorld.EntityManager.GetEntity(entityIdentifier); //Get the event that was raised INetworkedEvent raisedEvent = VersionGenerator.CreateTypeFromIdentifier(networkedIdentifier); //Deserialize the event @@ -301,7 +310,7 @@ protected override void HandleMessage(IPEndPoint sender, PacketHeaders header, b Logger.WriteMetric("networked_global_event", raisedEvent.GetType().ToString()); //Deserialize the event raisedEvent.Deserialise(reader); - raisedEvent.RaiseGlobally(false); + raisedEvent.RaiseGlobally(CorgEngMain.PrimaryWorld, false); } } return; @@ -313,7 +322,7 @@ protected override void HandleMessage(IPEndPoint sender, PacketHeaders header, b Task.Run(async () => { IEntity createdEntity = await EntityCommunicator.DeserialiseEntity(data.Skip(start).Take(length).ToArray()); - EntityManager.RegisterEntity(createdEntity); + attachedWorld.EntityManager.RegisterEntity(createdEntity); }); return; case PacketHeaders.UPDATE_CLIENT_VIEW: @@ -332,7 +341,7 @@ protected override void HandleMessage(IPEndPoint sender, PacketHeaders header, b double viewOffsetHeight = reader.ReadDouble(); //Update our view //Send a signal - new ModifyIsometricView(viewX + viewOffsetX, viewY + viewOffsetY, viewZ, viewOffsetWidth, viewOffsetHeight).RaiseGlobally(); + new ModifyIsometricView(viewX + viewOffsetX, viewY + viewOffsetY, viewZ, viewOffsetWidth, viewOffsetHeight).RaiseGlobally(CorgEngMain.PrimaryWorld); } } return; diff --git a/CorgEng.Networking/Networking/Client/NetworkClientFactory.cs b/CorgEng.Networking/Networking/Client/NetworkClientFactory.cs new file mode 100644 index 00000000..80c6d0c9 --- /dev/null +++ b/CorgEng.Networking/Networking/Client/NetworkClientFactory.cs @@ -0,0 +1,23 @@ +using CorgEng.Core.Dependencies; +using CorgEng.DependencyInjection.Dependencies; +using CorgEng.GenericInterfaces.EntityComponentSystem; +using CorgEng.GenericInterfaces.Networking.Networking.Client; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CorgEng.Networking.Networking.Client +{ + [Dependency] + internal class NetworkClientFactory : INetworkClientFactory + { + + public INetworkClient CreateNetworkClient(IWorld world) + { + return new NetworkClient(world); + } + + } +} diff --git a/CorgEng.Networking/Networking/Server/EntityCommunicator.cs b/CorgEng.Networking/Networking/EntityCommunicator.cs similarity index 95% rename from CorgEng.Networking/Networking/Server/EntityCommunicator.cs rename to CorgEng.Networking/Networking/EntityCommunicator.cs index fc637e7a..8c3dfc11 100644 --- a/CorgEng.Networking/Networking/Server/EntityCommunicator.cs +++ b/CorgEng.Networking/Networking/EntityCommunicator.cs @@ -23,9 +23,8 @@ using System.Text; using System.Threading.Tasks; -namespace CorgEng.Networking.Networking.Server +namespace CorgEng.Networking.Networking { - [Dependency] internal class EntityCommunicator : IEntityCommunicator { @@ -44,6 +43,13 @@ internal class EntityCommunicator : IEntityCommunicator [UsingDependency] private static INetworkMessageFactory NetworkMessageFactory; + internal IWorld World { get; } + + public EntityCommunicator(IWorld world) + { + World = world; + } + /// /// Communicate information about an entity to a client. /// We need to include: @@ -71,14 +77,14 @@ public async Task DeserialiseEntity(byte[] data) uint entityIdentifier = binaryReader.ReadUInt32(); uint prototypeIdentifier = binaryReader.ReadUInt32(); //Create or locate the entity - IEntity entity = EntityManager.GetEntity(entityIdentifier); + IEntity entity = World.EntityManager.GetEntity(entityIdentifier); if (entity == null) { //We need to create the entity //Create it based on the prototype we have, which we need to request from the server if we don't have it already IPrototype locatedPrototype = await PrototypeManager.GetPrototype(prototypeIdentifier); //Create the entity with a specific identifier - entity = locatedPrototype.CreateEntityFromPrototype(entityIdentifier); + entity = locatedPrototype.CreateEntityFromPrototype(World, entityIdentifier); } else { diff --git a/CorgEng.Networking/Networking/EntityCommunicatorFactory.cs b/CorgEng.Networking/Networking/EntityCommunicatorFactory.cs new file mode 100644 index 00000000..e1072672 --- /dev/null +++ b/CorgEng.Networking/Networking/EntityCommunicatorFactory.cs @@ -0,0 +1,20 @@ +using CorgEng.DependencyInjection.Dependencies; +using CorgEng.GenericInterfaces.EntityComponentSystem; +using CorgEng.GenericInterfaces.Networking.Networking.Server; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CorgEng.Networking.Networking +{ + [Dependency] + internal class EntityCommunicatorFactory : IEntityCommunicatorFactory + { + public IEntityCommunicator CreateEntityCommunicator(IWorld world) + { + return new EntityCommunicator(world); + } + } +} diff --git a/CorgEng.Networking/Networking/NetworkCommunicator.cs b/CorgEng.Networking/Networking/NetworkCommunicator.cs index 73fd38a1..846cd9cc 100644 --- a/CorgEng.Networking/Networking/NetworkCommunicator.cs +++ b/CorgEng.Networking/Networking/NetworkCommunicator.cs @@ -2,6 +2,7 @@ using CorgEng.Core.Dependencies; using CorgEng.Core.Modules; using CorgEng.DependencyInjection.Dependencies; +using CorgEng.EntityComponentSystem.Components.ComponentVariables.Networking; using CorgEng.EntityComponentSystem.Entities; using CorgEng.EntityComponentSystem.Events; using CorgEng.EntityComponentSystem.Events.Events; @@ -16,6 +17,7 @@ using CorgEng.GenericInterfaces.Networking.Packets.PacketQueues; using CorgEng.GenericInterfaces.Networking.PrototypeManager; using CorgEng.GenericInterfaces.Rendering; +using CorgEng.GenericInterfaces.Serialization; using CorgEng.Networking.Components; using CorgEng.Networking.EntitySystems; using CorgEng.Networking.Events; @@ -36,7 +38,7 @@ namespace CorgEng.Networking.Networking { - internal abstract class NetworkCommunicator + public abstract class NetworkCommunicator { [UsingDependency] @@ -51,6 +53,9 @@ internal abstract class NetworkCommunicator [UsingDependency] private static IQueuedPacketFactory QueuedPacketFactory; + [UsingDependency] + private static IAutoSerialiser AutoSerialiser; + public IClientAddressingTable ClientAddressingTable { get; private set; } protected IPacketQueue PacketQueue; @@ -133,6 +138,8 @@ protected void NetworkSenderThread(UdpClient client) { //Create a stopwatch to get the current time stopwatch.Restart(); + //Create messages if needed + UpdateDirtyNetvars(); //Transmit packets while (PacketQueue.AcquireLockIfHasMessages()) { @@ -304,6 +311,27 @@ private void ProcessPacket(IPEndPoint sender, byte[] data) } } + private void UpdateDirtyNetvars() + { + lock (NetVar.DirtyNetvars) + { + // Get the values and packetise them + foreach (INetVar netVar in NetVar.DirtyNetvars) + { + object value = netVar.GetValue(); + byte[] serialisedValue = new byte[AutoSerialiser.SerialisationLength(netVar.GetStoredType(), value)]; + using (BinaryWriter binaryWriter = new BinaryWriter(new MemoryStream(serialisedValue))) + { + AutoSerialiser.SerializeInto(netVar.GetStoredType(), value, binaryWriter); + } + INetworkMessage networkMessage = NetworkMessageFactory.CreateMessage(PacketHeaders.NETVAR_VALUE_UPDATED, serialisedValue); + PacketQueue.QueueMessage(ClientAddressingTable?.GetEveryone() ?? null, networkMessage); + } + // Reset the list + NetVar.DirtyNetvars.Clear(); + } + } + /// /// Is the provided address the address of someone connected / what we are connected to? /// diff --git a/CorgEng.Networking/Networking/Server/NetworkingServer.cs b/CorgEng.Networking/Networking/Server/NetworkServer.cs similarity index 93% rename from CorgEng.Networking/Networking/Server/NetworkingServer.cs rename to CorgEng.Networking/Networking/Server/NetworkServer.cs index e3910538..69dbe6ef 100644 --- a/CorgEng.Networking/Networking/Server/NetworkingServer.cs +++ b/CorgEng.Networking/Networking/Server/NetworkServer.cs @@ -33,36 +33,31 @@ namespace CorgEng.Networking.Networking.Server { - - [Dependency] - internal class NetworkingServer : NetworkCommunicator, INetworkingServer + public class NetworkServer : NetworkCommunicator, INetworkServer { [UsingDependency] - private static ILogger Logger; + private static ILogger Logger = null!; [UsingDependency] - private static IClientFactory ClientFactory; + private static IClientFactory ClientFactory = null!; [UsingDependency] - private static IClientAddressingTableFactory ClientAddressingTableFactory; + private static IClientAddressingTableFactory ClientAddressingTableFactory = null!; public IClientAddressingTable ClientAddressingTable { get; private set; } [UsingDependency] - private static INetworkMessageFactory NetworkMessageFactory; - - [UsingDependency] - private static IPacketQueueFactory PacketQueueFactory; + private static INetworkMessageFactory NetworkMessageFactory = null!; [UsingDependency] - private static INetworkConfig NetworkConfig; + private static IPacketQueueFactory PacketQueueFactory = null!; [UsingDependency] - private static IPrototypeManager PrototypeManager; + private static INetworkConfig NetworkConfig = null!; [UsingDependency] - private static IEntityFactory EntityFactory; + private static IPrototypeManager PrototypeManager = null!; private static IPrototype DefaultEntityPrototype; @@ -73,11 +68,17 @@ internal class NetworkingServer : NetworkCommunicator, INetworkingServer private double lastPingAt = 0; - [ModuleLoad] + private IWorld world; + + public NetworkServer(IWorld world) + { + this.world = world; + } + public void LoadDefaultPrototype() { //Set the default prototype - EntityFactory.CreateEmptyEntity(sampleEntity => { + world.EntityManager.CreateEmptyEntity(sampleEntity => { sampleEntity.AddComponent(new NetworkTransformComponent()); sampleEntity.AddComponent(new ClientComponent()); //Get the prototype @@ -110,6 +111,13 @@ public virtual void StartHosting(int port) } //Start networking NetworkConfig.NetworkingActive = true; + NetworkConfig.ProcessServerSystems = true; + // Load the default player prototype if it hasn't been set. + // Must be done after we activate the server system processing flag + if (DefaultEntityPrototype == null) + { + LoadDefaultPrototype(); + } //Log Logger?.WriteLine($"Starting UDP server on port {port}.", LogType.MESSAGE); //Set the listening port @@ -134,7 +142,6 @@ public virtual void StartHosting(int port) shutdownCountdown.Reset(); started = true; ServerCommunicator.server = this; - NetworkConfig.ProcessServerSystems = true; } protected override void HandleMessage(IPEndPoint sender, PacketHeaders header, byte[] data, int start, int length) @@ -182,7 +189,7 @@ protected override void HandleMessage(IPEndPoint sender, PacketHeaders header, b Logger.WriteMetric("networked_global_event", raisedEvent.ToString()); //Deserialize the event raisedEvent.Deserialise(reader); - raisedEvent.RaiseGlobally(false); + raisedEvent.RaiseGlobally(world, false); } } return; @@ -253,7 +260,7 @@ private void HandleConnectionRequest(IPEndPoint sender, byte[] data, int start, ClientAddressingTable.AddClient(createdClient), NetworkMessageFactory.CreateMessage(PacketHeaders.CONNECTION_ACCEPT, new byte[0])); //Create a new client entity and add what we need - IEntity createdEntity = DefaultEntityPrototype.CreateEntityFromPrototype(); + IEntity createdEntity = DefaultEntityPrototype.CreateEntityFromPrototype(world); //Send a connection event new ClientConnectedEvent(createdClient).Raise(createdEntity); //Initialise the entity diff --git a/CorgEng.Networking/Networking/Server/NetworkServerFactory.cs b/CorgEng.Networking/Networking/Server/NetworkServerFactory.cs new file mode 100644 index 00000000..bb414de9 --- /dev/null +++ b/CorgEng.Networking/Networking/Server/NetworkServerFactory.cs @@ -0,0 +1,22 @@ +using CorgEng.DependencyInjection.Dependencies; +using CorgEng.GenericInterfaces.EntityComponentSystem; +using CorgEng.GenericInterfaces.Networking.Networking.Server; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CorgEng.Networking.Networking.Server +{ + [Dependency] + internal class NetworkServerFactory : INetworkServerFactory + { + + public INetworkServer CreateNetworkServer(IWorld world) + { + return new NetworkServer(world); + } + + } +} diff --git a/CorgEng.Networking/Networking/Server/ServerCommunicator.cs b/CorgEng.Networking/Networking/Server/ServerCommunicator.cs index 727d4529..d9e1b66f 100644 --- a/CorgEng.Networking/Networking/Server/ServerCommunicator.cs +++ b/CorgEng.Networking/Networking/Server/ServerCommunicator.cs @@ -19,7 +19,7 @@ internal class ServerCommunicator : IServerCommunicator [UsingDependency] private static IClientAddressFactory ClientAddressFactory; - public static NetworkingServer server; + public static NetworkServer server; public bool IsServer => server != null; diff --git a/CorgEng.Networking/Prototypes/Prototype.cs b/CorgEng.Networking/Prototypes/Prototype.cs index 45c32e15..66b445e6 100644 --- a/CorgEng.Networking/Prototypes/Prototype.cs +++ b/CorgEng.Networking/Prototypes/Prototype.cs @@ -1,5 +1,7 @@ using CorgEng.Core.Dependencies; using CorgEng.EntityComponentSystem.Entities; +using CorgEng.EntityComponentSystem.Events; +using CorgEng.EntityComponentSystem.Events.Events; using CorgEng.EntityComponentSystem.Implementations.Deletion; using CorgEng.GenericInterfaces.EntityComponentSystem; using CorgEng.GenericInterfaces.Logging; @@ -62,14 +64,14 @@ public void GenerateFromEntity(IEntity entity) } } - public IEntity CreateEntityFromPrototype() + public IEntity CreateEntityFromPrototype(IWorld world) { - return CreateEntityFromPrototype(EntityManager.GetNewEntityId()); + return CreateEntityFromPrototype(world, world.EntityManager.GetNewEntityId()); } - public IEntity CreateEntityFromPrototype(uint entityIdentifier) + public IEntity CreateEntityFromPrototype(IWorld world, uint entityIdentifier) { - IEntity createdEntity = new Entity(entityIdentifier); + IEntity createdEntity = world.EntityManager.CreateUninitialisedEntity(entityIdentifier); //Add components foreach (Type type in prototypeComponents.Keys) { @@ -83,6 +85,8 @@ public IEntity CreateEntityFromPrototype(uint entityIdentifier) //Add the component createdEntity.AddComponent(createdComponent); } + //Raise the initialise event + new InitialiseEvent().Raise(createdEntity, true); return createdEntity; } diff --git a/CorgEng.Networking/Serialization/AutoSerialiser.cs b/CorgEng.Networking/Serialization/AutoSerialiser.cs index cdb97cd6..5e816298 100644 --- a/CorgEng.Networking/Serialization/AutoSerialiser.cs +++ b/CorgEng.Networking/Serialization/AutoSerialiser.cs @@ -16,18 +16,31 @@ namespace CorgEng.Networking.Serialization [Dependency] internal class AutoSerialiser : IAutoSerialiser { + + private Type ConstructGenericType(Type startingType, BinaryReader binaryReader) + { + Type nextType = VersionGenerator.GetTypeFromNetworkedIdentifier(binaryReader.ReadUInt16()); + if (nextType.IsGenericType) + { + Type nextGenericType = ConstructGenericType(nextType, binaryReader); + nextType = nextType.MakeGenericType(nextGenericType); + } + return nextType; + } + public object Deserialize(Type deserialisationType, BinaryReader binaryReader) { //Set the value based on the property type if (typeof(ICustomSerialisationBehaviour).IsAssignableFrom(deserialisationType)) { + // Wait, why do we need to do any of this if we already know the type we are deserialising?? + // ^ Because subtypes can be assigned to variables of a parent type. Type type = VersionGenerator.GetTypeFromNetworkedIdentifier(binaryReader.ReadUInt16()); //Generic types need additional handling in order to determine the contained generic type if (type.IsGenericType) { - //Doesn't allow for embedded generic types, would be easy to implement with a recursive function however - Type genericType = VersionGenerator.GetTypeFromNetworkedIdentifier(binaryReader.ReadUInt16()); ; - type = type.MakeGenericType(genericType); + Type createdGenericType = ConstructGenericType(type, binaryReader); + type = type.MakeGenericType(createdGenericType); } ICustomSerialisationBehaviour createdObject = (ICustomSerialisationBehaviour)FormatterServices.GetUninitializedObject(type); createdObject.DeserialiseFrom(binaryReader); @@ -79,7 +92,13 @@ public int SerialisationLength(Type type, object value) } if (value is ICustomSerialisationBehaviour serialisationBehaviour) { - int genericSize = value.GetType().IsGenericType ? sizeof(ushort) : 0; + int genericSize = 0; + Type current = value.GetType(); + while (current.IsGenericType) + { + genericSize += sizeof(ushort); + current = current.GetGenericArguments()[0]; + } return sizeof(ushort) + genericSize + serialisationBehaviour.GetSerialisationLength(); } if (type.IsPrimitive) @@ -97,10 +116,12 @@ public void SerializeInto(Type type, object value, BinaryWriter binaryWriter) if (typeof(ICustomSerialisationBehaviour).IsAssignableFrom(objectType)) { binaryWriter.Write(VersionGenerator.GetNetworkedIdentifier(value.GetType())); - if (value.GetType().IsGenericType) + Type current = value.GetType(); + while (current.IsGenericType) { //TODO: Add embedded recursive types and multiple recursive type arguments. - binaryWriter.Write(VersionGenerator.GetNetworkedIdentifier(value.GetType().GenericTypeArguments[0])); + current = current.GenericTypeArguments[0]; + binaryWriter.Write(VersionGenerator.GetNetworkedIdentifier(current)); } ((ICustomSerialisationBehaviour)value).SerialiseInto(binaryWriter); } diff --git a/CorgEng.NetworkingPrimitives/CorgEng.NetworkingPrimitives.csproj b/CorgEng.NetworkingPrimitives/CorgEng.NetworkingPrimitives.csproj new file mode 100644 index 00000000..132c02c5 --- /dev/null +++ b/CorgEng.NetworkingPrimitives/CorgEng.NetworkingPrimitives.csproj @@ -0,0 +1,9 @@ + + + + net6.0 + enable + enable + + + diff --git a/CorgEng.NetworkingPrimitives/Networking/INetVar.cs b/CorgEng.NetworkingPrimitives/Networking/INetVar.cs new file mode 100644 index 00000000..78c84268 --- /dev/null +++ b/CorgEng.NetworkingPrimitives/Networking/INetVar.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CorgEng.NetworkingPrimitives.Variables +{ + internal interface INetVar + { + + Type GetStoredType(); + + object GetValue(); + + } +} diff --git a/CorgEng.NetworkingPrimitives/Networking/NetCVar.cs b/CorgEng.NetworkingPrimitives/Networking/NetCVar.cs new file mode 100644 index 00000000..ad124319 --- /dev/null +++ b/CorgEng.NetworkingPrimitives/Networking/NetCVar.cs @@ -0,0 +1,65 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CorgEng.NetworkingPrimitives.Variables +{ + + internal static class NetVar + { + internal static HashSet DirtyNetvars = new HashSet(); + } + + /** + * Networked version of a CVar + */ + public class NetCVar : CVar, INetVar + { + + private static ulong _netVarCount = 0; + + public ulong NetVarID { get; private set; } + + public bool PrototypeSerialised { get; set; } + + public NetCVar() + { + // Set the NetVarID + NetVarID = _netVarCount++; + } + + public override void TriggerChanged() + { + base.TriggerChanged(); + MarkDirty(); + } + + public void MarkDirty() + { + lock (NetVar.DirtyNetvars) + { + if (NetVar.DirtyNetvars.Contains(this)) + return; + NetVar.DirtyNetvars.Add(this); + } + } + + public NetCVar SetPrototypeSerialised(bool value) + { + PrototypeSerialised = value; + return this; + } + + public object GetValue() + { + return Value; + } + + public Type GetStoredType() + { + return typeof(T); + } + } +} diff --git a/CorgEng.Pathfinding/Queryers/WorldPathCellQueryer.cs b/CorgEng.Pathfinding/Queryers/WorldPathCellQueryer.cs index 7eaee559..2193b5ec 100644 --- a/CorgEng.Pathfinding/Queryers/WorldPathCellQueryer.cs +++ b/CorgEng.Pathfinding/Queryers/WorldPathCellQueryer.cs @@ -16,7 +16,7 @@ public class WorldPathCellQueryer : IPathCellQueryer { [UsingDependency] - private static IWorld WorldAccess; + private static IEntityPositionTracker WorldAccess; public int EnterPositionCost(IVector position, Direction enterDirection) { diff --git a/CorgEng.Pathfinding/Systems/SolidSystem.cs b/CorgEng.Pathfinding/Systems/SolidSystem.cs index f5b02964..a3d1b0eb 100644 --- a/CorgEng.Pathfinding/Systems/SolidSystem.cs +++ b/CorgEng.Pathfinding/Systems/SolidSystem.cs @@ -19,7 +19,7 @@ internal class SolidSystem : EntitySystem { [UsingDependency] - private static IWorld WorldAccess; + private static IEntityPositionTracker WorldAccess; /// /// The world layers @@ -28,7 +28,7 @@ internal class SolidSystem : EntitySystem public override EntitySystemFlags SystemFlags => EntitySystemFlags.HOST_SYSTEM; - public override void SystemSetup() + public override void SystemSetup(IWorld world) { RegisterLocalEvent(OnComponentAdded); RegisterLocalEvent(OnComponentRemoved); @@ -49,7 +49,7 @@ private void OnComponentAdded(IEntity entity, SolidComponent solidComponent, Com WorldLayers.Add(0, new WorldGrid()); } //Get the position - IVector gridPosition = WorldAccess.GetGridPosition(attachedTransformComponent.Position); + IVector gridPosition = WorldAccess.GetGridPosition(attachedTransformComponent.Position.Value); int positionX = gridPosition.X; int positionY = gridPosition.Y; WorldLayers[0].AddElement(positionX, positionY); @@ -69,7 +69,7 @@ private void OnComponentRemoved(IEntity entity, SolidComponent solidComponent, C return; } //Get the position - IVector gridPosition = WorldAccess.GetGridPosition(attachedTransformComponent.Position); + IVector gridPosition = WorldAccess.GetGridPosition(attachedTransformComponent.Position.Value); int positionX = gridPosition.X; int positionY = gridPosition.Y; WorldLayers[0].RemoveElement(positionX, positionY); diff --git a/CorgEng.Rendering/ComponentSystem/DepthParallax/DepthSpriteRenderComponent.cs b/CorgEng.Rendering/ComponentSystem/DepthParallax/DepthSpriteRenderComponent.cs new file mode 100644 index 00000000..535d24ce --- /dev/null +++ b/CorgEng.Rendering/ComponentSystem/DepthParallax/DepthSpriteRenderComponent.cs @@ -0,0 +1,34 @@ +using CorgEng.EntityComponentSystem.Implementations.Rendering.SpriteRendering; +using CorgEng.GenericInterfaces.Networking.Attributes; +using CorgEng.GenericInterfaces.Rendering.Renderers.SpriteRendering; +using CorgEng.Rendering.DepthParallax; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CorgEng.Rendering.ComponentSystem.DepthParallax +{ + public class DepthSpriteRenderComponent : SpriteRenderComponent + { + + public override ISpriteRenderer SpriteRenderer + { + get + { + if (SpriteRendererIdentifier == 0) + return null; + if (_spriteRenderer == null || cachedSpriteRendererIdentifier != SpriteRendererIdentifier) + { + if (!ParallaxLayerRenderCore.parallaxRenderCores.ContainsKey((int)SpriteRendererIdentifier)) + return null; + _spriteRenderer = ParallaxLayerRenderCore.parallaxRenderCores[(int)SpriteRendererIdentifier][(int)Sprite.Layer-1].renderer; + cachedSpriteRendererIdentifier = SpriteRendererIdentifier; + } + return _spriteRenderer; + } + } + + } +} diff --git a/CorgEng.EntityComponentSystem/Implementations/Rendering/SpriteRendering/AddOverlayEvent.cs b/CorgEng.Rendering/ComponentSystem/SpriteRendering/AddOverlayEvent.cs similarity index 100% rename from CorgEng.EntityComponentSystem/Implementations/Rendering/SpriteRendering/AddOverlayEvent.cs rename to CorgEng.Rendering/ComponentSystem/SpriteRendering/AddOverlayEvent.cs diff --git a/CorgEng.EntityComponentSystem/Implementations/Rendering/SpriteRendering/RemoveOverlayEvent.cs b/CorgEng.Rendering/ComponentSystem/SpriteRendering/RemoveOverlayEvent.cs similarity index 100% rename from CorgEng.EntityComponentSystem/Implementations/Rendering/SpriteRendering/RemoveOverlayEvent.cs rename to CorgEng.Rendering/ComponentSystem/SpriteRendering/RemoveOverlayEvent.cs diff --git a/CorgEng.EntityComponentSystem/Implementations/Rendering/SpriteRendering/SetDirectionEvent.cs b/CorgEng.Rendering/ComponentSystem/SpriteRendering/SetDirectionEvent.cs similarity index 100% rename from CorgEng.EntityComponentSystem/Implementations/Rendering/SpriteRendering/SetDirectionEvent.cs rename to CorgEng.Rendering/ComponentSystem/SpriteRendering/SetDirectionEvent.cs diff --git a/CorgEng.EntityComponentSystem/Implementations/Rendering/SpriteRendering/SetSpriteEvent.cs b/CorgEng.Rendering/ComponentSystem/SpriteRendering/SetSpriteEvent.cs similarity index 100% rename from CorgEng.EntityComponentSystem/Implementations/Rendering/SpriteRendering/SetSpriteEvent.cs rename to CorgEng.Rendering/ComponentSystem/SpriteRendering/SetSpriteEvent.cs diff --git a/CorgEng.EntityComponentSystem/Implementations/Rendering/SpriteRendering/SetSpriteRendererEvent.cs b/CorgEng.Rendering/ComponentSystem/SpriteRendering/SetSpriteRendererEvent.cs similarity index 100% rename from CorgEng.EntityComponentSystem/Implementations/Rendering/SpriteRendering/SetSpriteRendererEvent.cs rename to CorgEng.Rendering/ComponentSystem/SpriteRendering/SetSpriteRendererEvent.cs diff --git a/CorgEng.EntityComponentSystem/Implementations/Rendering/SpriteRendering/SpriteRenderComponent.cs b/CorgEng.Rendering/ComponentSystem/SpriteRendering/SpriteRenderComponent.cs similarity index 94% rename from CorgEng.EntityComponentSystem/Implementations/Rendering/SpriteRendering/SpriteRenderComponent.cs rename to CorgEng.Rendering/ComponentSystem/SpriteRendering/SpriteRenderComponent.cs index 75614292..dff5d587 100644 --- a/CorgEng.EntityComponentSystem/Implementations/Rendering/SpriteRendering/SpriteRenderComponent.cs +++ b/CorgEng.Rendering/ComponentSystem/SpriteRendering/SpriteRenderComponent.cs @@ -58,11 +58,11 @@ public class SpriteRenderComponent : Component, IInstantiatable internal bool WantsToRender { get; set; } = true; - private uint cachedSpriteRendererIdentifier = 0; + protected uint cachedSpriteRendererIdentifier = 0; - private ISpriteRenderer _spriteRenderer; + protected ISpriteRenderer _spriteRenderer; - public ISpriteRenderer SpriteRenderer + public virtual ISpriteRenderer SpriteRenderer { get { diff --git a/CorgEng.EntityComponentSystem/Implementations/Rendering/SpriteRendering/SpriteRenderSystem.cs b/CorgEng.Rendering/ComponentSystem/SpriteRendering/SpriteRenderSystem.cs similarity index 98% rename from CorgEng.EntityComponentSystem/Implementations/Rendering/SpriteRendering/SpriteRenderSystem.cs rename to CorgEng.Rendering/ComponentSystem/SpriteRendering/SpriteRenderSystem.cs index bb2c94ce..68823767 100644 --- a/CorgEng.EntityComponentSystem/Implementations/Rendering/SpriteRendering/SpriteRenderSystem.cs +++ b/CorgEng.Rendering/ComponentSystem/SpriteRendering/SpriteRenderSystem.cs @@ -1,5 +1,6 @@ using CorgEng.Core; using CorgEng.Core.Dependencies; +using CorgEng.EntityComponentSystem.Components; using CorgEng.EntityComponentSystem.Entities; using CorgEng.EntityComponentSystem.Events.Events; using CorgEng.EntityComponentSystem.Implementations.Transform; @@ -38,13 +39,14 @@ public class SpriteRenderSystem : EntitySystem //Runs only on the client, contains no server-side logic. public override EntitySystemFlags SystemFlags { get; } = EntitySystemFlags.CLIENT_SYSTEM | EntitySystemFlags.HOST_SYSTEM; - public override void SystemSetup() + public override void SystemSetup(IWorld world) { RegisterLocalEvent(OnSetSprite); RegisterLocalEvent(OnSetRenderer); RegisterLocalEvent(OnEntityDestroyed); RegisterLocalEvent(OnComponentAdded); RegisterLocalEvent(OnEntityMoved); + //Component.GetTemplate().Position RegisterLocalEvent(OnEntityRotated); RegisterLocalEvent(OnInitialise); RegisterLocalEvent(AddOverlay); @@ -60,7 +62,7 @@ private void OnInitialise(IEntity entity, SpriteRenderComponent spriteRenderComp if (!CorgEngMain.IsRendering) return; //Take the position - spriteRenderComponent.CachedPosition = entity.GetComponent().Position.Copy(); + spriteRenderComponent.CachedPosition = entity.GetComponent().Position.Value.Copy(); //Update the sprite UpdateSprite(spriteRenderComponent); //Apply initial overlays diff --git a/CorgEng.Rendering/Content/Shaders/SpriteShader/SpriteShader.frag b/CorgEng.Rendering/Content/Shaders/SpriteShader/SpriteShader.frag index b89ed638..32fc8994 100644 --- a/CorgEng.Rendering/Content/Shaders/SpriteShader/SpriteShader.frag +++ b/CorgEng.Rendering/Content/Shaders/SpriteShader/SpriteShader.frag @@ -22,6 +22,7 @@ void main() transformedUV *= vec2(fragTextureData[2], fragTextureData[3]); transformedUV += vec2(fragTextureData[0], 1.0 - fragTextureData[1] - fragTextureData[3]); result = texture(renderTexture, transformedUV) * fragColour; + //result.rgb = vec3(gl_FragCoord.z, gl_FragCoord.z, gl_FragCoord.z); //result = vec4(1, 0, 0, 1); //result = vec4(vec3(gl_FragCoord.z), 1.0); if (result.a == 0) diff --git a/CorgEng.Rendering/CorgEng.Rendering.csproj b/CorgEng.Rendering/CorgEng.Rendering.csproj index 0e786021..25614f64 100644 --- a/CorgEng.Rendering/CorgEng.Rendering.csproj +++ b/CorgEng.Rendering/CorgEng.Rendering.csproj @@ -28,6 +28,7 @@ + diff --git a/CorgEng.Rendering/DepthParallax/ParallaxLayerRenderCore.cs b/CorgEng.Rendering/DepthParallax/ParallaxLayerRenderCore.cs new file mode 100644 index 00000000..176e82bc --- /dev/null +++ b/CorgEng.Rendering/DepthParallax/ParallaxLayerRenderCore.cs @@ -0,0 +1,80 @@ +using CorgEng.Core; +using CorgEng.Core.Dependencies; +using CorgEng.Core.Rendering; +using CorgEng.GenericInterfaces.EntityComponentSystem; +using CorgEng.GenericInterfaces.Rendering.Renderers; +using CorgEng.GenericInterfaces.Rendering.Renderers.SpriteRendering; +using CorgEng.Rendering.SpriteRendering; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using static OpenGL.Gl; + +namespace CorgEng.Rendering.DepthParallax +{ + public class ParallaxLayerRenderCore : RenderCore + { + + [UsingDependency] + private static ISpriteRendererFactory SpriteRendererFactory = null!; + + /// + /// The layer that this parallax renderer is on + /// + public int LayerValue { get; } + + /// + /// The next layer in the sequence + /// + private ParallaxLayerRenderCore? next; + + /// + /// The renderer + /// + internal ISpriteRenderer renderer; + + /// + /// Parallax render cores that are active in the world + /// + public static Dictionary parallaxRenderCores = new Dictionary(); + + public override float DepthAdd => 0.01f; + + public ParallaxLayerRenderCore(IWorld world, int identifier, int layer) : base(world) + { + if (!parallaxRenderCores.ContainsKey(identifier)) + parallaxRenderCores.Add(identifier, new ParallaxLayerRenderCore[layer]); + parallaxRenderCores[identifier][layer - 1] = this; + LayerValue = layer; + // Keep going until we reach layer 1 + if (layer == 1) + return; + next = new ParallaxLayerRenderCore(world, identifier, layer - 1); + } + + public override void Initialize() + { + renderer = SpriteRendererFactory.CreateSpriteRenderer(0); + renderer.Initialize(); + next?.Initialize(); + } + + public override void PerformRender() + { + // Render our current layer then pass that on to the next render core to draw + renderer.Render(CorgEngMain.MainCamera); + // Now render the layer below us + if (next != null) + { + next.DoRender(() => { + DrawToBuffer(next.FrameBufferUint, 0, 0, Width, Height); + }); + // Render the layer below us onto our layer + next.DrawToBuffer(FrameBufferUint, 1, 1, Width, Height); + } + } + + } +} diff --git a/CorgEng.Rendering/DepthParallax/README.md b/CorgEng.Rendering/DepthParallax/README.md new file mode 100644 index 00000000..2311cce0 --- /dev/null +++ b/CorgEng.Rendering/DepthParallax/README.md @@ -0,0 +1,4 @@ + +# Parallax Depth + +Objects are rendered from front to back, with each layer taking the previous layer and then stacking new elements on top of it. diff --git a/CorgEng.Rendering/Icons/Icon.cs b/CorgEng.Rendering/Icons/Icon.cs index 89dd4b11..9529ea89 100644 --- a/CorgEng.Rendering/Icons/Icon.cs +++ b/CorgEng.Rendering/Icons/Icon.cs @@ -109,7 +109,7 @@ public Icon(string iconName, float layer, uint plane) IconName = iconName; Layer = layer; Plane = plane; - Colour.OnChange += (e, args) => + Colour.ValueChanged += (e, args) => { ValueChanged?.Invoke(); }; diff --git a/CorgEng.Rendering/InstancedRenderer.cs b/CorgEng.Rendering/InstancedRenderer.cs index 02ec444a..c0558792 100644 --- a/CorgEng.Rendering/InstancedRenderer.cs +++ b/CorgEng.Rendering/InstancedRenderer.cs @@ -79,6 +79,8 @@ public void Initialize() public void Render(ICamera camera) { + if (ShaderSet == null) + throw new Exception("Attempting to use a renderer before it has been initialised. Please call Initialize() first."); //Start using our program //Shaders were loaded during init glUseProgram(programUint); diff --git a/CorgEng.Tests/ContentLoadingTests/ContentLoadingTests.cs b/CorgEng.Tests/ContentLoadingTests/ContentLoadingTests.cs index 25568c36..ecdc705a 100644 --- a/CorgEng.Tests/ContentLoadingTests/ContentLoadingTests.cs +++ b/CorgEng.Tests/ContentLoadingTests/ContentLoadingTests.cs @@ -1,6 +1,7 @@ using CorgEng.ContentLoading; using CorgEng.Core.Dependencies; using CorgEng.GenericInterfaces.ContentLoading; +using CorgEng.GenericInterfaces.EntityComponentSystem; using CorgEng.GenericInterfaces.Logging; using CorgEng.Logging; using Microsoft.VisualStudio.TestTools.UnitTesting; @@ -13,7 +14,7 @@ namespace CorgEng.Tests.ContentLoadingTests { [TestClass] - public class ContentLoadingTests + public class ContentLoadingTests : TestBase { [UsingDependency] @@ -22,16 +23,20 @@ public class ContentLoadingTests [UsingDependency] private static ILogger Logger; + [UsingDependency] + private static IWorldFactory WorldFactory; + [TestMethod] public void TestContentLoading() { + IWorld testWorld = WorldFactory.CreateWorld(); ConsoleLogger.ExceptionCount = 0; EntityLoader.LoadEntities(); Assert.AreEqual(0, ConsoleLogger.ExceptionCount, "An exception occured during entity loading. See logs for details."); //Spawn everything to test spawning foreach (string name in EntityLoader.LoadedDefinitionNames) { - EntityCreator.CreateObject(name); + EntityCreator.CreateObject(testWorld, name); Logger.WriteLine($"Successfully created an instant of {name}.", LogType.LOG); Assert.AreEqual(0, ConsoleLogger.ExceptionCount, $"Failed to spawn object with ID '{name}'."); } diff --git a/CorgEng.Tests/DependencyInjection/DependencyTests.cs b/CorgEng.Tests/DependencyInjection/DependencyTests.cs index 455927d2..d8d0f264 100644 --- a/CorgEng.Tests/DependencyInjection/DependencyTests.cs +++ b/CorgEng.Tests/DependencyInjection/DependencyTests.cs @@ -10,7 +10,7 @@ namespace CorgEng.Tests.DependencyInjection /// Dependency injection overiding is required for these tests to run, so no point in testing it. /// [TestClass] - public class DependencyTests + public class DependencyTests : TestBase { private interface IDependency diff --git a/CorgEng.Tests/EntityComponentSystem/ProcessingSystemTest.cs b/CorgEng.Tests/EntityComponentSystem/ProcessingSystemTest.cs index 9445cff1..fe4cc63e 100644 --- a/CorgEng.Tests/EntityComponentSystem/ProcessingSystemTest.cs +++ b/CorgEng.Tests/EntityComponentSystem/ProcessingSystemTest.cs @@ -1,10 +1,12 @@ -using CorgEng.Core.Dependencies; +using CorgEng.Core; +using CorgEng.Core.Dependencies; using CorgEng.EntityComponentSystem.Components; using CorgEng.EntityComponentSystem.Entities; using CorgEng.EntityComponentSystem.Events; using CorgEng.EntityComponentSystem.Systems; using CorgEng.GenericInterfaces.ContentLoading; using CorgEng.GenericInterfaces.EntityComponentSystem; +using CorgEng.GenericInterfaces.Logging; using Microsoft.VisualStudio.TestTools.UnitTesting; using System; using System.Collections.Generic; @@ -12,15 +14,19 @@ using System.Text; using System.Threading; using System.Threading.Tasks; +using static CorgEng.Tests.EntityComponentSystem.TestEntitySystems; namespace CorgEng.Tests.EntityComponentSystem { [TestClass] - public class ProcessingSystemTest + public class ProcessingSystemTest : TestBase { [UsingDependency] - private static IEntityFactory EntityFactory; + private static IWorldFactory WorldFactory; + + [UsingDependency] + private static ILogger Logger; private class TestComponent : Component { @@ -34,11 +40,11 @@ private class TestSystem : ProcessingSystem public override EntitySystemFlags SystemFlags => EntitySystemFlags.HOST_SYSTEM; - protected override int ProcessDelay => 99; + protected override int ProcessDelay => 100; public volatile int timesSignalled = 0; - public override void SystemSetup() + public override void SystemSetup(IWorld world) { RegisterLocalEvent((e, t, ev) => { timesSignalled++; @@ -51,28 +57,31 @@ public override void SystemSetup() [TestMethod] public void TestProcessingSystems() { - //Start the test system - TestSystem testSystem = new TestSystem(); - testSystem.SystemSetup(); + IWorld world = WorldFactory.CreateWorld(); + CorgEngMain.PrimaryWorld = world; //Create an entity to process - IEntity testEntity = EntityFactory.CreateEmptyEntity(null); + IEntity testEntity = world.EntityManager.CreateEmptyEntity(null); TestComponent testComponent = new TestComponent(); testEntity.AddComponent(testComponent); + TestSystem testSystem = world.EntitySystemManager.GetSingleton(); //Start processing testSystem.RegisterProcess(testEntity, (e, t, d) => { t.timesProcessed++; }); + Logger.WriteLine(testSystem, LogType.TEMP); //Send some signals to check new TestEvent().Raise(testEntity); new TestEvent().Raise(testEntity); new TestEvent().Raise(testEntity); //Wait 1 second - Thread.Sleep(1000); + Thread.Sleep(5000); //Kill the test system off testSystem.Kill(); //Assertions Assert.AreEqual(3, testSystem.timesSignalled); - Assert.IsTrue(testComponent.timesProcessed > 0); + // Close enough + Assert.IsTrue(testComponent.timesProcessed > 45 && testComponent.timesProcessed <= 51, $"TestComponent was processed {testComponent.timesProcessed} times."); + Console.WriteLine($"TestComponent was processed {testComponent.timesProcessed} times."); } } diff --git a/CorgEng.Tests/EntityComponentSystem/SignalTests.cs b/CorgEng.Tests/EntityComponentSystem/SignalTests.cs index b53b2053..fbf303ec 100644 --- a/CorgEng.Tests/EntityComponentSystem/SignalTests.cs +++ b/CorgEng.Tests/EntityComponentSystem/SignalTests.cs @@ -46,7 +46,7 @@ internal class TestEntitySystem : EntitySystem //Just run on everything public override EntitySystemFlags SystemFlags { get; } = EntitySystemFlags.HOST_SYSTEM | EntitySystemFlags.CLIENT_SYSTEM; - public override void SystemSetup() + public override void SystemSetup(IWorld world) { RegisterLocalEvent(HandleTestEvent); RegisterLocalEvent(HandleSecondaryTestEvent); @@ -107,11 +107,11 @@ private void HandleGlobalEvent(TestEvent globalEvent) } [TestClass] - public class SignalTests + public class SignalTests : TestBase { [UsingDependency] - private static IEntityFactory EntityFactory; + private static IWorldFactory WorldFactory; /// /// DONT USE DEPENDNCY INJECTION TO FORCE THIS @@ -124,6 +124,8 @@ public class SignalTests internal volatile static bool passedGlobalTest = false; + private volatile static IWorld world; + private volatile static TestEntitySystem entitySystem; internal volatile static int currentTestId = 0; @@ -133,8 +135,8 @@ public SignalTests() if (entitySystem == null) { Logger?.WriteLine("Setting up test", LogType.LOG); - entitySystem = new TestEntitySystem(); - entitySystem.SystemSetup(); + world = WorldFactory.CreateWorld(); + entitySystem = world.EntitySystemManager.GetSingleton(); } } @@ -154,7 +156,7 @@ public void TestComponentRemoval() Assert.AreEqual(0, handlesReceieved, "INCORRECT TEST CONFIGURATION"); Logger?.WriteLine($"Current thread: {Thread.CurrentThread.ManagedThreadId}. TestID: {currentTestId}", LogType.LOG); //Create a test entity - IEntity testEntity = EntityFactory.CreateEmptyEntity(null); + IEntity testEntity = world.EntityManager.CreateEmptyEntity(null); //Add a test component TestComponent testComponent = new TestComponent(); testEntity.AddComponent(testComponent); @@ -175,7 +177,7 @@ public void TestComponentRemovalIsolation() Assert.AreEqual(0, secondaryHandlesReceieved, "INCORRECT TEST CONFIGURATION"); Logger?.WriteLine($"Current thread: {Thread.CurrentThread.ManagedThreadId}. TestID: {currentTestId}", LogType.LOG); //Create a test entity - IEntity testEntity = EntityFactory.CreateEmptyEntity(null); + IEntity testEntity = world.EntityManager.CreateEmptyEntity(null); //Add a test component TestComponent testComponent = new TestComponent(); testEntity.AddComponent(testComponent); @@ -200,7 +202,7 @@ public void TestSignalHandling() Assert.AreEqual(0, handlesReceieved, "INCORRECT TEST CONFIGURATION"); Logger?.WriteLine($"Current thread: {Thread.CurrentThread.ManagedThreadId}. TestID: {currentTestId}", LogType.LOG); //Create a test entity - IEntity testEntity = EntityFactory.CreateEmptyEntity(null); + IEntity testEntity = world.EntityManager.CreateEmptyEntity(null); //Add a test component TestComponent testComponent = new TestComponent(); testEntity.AddComponent(testComponent); @@ -230,10 +232,10 @@ public void TestGlobalSignalHandling() { Assert.IsFalse(passedGlobalTest, "INCORRECT TEST CONFIGURATION"); Logger?.WriteLine($"Current thread: {Thread.CurrentThread.ManagedThreadId}. TestID: {currentTestId}", LogType.LOG); - new OtherEvent().RaiseGlobally(); + new OtherEvent().RaiseGlobally(world); Thread.Sleep(50); Assert.IsFalse(passedGlobalTest); - new TestEvent().RaiseGlobally(); + new TestEvent().RaiseGlobally(world); while (!passedGlobalTest) Thread.Sleep(1); } @@ -245,7 +247,7 @@ public void TestMultipleSignalHandling() Assert.AreEqual(0, handlesReceieved, "INCORRECT TEST CONFIGURATION"); Logger?.WriteLine($"Current thread: {Thread.CurrentThread.ManagedThreadId}. TestID: {currentTestId}", LogType.LOG); //Create a test entity - IEntity testEntity = EntityFactory.CreateEmptyEntity(null); + IEntity testEntity = world.EntityManager.CreateEmptyEntity(null); //Add a test component TestComponent testComponent = new TestComponent(); testEntity.AddComponent(testComponent); @@ -266,7 +268,7 @@ public void TestSynchronousSignalHandling() Assert.AreEqual(0, handlesReceieved, "INCORRECT TEST CONFIGURATION"); Logger?.WriteLine($"Current thread: {Thread.CurrentThread.ManagedThreadId}. TestID: {currentTestId}", LogType.LOG); //Create a test entity - IEntity testEntity = EntityFactory.CreateEmptyEntity(null); + IEntity testEntity = world.EntityManager.CreateEmptyEntity(null); //Add a test component TestComponent testComponent = new TestComponent(); testEntity.AddComponent(testComponent); @@ -290,7 +292,7 @@ public void TestUnregisteringSignals() Assert.AreEqual(0, handlesReceieved, "INCORRECT TEST CONFIGURATION"); Logger?.WriteLine($"Current thread: {Thread.CurrentThread.ManagedThreadId}. TestID: {currentTestId}", LogType.LOG); //Create a test entity - IEntity testEntity = EntityFactory.CreateEmptyEntity(null); + IEntity testEntity = world.EntityManager.CreateEmptyEntity(null); //Add a test component TestComponent testComponent = new TestComponent(); testEntity.AddComponent(testComponent); @@ -300,8 +302,8 @@ public void TestUnregisteringSignals() new UnregisterEvent().Raise(testEntity, true); new UnregisterEvent().Raise(testEntity, true); Assert.AreEqual(1, handlesReceieved, "Should have receieved 1 handle."); - new UnregisterEvent().RaiseGlobally(true); - new UnregisterEvent().RaiseGlobally(true); + new UnregisterEvent().RaiseGlobally(world, true); + new UnregisterEvent().RaiseGlobally(world, true); Assert.AreEqual(2, handlesReceieved, "Should have receieved 2 handles."); } diff --git a/CorgEng.Tests/EntityComponentSystem/TestEntitySystems.cs b/CorgEng.Tests/EntityComponentSystem/TestEntitySystems.cs new file mode 100644 index 00000000..9a98973b --- /dev/null +++ b/CorgEng.Tests/EntityComponentSystem/TestEntitySystems.cs @@ -0,0 +1,98 @@ +using CorgEng.Core.Dependencies; +using CorgEng.EntityComponentSystem.Components; +using CorgEng.EntityComponentSystem.Events; +using CorgEng.EntityComponentSystem.Systems; +using CorgEng.GenericInterfaces.EntityComponentSystem; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace CorgEng.Tests.EntityComponentSystem +{ + [TestClass] + public class TestEntitySystems : TestBase + { + + [UsingDependency] + private static IWorldFactory WorldFactory = null!; + + public class TestSystem : EntitySystem + { + + public bool testCompleted = false; + + public override EntitySystemFlags SystemFlags => EntitySystemFlags.HOST_SYSTEM | EntitySystemFlags.CLIENT_SYSTEM; + + public override void SystemSetup(IWorld world) + { + RegisterLocalEvent((e, c, s) => { + testCompleted = true; + }); + } + } + + public class TestComponent : Component + { + + } + + public class TestEvent : IEvent + { + + } + + [TestMethod] + public void TestEntitySystem() + { + IWorld world = WorldFactory.CreateWorld(); + IEntity entity = world.EntityManager.CreateEmptyEntity(e => { + e.AddComponent(new TestComponent()); + }); + TestSystem system = world.EntitySystemManager.GetSingleton(); + // Verify + Assert.IsFalse(system.testCompleted); + // Send the signal + new TestEvent().Raise(entity); + // Assertion + Assert.IsTrue(system.testCompleted); + } + + [TestMethod] + [Timeout(200)] + public void TestEntitySystemThreadProcessing() + { + IWorld world = WorldFactory.CreateWorld(); + IEntity entity = world.EntityManager.CreateEmptyEntity(e => { + e.AddComponent(new TestComponent()); + }); + TestSystem system = world.EntitySystemManager.GetSingleton(); + // Verify + Assert.IsFalse(system.testCompleted); + // Send the signal + system.AcquireHighPriorityLock(); + new TestEvent().Raise(entity); + system.ReleaseLock(); + while (!system.testCompleted) + Thread.Sleep(10); + } + + [TestMethod] + public void TestSystemIsolation() + { + IWorld world = WorldFactory.CreateWorld(); + IWorld backgroundWorld = WorldFactory.CreateWorld(); + IEntity entity = world.EntityManager.CreateEmptyEntity(e => { + e.AddComponent(new TestComponent()); + }); + TestSystem backgroundSystem = backgroundWorld.EntitySystemManager.GetSingleton(); + Assert.IsFalse(backgroundSystem.testCompleted); + new TestEvent().Raise(entity); + Assert.IsFalse(backgroundSystem.testCompleted); + } + + } +} diff --git a/CorgEng.Tests/ExperimentationTests.cs b/CorgEng.Tests/ExperimentationTests.cs index 754355f2..165323ef 100644 --- a/CorgEng.Tests/ExperimentationTests.cs +++ b/CorgEng.Tests/ExperimentationTests.cs @@ -12,7 +12,7 @@ namespace CorgEng.Tests /// Class for experimenting with C# features /// [TestClass] - public class ExperimentationTests + public class ExperimentationTests : TestBase { [TestMethod] diff --git a/CorgEng.Tests/FontTests/FontTests.cs b/CorgEng.Tests/FontTests/FontTests.cs index 7dee6d8b..0704906c 100644 --- a/CorgEng.Tests/FontTests/FontTests.cs +++ b/CorgEng.Tests/FontTests/FontTests.cs @@ -11,7 +11,7 @@ namespace CorgEng.Tests.FontTests { [TestClass] - public class FontTests + public class FontTests : TestBase { [UsingDependency] diff --git a/CorgEng.Tests/NetworkingTests/ClientAddressTests.cs b/CorgEng.Tests/NetworkingTests/ClientAddressTests.cs index afb28834..b6accdb3 100644 --- a/CorgEng.Tests/NetworkingTests/ClientAddressTests.cs +++ b/CorgEng.Tests/NetworkingTests/ClientAddressTests.cs @@ -11,7 +11,7 @@ namespace CorgEng.Tests.NetworkingTests { [TestClass] - public class ClientAddressTests + public class ClientAddressTests : TestBase { [UsingDependency] diff --git a/CorgEng.Tests/NetworkingTests/EntityCommunicatorTests.cs b/CorgEng.Tests/NetworkingTests/EntityCommunicatorTests.cs index f7ac4934..ea5319f8 100644 --- a/CorgEng.Tests/NetworkingTests/EntityCommunicatorTests.cs +++ b/CorgEng.Tests/NetworkingTests/EntityCommunicatorTests.cs @@ -18,38 +18,26 @@ namespace CorgEng.Tests.NetworkingTests { [TestClass] - public class EntityCommunicatorTests + public class EntityCommunicatorTests : TestBase { [UsingDependency] - private static IEntityCommunicator EntityCommunicator; - - [UsingDependency] - private static INetworkingServer Server; - - [UsingDependency] - private static INetworkingClient Client; + private static IEntityCommunicatorFactory EntityCommunicatorFactory; [UsingDependency] private static ILogger Logger; [UsingDependency] - private static IEntityFactory EntityFactory; - - [TestCleanup] - public void AfterTest() - { - Server.Cleanup(); - Client.Cleanup(); - Logger?.WriteLine("TEST COMPLETED", LogType.DEBUG); - } + private static IWorldFactory WorldFactory; [TestMethod] [Timeout(1000)] public void TestEntityCommunication() { + IWorld world = WorldFactory.CreateWorld(); + IEntityCommunicator EntityCommunicator = EntityCommunicatorFactory.CreateEntityCommunicator(world); //Alright, connection established. Lets communicate an entity - IEntity testEntity = EntityFactory.CreateEmptyEntity(null); + IEntity testEntity = world.EntityManager.CreateEmptyEntity(null); TestComponent testComponent = new TestComponent(); testComponent.Text = "test"; testComponent.DontSerialise = 5.31262; @@ -59,7 +47,7 @@ public void TestEntityCommunication() //Communicate the entity byte[] serialisedEntity = EntityCommunicator.SerializeEntity(testEntity); //Clear entity manager, so it doesn't know about it - EntityManager.RemoveEntity(testEntity); + world.EntityManager.RemoveEntity(testEntity); //Deserialise the entity IEntity deserialisedEntity = EntityCommunicator.DeserialiseEntity(serialisedEntity).Result; //Perform tests diff --git a/CorgEng.Tests/NetworkingTests/NetworkingTest.cs b/CorgEng.Tests/NetworkingTests/NetworkingTest.cs index fe0f4bc7..2027e885 100644 --- a/CorgEng.Tests/NetworkingTests/NetworkingTest.cs +++ b/CorgEng.Tests/NetworkingTests/NetworkingTest.cs @@ -1,4 +1,5 @@ using CorgEng.Core.Dependencies; +using CorgEng.DependencyInjection.Injection; using CorgEng.EntityComponentSystem.Components; using CorgEng.EntityComponentSystem.Entities; using CorgEng.EntityComponentSystem.Events; @@ -6,11 +7,14 @@ using CorgEng.GenericInterfaces.ContentLoading; using CorgEng.GenericInterfaces.EntityComponentSystem; using CorgEng.GenericInterfaces.Logging; +using CorgEng.GenericInterfaces.Networking.Clients; using CorgEng.GenericInterfaces.Networking.Networking; using CorgEng.GenericInterfaces.Networking.Networking.Client; using CorgEng.GenericInterfaces.Networking.Networking.Server; using CorgEng.GenericInterfaces.Networking.Packets; using CorgEng.Networking.EntitySystems; +using CorgEng.Networking.Networking.Client; +using CorgEng.Networking.Networking.Server; using CorgEng.Networking.VersionSync; using Microsoft.VisualStudio.TestTools.UnitTesting; using System; @@ -30,46 +34,27 @@ public class NetworkingTest { [UsingDependency] - private static INetworkingServer Server; + private static INetworkMessageFactory MessageFactory; [UsingDependency] - private static INetworkingClient Client; - - [UsingDependency] - private static INetworkMessageFactory MessageFactory; + private static IWorldFactory WorldFactory; [UsingDependency] private static ILogger Logger; - private static bool TestRunning = false; - - [TestCleanup] - public void AfterTest() - { - Server.Cleanup(); - Client.Cleanup(); - Logger?.WriteLine("TEST COMPLETED", LogType.DEBUG); - TestRunning = false; - } - - [TestInitialize] - public void SetupServer() - { - Server.GetType().GetMethod("LoadDefaultPrototype").Invoke(Server, new object[0]); - } - [TestMethod] [Timeout(5000)] public void TestNetworkConnection() { - if (TestRunning) - Assert.Fail("Attempted to test while testing."); - TestRunning = true; bool success = false; - Server.StartHosting(5000); - Client.OnConnectionSuccess += (IPAddress ipAddress) => { success = true; }; - Client.OnConnectionFailed += (IPAddress ipAddress, DisconnectReason disconnectReason, string reasonText) => { Assert.Fail("Connection failed, server rejected connection."); }; - Client.AttemptConnection("127.0.0.1", 5000, 1000); + + IWorld clientWorld = WorldFactory.CreateWorld(); + IWorld serverWorld = WorldFactory.CreateWorld(); + + serverWorld.ServerInstance.StartHosting(5000); + clientWorld.ClientInstance.OnConnectionSuccess += (IPAddress ipAddress) => { success = true; }; + clientWorld.ClientInstance.OnConnectionFailed += (IPAddress ipAddress, DisconnectReason disconnectReason, string reasonText) => { Assert.Fail("Connection failed, server rejected connection."); }; + clientWorld.ClientInstance.AttemptConnection("127.0.0.1", 5000, 1000); while (!success) Thread.Sleep(0); @@ -80,16 +65,17 @@ public void TestNetworkConnection() [Timeout(3000)] public void TestSendingToServer() { - if (TestRunning) - Assert.Fail("Attempted to test while testing."); - TestRunning = true; bool connected = false; bool success = false; - Server.StartHosting(5001); - Client.OnConnectionFailed += (IPAddress ipAddress, DisconnectReason disconnectReason, string reasonText) => { Assert.Inconclusive("Connection failed, server rejected connection."); }; - Client.OnConnectionSuccess += (IPAddress ipAddress) => { connected = true; }; - Server.NetworkMessageReceived += (PacketHeaders packetHeader, byte[] message, int start, int end) => + IWorld clientWorld = WorldFactory.CreateWorld(); + IWorld serverWorld = WorldFactory.CreateWorld(); + + serverWorld.ServerInstance.StartHosting(5001); + clientWorld.ClientInstance.OnConnectionFailed += (IPAddress ipAddress, DisconnectReason disconnectReason, string reasonText) => { Assert.Inconclusive("Connection failed, server rejected connection."); }; + clientWorld.ClientInstance.OnConnectionSuccess += (IPAddress ipAddress) => { connected = true; }; + + serverWorld.ServerInstance.NetworkMessageReceived += (PacketHeaders packetHeader, byte[] message, int start, int end) => { if (packetHeader == PacketHeaders.NETWORKING_TEST) { @@ -100,14 +86,14 @@ public void TestSendingToServer() } }; - Client.AttemptConnection("127.0.0.1", 5001, 1000); + clientWorld.ClientInstance.AttemptConnection("127.0.0.1", 5001, 1000); //Await connection to the server while (!connected) Thread.Sleep(0); //Send a server message to the client - Client.QueueMessage( + clientWorld.ClientInstance.QueueMessage( MessageFactory.CreateMessage(PacketHeaders.NETWORKING_TEST, Encoding.ASCII.GetBytes("CORRECT")) ); @@ -120,15 +106,16 @@ public void TestSendingToServer() [Timeout(3000)] public void TestSendingToClient() { - if (TestRunning) - Assert.Fail("Attempted to test while testing."); - TestRunning = true; bool connected = false; bool success = false; - Server.StartHosting(5002); - Client.OnConnectionFailed += (IPAddress ipAddress, DisconnectReason disconnectReason, string reasonText) => { Assert.Inconclusive("Connection failed, server rejected connection."); }; - Client.OnConnectionSuccess += (IPAddress ipAddress) => { connected = true; }; - Client.NetworkMessageReceived += (PacketHeaders packetHeader, byte[] message, int start, int end) => + + IWorld clientWorld = WorldFactory.CreateWorld(); + IWorld serverWorld = WorldFactory.CreateWorld(); + + serverWorld.ServerInstance.StartHosting(5002); + clientWorld.ClientInstance.OnConnectionFailed += (IPAddress ipAddress, DisconnectReason disconnectReason, string reasonText) => { Assert.Inconclusive("Connection failed, server rejected connection."); }; + clientWorld.ClientInstance.OnConnectionSuccess += (IPAddress ipAddress) => { connected = true; }; + clientWorld.ClientInstance.NetworkMessageReceived += (PacketHeaders packetHeader, byte[] message, int start, int end) => { if (packetHeader == PacketHeaders.NETWORKING_TEST) { @@ -138,15 +125,15 @@ public void TestSendingToClient() success = true; } }; - Client.AttemptConnection("127.0.0.1", 5002, 1000); + clientWorld.ClientInstance.AttemptConnection("127.0.0.1", 5002, 1000); //Await connection to the server while (!connected) Thread.Sleep(0); //Send a server message to the client - Server.QueueMessage( - Server.ClientAddressingTable.GetEveryone(), + serverWorld.ServerInstance.QueueMessage( + serverWorld.ServerInstance.ClientAddressingTable.GetEveryone(), MessageFactory.CreateMessage(PacketHeaders.NETWORKING_TEST, Encoding.ASCII.GetBytes("CORRECT")) ); @@ -158,9 +145,6 @@ public void TestSendingToClient() [Timeout(5000)] public void TestClientKick() { - if (TestRunning) - Assert.Fail("Attempted to test while testing."); - TestRunning = true; Assert.Inconclusive("Test isn't implemented"); } @@ -168,9 +152,6 @@ public void TestClientKick() [Timeout(5000)] public void TestBanning() { - if (TestRunning) - Assert.Fail("Attempted to test while testing."); - TestRunning = true; Assert.Inconclusive("Test isn't implemented"); } @@ -208,7 +189,7 @@ private class NetworkedTestEntitySystem : EntitySystem public override EntitySystemFlags SystemFlags { get; } = EntitySystemFlags.HOST_SYSTEM | EntitySystemFlags.CLIENT_SYSTEM; - public override void SystemSetup() + public override void SystemSetup(IWorld world) { RegisterGlobalEvent(AcceptTestEvent); } @@ -231,23 +212,23 @@ private void AcceptTestEvent(NetworkedTestEvent networkedTestEvent) [Timeout(3000)] public void TestGlobalNetworkedEvent() { - if (TestRunning) - Assert.Fail("Attempted to test while testing."); - TestRunning = true; - //Set up a test entity system - NetworkedTestEntitySystem networkedTestEntitySystem = new NetworkedTestEntitySystem(); - networkedTestEntitySystem.SystemSetup(); + // Currently broken + Assert.Inconclusive("This test currently doesn't work."); + IWorld clientWorld = WorldFactory.CreateWorld(); + IWorld serverWorld = WorldFactory.CreateWorld(); + + //Set up a test entity system + NetworkedTestEntitySystem networkedTestEntitySystem = clientWorld.EntitySystemManager.GetSingleton(); //Start a networking system - NetworkSystem networkSystem = new NetworkSystem(); - networkSystem.SystemSetup(); + NetworkSystem networkSystem = clientWorld.EntitySystemManager.GetSingleton(); //Connect to the server bool success = false; - Server.StartHosting(5003); - Client.OnConnectionSuccess += (IPAddress ipAddress) => { success = true; }; - Client.OnConnectionFailed += (IPAddress ipAddress, DisconnectReason disconnectReason, string reasonText) => { Assert.Fail("Connection failed, server rejected connection."); }; - Client.AttemptConnection("127.0.0.1", 5003, 1000); + serverWorld.ServerInstance.StartHosting(5003); + clientWorld.ClientInstance.OnConnectionSuccess += (IPAddress ipAddress) => { success = true; }; + clientWorld.ClientInstance.OnConnectionFailed += (IPAddress ipAddress, DisconnectReason disconnectReason, string reasonText) => { Assert.Fail("Connection failed, server rejected connection."); }; + clientWorld.ClientInstance.AttemptConnection("127.0.0.1", 5003, 1000); while (!success) Thread.Sleep(0); @@ -255,7 +236,7 @@ public void TestGlobalNetworkedEvent() //Raise a global event NetworkedTestEvent testEvent = new NetworkedTestEvent(); testEvent.testNumber = 142; - testEvent.RaiseGlobally(); + testEvent.RaiseGlobally(serverWorld); Logger?.WriteLine($"Test event raised globally, ID: {testEvent.GetNetworkedIdentifier()}", LogType.DEBUG); diff --git a/CorgEng.Tests/NetworkingTests/PacketQueueTests.cs b/CorgEng.Tests/NetworkingTests/PacketQueueTests.cs index 43149b10..f45b921f 100644 --- a/CorgEng.Tests/NetworkingTests/PacketQueueTests.cs +++ b/CorgEng.Tests/NetworkingTests/PacketQueueTests.cs @@ -12,7 +12,7 @@ namespace CorgEng.Tests.NetworkingTests { [TestClass] - public class PacketQueueTests + public class PacketQueueTests : TestBase { [UsingDependency] diff --git a/CorgEng.Tests/NetworkingTests/PrototypeTests.cs b/CorgEng.Tests/NetworkingTests/PrototypeTests.cs index 4a524c88..464a45b7 100644 --- a/CorgEng.Tests/NetworkingTests/PrototypeTests.cs +++ b/CorgEng.Tests/NetworkingTests/PrototypeTests.cs @@ -39,46 +39,37 @@ public class TestComponent : Component } [TestClass] - public class PrototypeTests + public class PrototypeTests : TestBase { [UsingDependency] - private static IEntityFactory EntityFactory; + private static IWorldFactory WorldFactory; [UsingDependency] private static IPrototypeManager PrototypeManager; - [UsingDependency] - private static INetworkingServer Server; - - [UsingDependency] - private static INetworkingClient Client; - [UsingDependency] private static ILogger Logger; - [TestCleanup] - public void AfterTest() - { - Server.Cleanup(); - Client.Cleanup(); - } - [TestMethod] [Timeout(10000)] public void TestPrototypes() { bool success = false; - Server.StartHosting(5000); - Client.OnConnectionSuccess += (IPAddress ipAddress) => { success = true; }; - Client.OnConnectionFailed += (IPAddress ipAddress, DisconnectReason disconnectReason, string reasonText) => { Assert.Fail("Connection failed, server rejected connection."); }; - Client.AttemptConnection("127.0.0.1", 5000, 1000); + + IWorld clientWorld = WorldFactory.CreateWorld(); + IWorld serverWorld = WorldFactory.CreateWorld(); + + serverWorld.ServerInstance.StartHosting(5000); + clientWorld.ClientInstance.OnConnectionSuccess += (IPAddress ipAddress) => { success = true; }; + clientWorld.ClientInstance.OnConnectionFailed += (IPAddress ipAddress, DisconnectReason disconnectReason, string reasonText) => { Assert.Fail("Connection failed, server rejected connection."); }; + clientWorld.ClientInstance.AttemptConnection("127.0.0.1", 5000, 1000); while (!success) Thread.Sleep(0); //Create an entity - IEntity entity = EntityFactory.CreateEmptyEntity(null); + IEntity entity = serverWorld.EntityManager.CreateEmptyEntity(null); TestComponent testComponent = new TestComponent(); testComponent.Integer = 59; testComponent.Text = "Hello World!"; @@ -95,7 +86,7 @@ public void TestPrototypes() //Deserialize the entity's prototype IPrototype deserialisedPrototype = PrototypeManager.GetPrototype(serialisedPrototype); - IEntity createdEntity = deserialisedPrototype.CreateEntityFromPrototype(); //We don't care about the identifier for this test + IEntity createdEntity = deserialisedPrototype.CreateEntityFromPrototype(clientWorld); //We don't care about the identifier for this test //Verify the entity is correct Assert.AreEqual(entity.Components.Count, createdEntity.Components.Count); TestComponent deserializedComponent = (TestComponent)createdEntity.Components[1]; diff --git a/CorgEng.Tests/NetworkingTests/SerializationTest.cs b/CorgEng.Tests/NetworkingTests/SerializationTest.cs index e2f85c69..85cfdfba 100644 --- a/CorgEng.Tests/NetworkingTests/SerializationTest.cs +++ b/CorgEng.Tests/NetworkingTests/SerializationTest.cs @@ -20,7 +20,7 @@ namespace CorgEng.Tests.NetworkingTests { [TestClass] - public class SerializationTest + public class SerializationTest : TestBase { [UsingDependency] diff --git a/CorgEng.Tests/NetworkingTests/TestCVars.cs b/CorgEng.Tests/NetworkingTests/TestCVars.cs new file mode 100644 index 00000000..0da7caf4 --- /dev/null +++ b/CorgEng.Tests/NetworkingTests/TestCVars.cs @@ -0,0 +1,93 @@ +using CorgEng.Core.Dependencies; +using CorgEng.EntityComponentSystem.Components; +using CorgEng.EntityComponentSystem.Components.ComponentVariables.Networking; +using CorgEng.EntityComponentSystem.Entities; +using CorgEng.GenericInterfaces.ContentLoading; +using CorgEng.GenericInterfaces.EntityComponentSystem; +using CorgEng.GenericInterfaces.Logging; +using CorgEng.GenericInterfaces.Networking.Clients; +using CorgEng.GenericInterfaces.Networking.Networking; +using CorgEng.GenericInterfaces.Networking.Networking.Client; +using CorgEng.GenericInterfaces.Networking.Networking.Server; +using CorgEng.GenericInterfaces.Networking.Packets; +using CorgEng.Networking.Components; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace CorgEng.Tests.NetworkingTests +{ + + public class ExampleComponent : Component + { + + public NetCVar exampleNetvar = new NetCVar(5); + + } + + + [TestClass] + public class TestCVars : TestBase + { + + [UsingDependency] + private static IWorldFactory WorldFactory; + + [UsingDependency] + private static INetworkMessageFactory MessageFactory; + + [UsingDependency] + private static ILogger Logger; + + private IWorld serverWorld; + private IWorld clientWorld; + + [TestInitialize] + public void TestStart() + { + serverWorld = WorldFactory.CreateWorld(); + clientWorld = WorldFactory.CreateWorld(); + + bool connected = false; + bool success = false; + serverWorld.ServerInstance.StartHosting(5001); + clientWorld.ClientInstance.OnConnectionFailed += (IPAddress ipAddress, DisconnectReason disconnectReason, string reasonText) => { Assert.Inconclusive("Connection failed, server rejected connection."); }; + clientWorld.ClientInstance.OnConnectionSuccess += (IPAddress ipAddress) => { connected = true; }; + + serverWorld.ServerInstance.NetworkMessageReceived += (PacketHeaders packetHeader, byte[] message, int start, int end) => + { + if (packetHeader == PacketHeaders.NETWORKING_TEST) + { + string gotMessage = Encoding.ASCII.GetString(message.Skip(start).Take(end).ToArray()); + if (gotMessage != "CORRECT") + Assert.Fail($"Recieved message did not contain correct content, content recieved: '{gotMessage}'"); + success = true; + } + }; + + clientWorld.ClientInstance.AttemptConnection("127.0.0.1", 5001, 1000); + + //Await connection to the server + while (!connected) + Thread.Sleep(0); + } + + [TestMethod] + public void TestNetVarModification() + { + // Create the entity + IEntity createdEntity = serverWorld.EntityManager.CreateEmptyEntity(entity => { + entity.AddComponent(new NetworkTransformComponent()); + entity.AddComponent(new ExampleComponent()); + }); + // + Assert.Inconclusive("I haven't made this"); + } + + } +} diff --git a/CorgEng.Tests/Pathfinding/PathfindingTests.cs b/CorgEng.Tests/Pathfinding/PathfindingTests.cs index 3c8e1779..1b229450 100644 --- a/CorgEng.Tests/Pathfinding/PathfindingTests.cs +++ b/CorgEng.Tests/Pathfinding/PathfindingTests.cs @@ -16,7 +16,7 @@ namespace CorgEng.Tests.Pathfinding { [TestClass] - public class PathfindingTests + public class PathfindingTests : TestBase { private class PathfindingTestQueryer : IPathCellQueryer diff --git a/CorgEng.Tests/Performance/EventProcessing.cs b/CorgEng.Tests/Performance/EventProcessing.cs index f7d035bb..f665e8c8 100644 --- a/CorgEng.Tests/Performance/EventProcessing.cs +++ b/CorgEng.Tests/Performance/EventProcessing.cs @@ -1,5 +1,6 @@ //#define PERFORMANCE_TEST +using CorgEng.Core.Dependencies; using CorgEng.EntityComponentSystem.Events; using CorgEng.EntityComponentSystem.Systems; using CorgEng.GenericInterfaces.EntityComponentSystem; @@ -24,6 +25,9 @@ namespace CorgEng.Tests.Performance public class EventProcessing { + [UsingDependency] + private static IWorldFactory WorldFactory; + private static int EventsHandled = 0; private static int LateHandles = 0; private static bool Running = false; @@ -35,7 +39,7 @@ private class TestEntitySystem : EntitySystem { public override EntitySystemFlags SystemFlags => EntitySystemFlags.HOST_SYSTEM; - public override void SystemSetup() + public override void SystemSetup(IWorld world) { RegisterGlobalEvent(HandleEvent); } @@ -59,10 +63,12 @@ public void TestAsyncEventPerformance() #if !PERFORMANCE_TEST Assert.Inconclusive("Test not executed. Please enable PERFORMANCE_TEST define in order to test performance."); #endif + + IWorld world = WorldFactory.CreateWorld(); + EventsHandled = 0; LateHandles = 0; - TestEntitySystem testEntitySystem = new TestEntitySystem(); - testEntitySystem.SystemSetup(); + TestEntitySystem testEntitySystem = world.EntitySystemManager.GetSingleton(); //Perform the tests Running = true; @@ -71,7 +77,7 @@ public void TestAsyncEventPerformance() while (Running) { //Perform a run - new TestEvent().RaiseGlobally(); + new TestEvent().RaiseGlobally(world); //Run completed runs++; } @@ -91,10 +97,11 @@ public void TestSynchronousEventPerformance() #if !PERFORMANCE_TEST Assert.Inconclusive("Test not executed. Please enable PERFORMANCE_TEST define in order to test performance."); #endif + IWorld world = WorldFactory.CreateWorld(); + EventsHandled = 0; LateHandles = 0; - TestEntitySystem testEntitySystem = new TestEntitySystem(); - testEntitySystem.SystemSetup(); + TestEntitySystem testEntitySystem = world.EntitySystemManager.GetSingleton(); //Perform the tests Running = true; @@ -103,7 +110,7 @@ public void TestSynchronousEventPerformance() while (Running) { //Perform a run - new TestEvent().RaiseGlobally(true); + new TestEvent().RaiseGlobally(world, true); //Run completed runs++; } @@ -123,10 +130,11 @@ public void TestMethodCallingPerformance() #if !PERFORMANCE_TEST Assert.Inconclusive("Test not executed. Please enable PERFORMANCE_TEST define in order to test performance."); #endif + IWorld world = WorldFactory.CreateWorld(); + EventsHandled = 0; LateHandles = 0; - TestEntitySystem testEntitySystem = new TestEntitySystem(); - testEntitySystem.SystemSetup(); + TestEntitySystem testEntitySystem = world.EntitySystemManager.GetSingleton(); //Perform the tests Running = true; diff --git a/CorgEng.Tests/Performance/SerialisationPerformance.cs b/CorgEng.Tests/Performance/SerialisationPerformance.cs index d29dc3f0..c1e75ec6 100644 --- a/CorgEng.Tests/Performance/SerialisationPerformance.cs +++ b/CorgEng.Tests/Performance/SerialisationPerformance.cs @@ -16,14 +16,14 @@ namespace CorgEng.Tests.Performance { [TestClass] - public class SerialisationPerformance + public class SerialisationPerformance : TestBase { [UsingDependency] - private static IEntityFactory EntityFactory; + private static IPrototypeManager PrototypeManager; [UsingDependency] - private static IPrototypeManager PrototypeManager; + private static IWorldFactory WorldFactory; private const int TEST_TIME = 5000; @@ -33,8 +33,9 @@ public void TestEntityToPrototypePerformance() #if !PERFORMANCE_TEST Assert.Inconclusive("Test not executed. Please enable PERFORMANCE_TEST define in order to test performance."); #endif + IWorld testWorld = WorldFactory.CreateWorld(); //Create an entity - IEntity entity = EntityFactory.CreateEmptyEntity(null); + IEntity entity = testWorld.EntityManager.CreateEmptyEntity(null); TestComponent testComponent = new TestComponent(); testComponent.Integer = 59; testComponent.Text = "Hello World!"; @@ -66,8 +67,9 @@ public void TestPrototypeToEntityPerformance() #if !PERFORMANCE_TEST Assert.Inconclusive("Test not executed. Please enable PERFORMANCE_TEST define in order to test performance."); #endif + IWorld testWorld = WorldFactory.CreateWorld(); //Create an entity - IEntity entity = EntityFactory.CreateEmptyEntity(null); + IEntity entity = testWorld.EntityManager.CreateEmptyEntity(null); TestComponent testComponent = new TestComponent(); testComponent.Integer = 59; testComponent.Text = "Hello World!"; @@ -83,7 +85,7 @@ public void TestPrototypeToEntityPerformance() while (running) { //Perform a run, don't care about the identifier - collectedPrototype.CreateEntityFromPrototype(); + collectedPrototype.CreateEntityFromPrototype(testWorld); //Run completed runs++; } @@ -101,9 +103,10 @@ public void TestDeserialisationPerformance() #if !PERFORMANCE_TEST Assert.Inconclusive("Test not executed. Please enable PERFORMANCE_TEST define in order to test performance."); #endif + IWorld testWorld = WorldFactory.CreateWorld(); //Create an entity - IEntity entity = EntityFactory.CreateEmptyEntity(null); + IEntity entity = testWorld.EntityManager.CreateEmptyEntity(null); TestComponent testComponent = new TestComponent(); testComponent.Integer = 59; testComponent.Text = "Hello World!"; @@ -138,9 +141,10 @@ public void TestSerialisationPerformance() #if !PERFORMANCE_TEST Assert.Inconclusive("Test not executed. Please enable PERFORMANCE_TEST define in order to test performance."); #endif + IWorld testWorld = WorldFactory.CreateWorld(); //Create an entity - IEntity entity = EntityFactory.CreateEmptyEntity(null); + IEntity entity = testWorld.EntityManager.CreateEmptyEntity(null); TestComponent testComponent = new TestComponent(); testComponent.Integer = 59; testComponent.Text = "Hello World!"; diff --git a/CorgEng.Tests/Performance/ThreadTest.cs b/CorgEng.Tests/Performance/ThreadTest.cs index e66bb066..e766a61b 100644 --- a/CorgEng.Tests/Performance/ThreadTest.cs +++ b/CorgEng.Tests/Performance/ThreadTest.cs @@ -15,7 +15,7 @@ namespace CorgEng.Tests.Performance { [TestClass] - public class ThreadTest + public class ThreadTest : TestBase { [UsingDependency] diff --git a/CorgEng.Tests/Performance/UnsortedTests.cs b/CorgEng.Tests/Performance/UnsortedTests.cs new file mode 100644 index 00000000..82bc61fd --- /dev/null +++ b/CorgEng.Tests/Performance/UnsortedTests.cs @@ -0,0 +1,127 @@ +//#define PERFORMANCE_TEST + +using CorgEng.EntityComponentSystem.Components; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace CorgEng.Tests.Performance +{ + [TestClass] + [DoNotParallelize] + public class UnsortedTests + { + + private class Test + { + + } + + private class TestA : Test + { } + private class TestB : Test + { } + private class TestC : Test + { } + private class TestD : Test + { } + + private const int TEST_TIME = 5000; + + private Dictionary testDictionaries = new Dictionary() { + { typeof(TestA), new TestA() }, + { typeof(TestB), new TestB() }, + { typeof(TestC), new TestC() }, + { typeof(TestD), new TestD() }, + }; + + private sealed class TestLookup + where T : Test + { + internal static T Tester = Activator.CreateInstance(); + } + + [TestMethod] + public void DictionaryLookup() + { +#if !PERFORMANCE_TEST + Assert.Inconclusive("Test not executed. Please enable PERFORMANCE_TEST define in order to test performance."); +#endif + + //Perform the tests + bool running = true; + int runs = 0; + Thread thread = new Thread(() => { + while (running) + { + switch (runs % 4) + { + case 0: + Test thing = testDictionaries[typeof(TestA)]; + break; + case 1: + Test thing1 = testDictionaries[typeof(TestB)]; + break; + case 2: + Test thing2 = testDictionaries[typeof(TestC)]; + break; + case 3: + Test thing3 = testDictionaries[typeof(TestD)]; + break; + } + //Run completed + runs++; + } + }); + thread.Start(); + Thread.Sleep(TEST_TIME); + running = false; + //Process results + Assert.Inconclusive($"Performed {runs} prototype gets in {TEST_TIME}ms at a rate of {runs / (TEST_TIME / 1000f)}/s"); + } + + [TestMethod] + public void TypeLookup() + { +#if !PERFORMANCE_TEST + Assert.Inconclusive("Test not executed. Please enable PERFORMANCE_TEST define in order to test performance."); +#endif + + //Perform the tests + bool running = true; + int runs = 0; + Thread thread = new Thread(() => { + while (running) + { + switch (runs % 4) + { + case 0: + Test thing = TestLookup.Tester; + break; + case 1: + Test thing1 = TestLookup.Tester; + break; + case 2: + Test thing2 = TestLookup.Tester; + break; + case 3: + Test thing3 = TestLookup.Tester; + break; + } + //Run completed + runs++; + } + }); + thread.Start(); + Thread.Sleep(TEST_TIME); + running = false; + //Process results + Assert.Inconclusive($"Performed {runs} prototype gets in {TEST_TIME}ms at a rate of {runs / (TEST_TIME / 1000f)}/s"); + } + + } +} diff --git a/CorgEng.Tests/RenderingTests/BindedBatchTest.cs b/CorgEng.Tests/RenderingTests/BindedBatchTest.cs index 2cbcb2b7..1774ee1f 100644 --- a/CorgEng.Tests/RenderingTests/BindedBatchTest.cs +++ b/CorgEng.Tests/RenderingTests/BindedBatchTest.cs @@ -13,7 +13,7 @@ namespace CorgEng.Tests.RenderingTests { [TestClass] - public class BindedBatchTest + public class BindedBatchTest : TestBase { private class TestBatch : Batch diff --git a/CorgEng.Tests/RenderingTests/TransparencyLoadingTests.cs b/CorgEng.Tests/RenderingTests/TransparencyLoadingTests.cs index f4fbdddc..24b42e80 100644 --- a/CorgEng.Tests/RenderingTests/TransparencyLoadingTests.cs +++ b/CorgEng.Tests/RenderingTests/TransparencyLoadingTests.cs @@ -11,7 +11,7 @@ namespace CorgEng.Tests.RenderingTests { [TestClass] - public class TransparencyLoadingTests + public class TransparencyLoadingTests : TestBase { [UsingDependency] diff --git a/CorgEng.Tests/Stubs/RenderCoreStub.cs b/CorgEng.Tests/Stubs/RenderCoreStub.cs index fee30874..72f55bc5 100644 --- a/CorgEng.Tests/Stubs/RenderCoreStub.cs +++ b/CorgEng.Tests/Stubs/RenderCoreStub.cs @@ -17,7 +17,7 @@ internal class RenderCoreStub : IRenderCore public int Height => 0; - public void DoRender() + public void DoRender(Action preRenderAction = null) { return; } diff --git a/CorgEng.Tests/TestBase.cs b/CorgEng.Tests/TestBase.cs new file mode 100644 index 00000000..74141a29 --- /dev/null +++ b/CorgEng.Tests/TestBase.cs @@ -0,0 +1,20 @@ +using CorgEng.Core; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CorgEng.Tests +{ + [TestClass] + public class TestBase + { + [TestCleanup] + public void TestCleanup() + { + CorgEngMain.Cleanup(); + } + } +} diff --git a/CorgEng.Tests/TestSetup.cs b/CorgEng.Tests/TestSetup.cs index 2f3775c8..52368333 100644 --- a/CorgEng.Tests/TestSetup.cs +++ b/CorgEng.Tests/TestSetup.cs @@ -18,7 +18,7 @@ namespace CorgEng.Tests { [TestClass] - public class TestSetup + public class TestSetup : TestBase { [AssemblyInitialize] diff --git a/CorgEng.Tests/UserInterfaceTests/UnitTests/UserInterfaceButtonTests.cs b/CorgEng.Tests/UserInterfaceTests/UnitTests/UserInterfaceButtonTests.cs index 33379a30..bc3b3c0f 100644 --- a/CorgEng.Tests/UserInterfaceTests/UnitTests/UserInterfaceButtonTests.cs +++ b/CorgEng.Tests/UserInterfaceTests/UnitTests/UserInterfaceButtonTests.cs @@ -1,5 +1,6 @@ using CorgEng.Core.Dependencies; using CorgEng.EntityComponentSystem.Events; +using CorgEng.GenericInterfaces.EntityComponentSystem; using CorgEng.GenericInterfaces.UserInterface.Components; using CorgEng.GenericInterfaces.UserInterface.Generators; using CorgEng.UserInterface.Attributes; @@ -17,12 +18,17 @@ namespace CorgEng.Tests.UserInterfaceTests.UnitTests { [TestClass] - public class UserInterfaceButtonTests + public class UserInterfaceButtonTests : TestBase { [UsingDependency] private static IUserInterfaceXmlLoader UserInterfaceXmlLoader; + [UsingDependency] + private static IWorldFactory WorldFactory; + + private static IWorld world; + private static bool TestPassed = false; [UserInterfaceCodeCallback("PassTest")] @@ -39,8 +45,7 @@ public void SetupTests() if (!setup) { setup = true; - UserInterfaceClickSystem clickSystem = new UserInterfaceClickSystem(); - clickSystem.SystemSetup(); + world = WorldFactory.CreateWorld(); } } @@ -54,7 +59,7 @@ public void TestUserInterfaceButton() if (UserInterfaceXmlLoader == null) Assert.Inconclusive("User interface XML loader dependency was not injected."); //Check for loading errors - IUserInterfaceComponent Root = UserInterfaceXmlLoader.LoadUserInterface("UserInterfaceTests/UnitTests/Content/UserInterfaceButton.xml"); + IUserInterfaceComponent Root = UserInterfaceXmlLoader.LoadUserInterface(world, "UserInterfaceTests/UnitTests/Content/UserInterfaceButton.xml"); IUserInterfaceComponent button = Root.GetChildren().First(); new UserInterfaceClickEvent().Raise(button.ComponentHolder); while (!TestPassed) diff --git a/CorgEng.Tests/UserInterfaceTests/UnitTests/UserInterfaceComponentTests.cs b/CorgEng.Tests/UserInterfaceTests/UnitTests/UserInterfaceComponentTests.cs index 3c440ece..0348b87a 100644 --- a/CorgEng.Tests/UserInterfaceTests/UnitTests/UserInterfaceComponentTests.cs +++ b/CorgEng.Tests/UserInterfaceTests/UnitTests/UserInterfaceComponentTests.cs @@ -1,5 +1,6 @@ using CorgEng.Core.Dependencies; using CorgEng.DependencyInjection.Injection; +using CorgEng.GenericInterfaces.EntityComponentSystem; using CorgEng.GenericInterfaces.UserInterface.Anchors; using CorgEng.GenericInterfaces.UserInterface.Components; using Microsoft.VisualStudio.TestTools.UnitTesting; @@ -12,7 +13,7 @@ namespace CorgEng.Tests.UserInterfaceTests.UnitTests { [TestClass] - public class UserInterfaceComponentTests + public class UserInterfaceComponentTests : TestBase { [UsingDependency] @@ -24,6 +25,9 @@ public class UserInterfaceComponentTests [UsingDependency] private static IAnchorDetailFactory AnchorDetailFactory; + [UsingDependency] + private static IWorldFactory WorldFactory; + /// /// This test verifies that the dependencies have been implemented. /// @@ -48,10 +52,12 @@ public void TestParentInterfaceExpansionPercentages() Assert.Inconclusive("Anchor factory not located."); if (UserInterfaceComponentFactory == null) Assert.Inconclusive("User interface factory not located."); + IWorld world = WorldFactory.CreateWorld(); //Create a root component IUserInterfaceComponent expandingComponent = null; IUserInterfaceComponent bigComponent = null; IUserInterfaceComponent parentUserInterfaceComponent = UserInterfaceComponentFactory.CreateGenericUserInterfaceComponent( + world, AnchorFactory.CreateAnchor( AnchorDetailFactory.CreateAnchorDetails(AnchorDirections.LEFT, AnchorUnits.PIXELS, 0), AnchorDetailFactory.CreateAnchorDetails(AnchorDirections.RIGHT, AnchorUnits.PIXELS, 0), @@ -61,6 +67,7 @@ public void TestParentInterfaceExpansionPercentages() createdComponent => { //Add a child component which can scale but starts with no height expandingComponent = UserInterfaceComponentFactory.CreateGenericUserInterfaceComponent( + world, createdComponent, AnchorFactory.CreateAnchor( AnchorDetailFactory.CreateAnchorDetails(AnchorDirections.LEFT, AnchorUnits.PERCENTAGE, 10, true), @@ -71,6 +78,7 @@ public void TestParentInterfaceExpansionPercentages() subCreatedComponent => { //Add a child component to that which has a huge scale bigComponent = UserInterfaceComponentFactory.CreateGenericUserInterfaceComponent( + world, subCreatedComponent, AnchorFactory.CreateAnchor( AnchorDetailFactory.CreateAnchorDetails(AnchorDirections.LEFT, AnchorUnits.PIXELS, 0, true), @@ -117,8 +125,10 @@ public void TestParentInterfaceExpansion() Assert.Inconclusive("Anchor factory not located."); if (UserInterfaceComponentFactory == null) Assert.Inconclusive("User interface factory not located."); + IWorld world = WorldFactory.CreateWorld(); //Create a root component IUserInterfaceComponent parentUserInterfaceComponent = UserInterfaceComponentFactory.CreateGenericUserInterfaceComponent( + world, AnchorFactory.CreateAnchor( AnchorDetailFactory.CreateAnchorDetails(AnchorDirections.LEFT, AnchorUnits.PIXELS, 0), AnchorDetailFactory.CreateAnchorDetails(AnchorDirections.RIGHT, AnchorUnits.PIXELS, 0), @@ -130,6 +140,7 @@ public void TestParentInterfaceExpansion() parentUserInterfaceComponent.SetWidth(1000, 1000); //Add a child component which can scale but starts with no height IUserInterfaceComponent expandingComponent = UserInterfaceComponentFactory.CreateGenericUserInterfaceComponent( + world, parentUserInterfaceComponent, AnchorFactory.CreateAnchor( AnchorDetailFactory.CreateAnchorDetails(AnchorDirections.LEFT, AnchorUnits.PIXELS, 100, true), @@ -141,6 +152,7 @@ public void TestParentInterfaceExpansion() ); //Add a child component to that which has a huge scale IUserInterfaceComponent bigComponent = UserInterfaceComponentFactory.CreateGenericUserInterfaceComponent( + world, expandingComponent, AnchorFactory.CreateAnchor( AnchorDetailFactory.CreateAnchorDetails(AnchorDirections.LEFT, AnchorUnits.PIXELS, 0, true), @@ -183,8 +195,10 @@ public void TestExpansionWithMargin() Assert.Inconclusive("Anchor factory not located."); if (UserInterfaceComponentFactory == null) Assert.Inconclusive("User interface factory not located."); + IWorld world = WorldFactory.CreateWorld(); //Create a root component IUserInterfaceComponent parentUserInterfaceComponent = UserInterfaceComponentFactory.CreateGenericUserInterfaceComponent( + world, AnchorFactory.CreateAnchor( AnchorDetailFactory.CreateAnchorDetails(AnchorDirections.LEFT, AnchorUnits.PIXELS, 0), AnchorDetailFactory.CreateAnchorDetails(AnchorDirections.RIGHT, AnchorUnits.PIXELS, 0), @@ -196,6 +210,7 @@ public void TestExpansionWithMargin() parentUserInterfaceComponent.SetWidth(1000, 1000); //Add a child component which can scale but starts with no height IUserInterfaceComponent expandingComponent = UserInterfaceComponentFactory.CreateGenericUserInterfaceComponent( + world, parentUserInterfaceComponent, AnchorFactory.CreateAnchor( AnchorDetailFactory.CreateAnchorDetails(AnchorDirections.LEFT, AnchorUnits.PIXELS, 100, true), @@ -207,6 +222,7 @@ public void TestExpansionWithMargin() ); //Add a child component to that which has a huge scale IUserInterfaceComponent bigComponent = UserInterfaceComponentFactory.CreateGenericUserInterfaceComponent( + world, expandingComponent, AnchorFactory.CreateAnchor( AnchorDetailFactory.CreateAnchorDetails(AnchorDirections.LEFT, AnchorUnits.PIXELS, 0, true), @@ -238,8 +254,10 @@ public void TestMinimumScale() Assert.Inconclusive("Anchor factory not located."); if (UserInterfaceComponentFactory == null) Assert.Inconclusive("User interface factory not located."); + IWorld world = WorldFactory.CreateWorld(); //Create a root component IUserInterfaceComponent parentUserInterfaceComponent = UserInterfaceComponentFactory.CreateGenericUserInterfaceComponent( + world, AnchorFactory.CreateAnchor( AnchorDetailFactory.CreateAnchorDetails(AnchorDirections.LEFT, AnchorUnits.PIXELS, 0), AnchorDetailFactory.CreateAnchorDetails(AnchorDirections.RIGHT, AnchorUnits.PIXELS, 0), @@ -251,6 +269,7 @@ public void TestMinimumScale() parentUserInterfaceComponent.SetWidth(1000, 1000); //Add a child component which can scale but starts with no height IUserInterfaceComponent expandingComponent = UserInterfaceComponentFactory.CreateGenericUserInterfaceComponent( + world, parentUserInterfaceComponent, AnchorFactory.CreateAnchor( AnchorDetailFactory.CreateAnchorDetails(AnchorDirections.LEFT, AnchorUnits.PIXELS, 0), @@ -262,6 +281,7 @@ public void TestMinimumScale() ); //Add a child component to that which has a huge scale IUserInterfaceComponent bigComponent = UserInterfaceComponentFactory.CreateGenericUserInterfaceComponent( + world, expandingComponent, AnchorFactory.CreateAnchor( AnchorDetailFactory.CreateAnchorDetails(AnchorDirections.LEFT, AnchorUnits.PIXELS, 0), @@ -296,8 +316,10 @@ public void TestStrictExpansion() Assert.Inconclusive("Anchor factory not located."); if (UserInterfaceComponentFactory == null) Assert.Inconclusive("User interface factory not located."); + IWorld world = WorldFactory.CreateWorld(); //Create a root component IUserInterfaceComponent parentUserInterfaceComponent = UserInterfaceComponentFactory.CreateGenericUserInterfaceComponent( + world, AnchorFactory.CreateAnchor( AnchorDetailFactory.CreateAnchorDetails(AnchorDirections.LEFT, AnchorUnits.PIXELS, 0), AnchorDetailFactory.CreateAnchorDetails(AnchorDirections.RIGHT, AnchorUnits.PIXELS, 0), @@ -309,6 +331,7 @@ public void TestStrictExpansion() parentUserInterfaceComponent.SetWidth(1000, 1000); //This element should neve rexpand past 500 height IUserInterfaceComponent expandingComponent = UserInterfaceComponentFactory.CreateGenericUserInterfaceComponent( + world, parentUserInterfaceComponent, AnchorFactory.CreateAnchor( AnchorDetailFactory.CreateAnchorDetails(AnchorDirections.LEFT, AnchorUnits.PIXELS, 0), @@ -320,6 +343,7 @@ public void TestStrictExpansion() ); //Add a child component to that which has a huge scale IUserInterfaceComponent bigComponent = UserInterfaceComponentFactory.CreateGenericUserInterfaceComponent( + world, expandingComponent, AnchorFactory.CreateAnchor( AnchorDetailFactory.CreateAnchorDetails(AnchorDirections.LEFT, AnchorUnits.PIXELS, 0), @@ -354,8 +378,10 @@ public void TestNonStrictExpansion() Assert.Inconclusive("Anchor factory not located."); if (UserInterfaceComponentFactory == null) Assert.Inconclusive("User interface factory not located."); + IWorld world = WorldFactory.CreateWorld(); //Create a root component IUserInterfaceComponent parentUserInterfaceComponent = UserInterfaceComponentFactory.CreateGenericUserInterfaceComponent( + world, AnchorFactory.CreateAnchor( AnchorDetailFactory.CreateAnchorDetails(AnchorDirections.LEFT, AnchorUnits.PIXELS, 0), AnchorDetailFactory.CreateAnchorDetails(AnchorDirections.RIGHT, AnchorUnits.PIXELS, 0), @@ -367,6 +393,7 @@ public void TestNonStrictExpansion() parentUserInterfaceComponent.SetWidth(1000, 1000); //Add a child component which can scale but starts with no height IUserInterfaceComponent expandingComponent = UserInterfaceComponentFactory.CreateGenericUserInterfaceComponent( + world, parentUserInterfaceComponent, AnchorFactory.CreateAnchor( AnchorDetailFactory.CreateAnchorDetails(AnchorDirections.LEFT, AnchorUnits.PIXELS, 0), @@ -378,6 +405,7 @@ public void TestNonStrictExpansion() ); //Add a child component to that which has a huge scale IUserInterfaceComponent bigComponent = UserInterfaceComponentFactory.CreateGenericUserInterfaceComponent( + world, expandingComponent, AnchorFactory.CreateAnchor( AnchorDetailFactory.CreateAnchorDetails(AnchorDirections.LEFT, AnchorUnits.PIXELS, 0), @@ -426,6 +454,7 @@ public void TestChildScaleCorrectness( Assert.Inconclusive("Anchor factory not located."); if (UserInterfaceComponentFactory == null) Assert.Inconclusive("User interface factory not located."); + IWorld world = WorldFactory.CreateWorld(); //Setup the parent component //Create the parent anchor details IAnchorDetails parentTopAnchorDetails = AnchorDetailFactory.CreateAnchorDetails(AnchorDirections.TOP, AnchorUnits.PIXELS, 0); @@ -435,7 +464,7 @@ public void TestChildScaleCorrectness( //Create the parent anchor IAnchor parentAnchor = AnchorFactory.CreateAnchor(parentLeftAnchorDetails, parentRightAnchorDetails, parentTopAnchorDetails, parentBottomAnchorDetails); //Create the parent component - IUserInterfaceComponent parentUserInterfaceComponent = UserInterfaceComponentFactory.CreateGenericUserInterfaceComponent(parentAnchor, null); + IUserInterfaceComponent parentUserInterfaceComponent = UserInterfaceComponentFactory.CreateGenericUserInterfaceComponent(world, parentAnchor, null); parentUserInterfaceComponent.SetWidth(parentWidth, 1000); //Setup the child component //Create the anchor details @@ -446,7 +475,7 @@ public void TestChildScaleCorrectness( //Create the anchor IAnchor childAnchor = AnchorFactory.CreateAnchor(childLeftAnchorDetails, childRightAnchorDetails, childTopAnchorDetails, childBottomAnchorDetails); //Create the child component - IUserInterfaceComponent childUserInterfaceComponent = UserInterfaceComponentFactory.CreateGenericUserInterfaceComponent(parentUserInterfaceComponent, childAnchor, null); + IUserInterfaceComponent childUserInterfaceComponent = UserInterfaceComponentFactory.CreateGenericUserInterfaceComponent(world, parentUserInterfaceComponent, childAnchor, null); //Verify child width Assert.AreEqual(800, childUserInterfaceComponent.PixelHeight); Assert.AreEqual(expectedChildPixelWidth, childUserInterfaceComponent.PixelWidth); @@ -478,6 +507,7 @@ public void TestUserInterfacePixelScaling( Assert.Inconclusive("Anchor factory not located."); if (UserInterfaceComponentFactory == null) Assert.Inconclusive("User interface factory not located."); + IWorld world = WorldFactory.CreateWorld(); //Create the anchor details IAnchorDetails topAnchorDetails = AnchorDetailFactory.CreateAnchorDetails(AnchorDirections.TOP, AnchorUnits.PIXELS, 100); IAnchorDetails bottomAnchorDetails = AnchorDetailFactory.CreateAnchorDetails(AnchorDirections.BOTTOM, AnchorUnits.PIXELS, 100); @@ -486,7 +516,7 @@ public void TestUserInterfacePixelScaling( //Craete the anchor IAnchor anchor = AnchorFactory.CreateAnchor(leftAnchorDetails, rightAnchorDetails, topAnchorDetails, bottomAnchorDetails); //Create a generic user interface component - IUserInterfaceComponent userInterfaceComponent = UserInterfaceComponentFactory.CreateGenericUserInterfaceComponent(anchor, null); + IUserInterfaceComponent userInterfaceComponent = UserInterfaceComponentFactory.CreateGenericUserInterfaceComponent(world, anchor, null); //Ensure user interface minimum scale correctness Assert.AreEqual(0, userInterfaceComponent.MinimumPixelHeight); Assert.AreEqual(expectedWidth, userInterfaceComponent.MinimumPixelWidth); @@ -520,6 +550,7 @@ public void TestUserInterfaceParentPixelScaling( Assert.Inconclusive("Anchor factory not located."); if (UserInterfaceComponentFactory == null) Assert.Inconclusive("User interface factory not located."); + IWorld world = WorldFactory.CreateWorld(); //Create the parent anchor details IAnchorDetails parentTopAnchorDetails = AnchorDetailFactory.CreateAnchorDetails(AnchorDirections.TOP, AnchorUnits.PIXELS, 0); IAnchorDetails parentBottomAnchorDetails = AnchorDetailFactory.CreateAnchorDetails(AnchorDirections.BOTTOM, AnchorUnits.PIXELS, 0); @@ -535,9 +566,9 @@ public void TestUserInterfaceParentPixelScaling( //Craete the anchor IAnchor childAnchor = AnchorFactory.CreateAnchor(childLeftAnchorDetails, childRightAnchorDetails, childTopAnchorDetails, childBottomAnchorDetails); //Create a generic user interface component - IUserInterfaceComponent parentUserInterfaceComponent = UserInterfaceComponentFactory.CreateGenericUserInterfaceComponent(parentAnchor, null); + IUserInterfaceComponent parentUserInterfaceComponent = UserInterfaceComponentFactory.CreateGenericUserInterfaceComponent(world, parentAnchor, null); //Create another generic user interface component - UserInterfaceComponentFactory.CreateGenericUserInterfaceComponent(parentUserInterfaceComponent, childAnchor, null); + UserInterfaceComponentFactory.CreateGenericUserInterfaceComponent(world, parentUserInterfaceComponent, childAnchor, null); //Check parent minimum width Assert.AreEqual(200, parentUserInterfaceComponent.MinimumPixelHeight); //Should be 200 as child component requires 100 space above, and 100 space below Assert.AreEqual(expectedParentWidth, parentUserInterfaceComponent.MinimumPixelWidth); //Should be 250 as child component requires 250 space left and none right. diff --git a/CorgEng.Tests/UserInterfaceTests/UnitTests/UserInterfaceRaycastTests.cs b/CorgEng.Tests/UserInterfaceTests/UnitTests/UserInterfaceRaycastTests.cs index 9d0620d8..05751c88 100644 --- a/CorgEng.Tests/UserInterfaceTests/UnitTests/UserInterfaceRaycastTests.cs +++ b/CorgEng.Tests/UserInterfaceTests/UnitTests/UserInterfaceRaycastTests.cs @@ -1,4 +1,5 @@ using CorgEng.Core.Dependencies; +using CorgEng.GenericInterfaces.EntityComponentSystem; using CorgEng.GenericInterfaces.UserInterface.Anchors; using CorgEng.GenericInterfaces.UserInterface.Components; using Microsoft.VisualStudio.TestTools.UnitTesting; @@ -11,7 +12,7 @@ namespace CorgEng.Tests.UserInterfaceTests.UnitTests { [TestClass] - public class UserInterfaceRaycastTests + public class UserInterfaceRaycastTests : TestBase { [UsingDependency] @@ -23,6 +24,9 @@ public class UserInterfaceRaycastTests [UsingDependency] private static IAnchorDetailFactory AnchorDetailFactory; + [UsingDependency] + private static IWorldFactory WorldFactory; + [DataTestMethod] [DataRow(0, 0, 0)] [DataRow(0, 50, 50)] @@ -40,9 +44,11 @@ public void TestRaycast(int target, int x, int y) Assert.Inconclusive("Anchor factory not located."); if (UserInterfaceComponentFactory == null) Assert.Inconclusive("User interface factory not located."); + IWorld world = WorldFactory.CreateWorld(); //Create a root component //(0, 0) -> (1000, 1000) IUserInterfaceComponent parentUserInterfaceComponent = UserInterfaceComponentFactory.CreateGenericUserInterfaceComponent( + world, null, AnchorFactory.CreateAnchor( AnchorDetailFactory.CreateAnchorDetails(AnchorDirections.LEFT, AnchorUnits.PIXELS, 0), @@ -56,6 +62,7 @@ public void TestRaycast(int target, int x, int y) //Add a child component which can scale but starts with no height //(100, 100) -> (900, 900) IUserInterfaceComponent expandingComponent = UserInterfaceComponentFactory.CreateGenericUserInterfaceComponent( + world, parentUserInterfaceComponent, AnchorFactory.CreateAnchor( AnchorDetailFactory.CreateAnchorDetails(AnchorDirections.LEFT, AnchorUnits.PERCENTAGE, 10, true), @@ -68,6 +75,7 @@ public void TestRaycast(int target, int x, int y) //Add a child component to that which has a huge scale //(100, 400) -> (900, 900) IUserInterfaceComponent bigComponent = UserInterfaceComponentFactory.CreateGenericUserInterfaceComponent( + world, expandingComponent, AnchorFactory.CreateAnchor( AnchorDetailFactory.CreateAnchorDetails(AnchorDirections.LEFT, AnchorUnits.PIXELS, 0, true), diff --git a/CorgEng.Tests/UserInterfaceTests/UnitTests/UserInterfaceXmlLoaderTests.cs b/CorgEng.Tests/UserInterfaceTests/UnitTests/UserInterfaceXmlLoaderTests.cs index bf2effb3..b88963d1 100644 --- a/CorgEng.Tests/UserInterfaceTests/UnitTests/UserInterfaceXmlLoaderTests.cs +++ b/CorgEng.Tests/UserInterfaceTests/UnitTests/UserInterfaceXmlLoaderTests.cs @@ -1,4 +1,5 @@ using CorgEng.Core.Dependencies; +using CorgEng.GenericInterfaces.EntityComponentSystem; using CorgEng.GenericInterfaces.UserInterface.Anchors; using CorgEng.GenericInterfaces.UserInterface.Components; using CorgEng.GenericInterfaces.UserInterface.Generators; @@ -13,12 +14,15 @@ namespace CorgEng.Tests.UserInterfaceTests.UnitTests { [TestClass] - public class UserInterfaceXmlLoaderTests + public class UserInterfaceXmlLoaderTests : TestBase { [UsingDependency] private static IUserInterfaceXmlLoader UserInterfaceXmlLoader; + [UsingDependency] + private static IWorldFactory WorldFactory; + [TestMethod] public void TestDependencyImplementation() { @@ -38,7 +42,7 @@ public void TestFirstCorrectSide(string filepath, AnchorDirections left, AnchorD if (UserInterfaceXmlLoader == null) Assert.Inconclusive("User interface XML loader dependency was not injected."); //Check for loading errors - IUserInterfaceComponent Root = UserInterfaceXmlLoader.LoadUserInterface(filepath); + IUserInterfaceComponent Root = UserInterfaceXmlLoader.LoadUserInterface(WorldFactory.CreateWorld(), filepath); //If the returned value if null, fail Assert.IsNotNull(Root, "User interface loader returned null."); //Execute test @@ -62,7 +66,7 @@ public void TestFirstCorrectStrictness(string filepath, bool leftStrict, bool ri if (UserInterfaceXmlLoader == null) Assert.Inconclusive("User interface XML loader dependency was not injected."); //Check for loading errors - IUserInterfaceComponent Root = UserInterfaceXmlLoader.LoadUserInterface(filepath); + IUserInterfaceComponent Root = UserInterfaceXmlLoader.LoadUserInterface(WorldFactory.CreateWorld(), filepath); //If the returned value if null, fail Assert.IsNotNull(Root, "User interface loader returned null."); //Execute test @@ -86,7 +90,7 @@ public void TestFirstCorrectAnchorUnits(string filepath, AnchorUnits leftAnchorU if (UserInterfaceXmlLoader == null) Assert.Inconclusive("User interface XML loader dependency was not injected."); //Check for loading errors - IUserInterfaceComponent Root = UserInterfaceXmlLoader.LoadUserInterface(filepath); + IUserInterfaceComponent Root = UserInterfaceXmlLoader.LoadUserInterface(WorldFactory.CreateWorld(), filepath); //If the returned value if null, fail Assert.IsNotNull(Root, "User interface loader returned null."); //Execute test @@ -110,7 +114,7 @@ public void TestFirstCorrectAnchorOffset(string filepath, double leftAnchorAmoun if (UserInterfaceXmlLoader == null) Assert.Inconclusive("User interface XML loader dependency was not injected."); //Check for loading errors - IUserInterfaceComponent Root = UserInterfaceXmlLoader.LoadUserInterface(filepath); + IUserInterfaceComponent Root = UserInterfaceXmlLoader.LoadUserInterface(WorldFactory.CreateWorld(), filepath); //If the returned value if null, fail Assert.IsNotNull(Root, "User interface loader returned null."); //Execute test @@ -136,7 +140,7 @@ public void TestXmlLoadingErrors(string filepath) if (UserInterfaceXmlLoader == null) Assert.Inconclusive("User interface XML loader dependency was not injected."); //Check for loading errors - UserInterfaceXmlLoader.LoadUserInterface(filepath); + UserInterfaceXmlLoader.LoadUserInterface(WorldFactory.CreateWorld(), filepath); } [DataTestMethod] @@ -149,7 +153,7 @@ public void TestCorrectInterfaceComponentCount(string filepath, int expectedComp if (UserInterfaceXmlLoader == null) Assert.Inconclusive("User interface XML loader dependency was not injected."); //Check for loading errors - IUserInterfaceComponent Root = UserInterfaceXmlLoader.LoadUserInterface(filepath); + IUserInterfaceComponent Root = UserInterfaceXmlLoader.LoadUserInterface(WorldFactory.CreateWorld(), filepath); //If the returned value if null, fail Assert.IsNotNull(Root, "User interface loader returned null."); //Validate assumptions diff --git a/CorgEng.Tests/UtilityTypes/BatchTests.cs b/CorgEng.Tests/UtilityTypes/BatchTests.cs index ff66c379..9bf11af9 100644 --- a/CorgEng.Tests/UtilityTypes/BatchTests.cs +++ b/CorgEng.Tests/UtilityTypes/BatchTests.cs @@ -8,7 +8,7 @@ namespace CorgEng.Tests.UtilityTypes { [TestClass] - public class BatchTests + public class BatchTests : TestBase { private Vector TestVector { get; } = new Vector(); diff --git a/CorgEng.Tests/UtilityTypes/BinaryListTests.cs b/CorgEng.Tests/UtilityTypes/BinaryListTests.cs index 1a6e94db..72d84e3c 100644 --- a/CorgEng.Tests/UtilityTypes/BinaryListTests.cs +++ b/CorgEng.Tests/UtilityTypes/BinaryListTests.cs @@ -10,7 +10,7 @@ namespace CorgEng.Tests.UtilityTypes { [TestClass] - public class BinaryListTests + public class BinaryListTests : TestBase { [UsingDependency] diff --git a/CorgEng.Tests/UtilityTypes/MatrixTests/GeneralMatrixTests.cs b/CorgEng.Tests/UtilityTypes/MatrixTests/GeneralMatrixTests.cs index 2491492c..c0bb17ac 100644 --- a/CorgEng.Tests/UtilityTypes/MatrixTests/GeneralMatrixTests.cs +++ b/CorgEng.Tests/UtilityTypes/MatrixTests/GeneralMatrixTests.cs @@ -9,7 +9,7 @@ namespace CorgEng.Tests.UtilityTypes.MatrixTests { [TestClass] - public class GeneralMatrixTests + public class GeneralMatrixTests : TestBase { [TestMethod] diff --git a/CorgEng.Tests/UtilityTypes/MatrixTests/IdentityMatrixTests.cs b/CorgEng.Tests/UtilityTypes/MatrixTests/IdentityMatrixTests.cs index e754492a..26335705 100644 --- a/CorgEng.Tests/UtilityTypes/MatrixTests/IdentityMatrixTests.cs +++ b/CorgEng.Tests/UtilityTypes/MatrixTests/IdentityMatrixTests.cs @@ -12,7 +12,7 @@ namespace CorgEng.Tests.UtilityTypes.MatrixTests /// Summary description for IdentityMatrixTests /// [TestClass] - public class IdentityMatrixTests + public class IdentityMatrixTests : TestBase { [TestMethod] diff --git a/CorgEng.Tests/UtilityTypes/MatrixTests/MatrixDimensionErrorTests.cs b/CorgEng.Tests/UtilityTypes/MatrixTests/MatrixDimensionErrorTests.cs index 25ab9038..dc9a57c0 100644 --- a/CorgEng.Tests/UtilityTypes/MatrixTests/MatrixDimensionErrorTests.cs +++ b/CorgEng.Tests/UtilityTypes/MatrixTests/MatrixDimensionErrorTests.cs @@ -9,7 +9,7 @@ namespace CorgEng.Tests.UtilityTypes.MatrixTests { [TestClass] - public class MatrixDimensionErrorTests + public class MatrixDimensionErrorTests : TestBase { [TestMethod] [ExpectedException(typeof(InvalidMatrixDimensionError))] diff --git a/CorgEng.Tests/UtilityTypes/MatrixTests/MultiplicationMatrixTests.cs b/CorgEng.Tests/UtilityTypes/MatrixTests/MultiplicationMatrixTests.cs index f4dbb967..52299074 100644 --- a/CorgEng.Tests/UtilityTypes/MatrixTests/MultiplicationMatrixTests.cs +++ b/CorgEng.Tests/UtilityTypes/MatrixTests/MultiplicationMatrixTests.cs @@ -9,7 +9,7 @@ namespace CorgEng.Tests.UtilityTypes.MatrixTests { [TestClass] - public class MultiplicationMatrixTests + public class MultiplicationMatrixTests : TestBase { [TestMethod] diff --git a/CorgEng.Tests/UtilityTypes/MatrixTests/TranslationMatrixTests.cs b/CorgEng.Tests/UtilityTypes/MatrixTests/TranslationMatrixTests.cs index 135dc1f8..8c633332 100644 --- a/CorgEng.Tests/UtilityTypes/MatrixTests/TranslationMatrixTests.cs +++ b/CorgEng.Tests/UtilityTypes/MatrixTests/TranslationMatrixTests.cs @@ -9,7 +9,7 @@ namespace CorgEng.Tests.UtilityTypes.MatrixTests { [TestClass] - public class TranslationMatrixTests + public class TranslationMatrixTests : TestBase { [TestMethod] public void TestNoTranslation() diff --git a/CorgEng.Tests/UtilityTypes/VectorTests.cs b/CorgEng.Tests/UtilityTypes/VectorTests.cs index 54b14ae7..7e649326 100644 --- a/CorgEng.Tests/UtilityTypes/VectorTests.cs +++ b/CorgEng.Tests/UtilityTypes/VectorTests.cs @@ -10,7 +10,7 @@ namespace CorgEng.Tests.UtilityTypes { [TestClass] - public class VectorTests + public class VectorTests : TestBase { [TestMethod] diff --git a/CorgEng.Tests/World/WorldEntitySystem.cs b/CorgEng.Tests/World/WorldEntitySystem.cs new file mode 100644 index 00000000..1128618f --- /dev/null +++ b/CorgEng.Tests/World/WorldEntitySystem.cs @@ -0,0 +1,23 @@ +using CorgEng.Core.Dependencies; +using CorgEng.EntityComponentSystem.Systems; +using CorgEng.GenericInterfaces.EntityComponentSystem; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CorgEng.Tests.World +{ + [TestClass] + internal class WorldEntitySystem + { + + [UsingDependency] + private static IWorldFactory WorldFactory = null!; + + + + } +} diff --git a/CorgEng.Tests/World/WorldTests.cs b/CorgEng.Tests/World/WorldTests.cs index 25c474a4..fe49f887 100644 --- a/CorgEng.Tests/World/WorldTests.cs +++ b/CorgEng.Tests/World/WorldTests.cs @@ -1,5 +1,6 @@ using CorgEng.Core.Dependencies; using CorgEng.EntityComponentSystem.Entities; +using CorgEng.EntityComponentSystem.Systems; using CorgEng.GenericInterfaces.EntityComponentSystem; using CorgEng.GenericInterfaces.World; using CorgEng.World.Components; @@ -14,11 +15,44 @@ namespace CorgEng.Tests.World { [TestClass] - public class WorldTests + public class WorldTests : TestBase { [UsingDependency] - public static IWorld WorldAccess; + public static IEntityPositionTracker WorldAccess; + + [UsingDependency] + public static IWorldFactory WorldFactory = null!; + + private class TestSystem : EntitySystem + { + public int setupTimes = 0; + + public override EntitySystemFlags SystemFlags => EntitySystemFlags.HOST_SYSTEM | EntitySystemFlags.CLIENT_SYSTEM; + + public override void SystemSetup(IWorld world) + { + setupTimes++; + } + } + + [TestMethod] + [Timeout(1000)] + public void TestCreatingWorlds() + { + IWorld world = WorldFactory.CreateWorld(); + Assert.AreEqual(1, world.EntitySystemManager.GetSingleton().setupTimes); + world = WorldFactory.CreateWorld(); + Assert.AreEqual(1, world.EntitySystemManager.GetSingleton().setupTimes); + world = WorldFactory.CreateWorld(); + Assert.AreEqual(1, world.EntitySystemManager.GetSingleton().setupTimes); + world = WorldFactory.CreateWorld(); + Assert.AreEqual(1, world.EntitySystemManager.GetSingleton().setupTimes); + world = WorldFactory.CreateWorld(); + Assert.AreEqual(1, world.EntitySystemManager.GetSingleton().setupTimes); + world = WorldFactory.CreateWorld(); + Assert.AreEqual(1, world.EntitySystemManager.GetSingleton().setupTimes); + } [TestMethod] public void TestMassInsertion() diff --git a/CorgEng.UserInterface/Generators/UserInterfaceXmlLoader.cs b/CorgEng.UserInterface/Generators/UserInterfaceXmlLoader.cs index 16fac617..362a2868 100644 --- a/CorgEng.UserInterface/Generators/UserInterfaceXmlLoader.cs +++ b/CorgEng.UserInterface/Generators/UserInterfaceXmlLoader.cs @@ -1,5 +1,6 @@ using CorgEng.Core.Dependencies; using CorgEng.DependencyInjection.Dependencies; +using CorgEng.GenericInterfaces.EntityComponentSystem; using CorgEng.GenericInterfaces.Logging; using CorgEng.GenericInterfaces.UserInterface.Anchors; using CorgEng.GenericInterfaces.UserInterface.Components; @@ -29,14 +30,14 @@ public class UserInterfaceXmlLoader : IUserInterfaceXmlLoader [UsingDependency] private static ILogger Logger; - public IUserInterfaceComponent LoadUserInterface(string filepath) + public IUserInterfaceComponent LoadUserInterface(IWorld world, string filepath) { try { //Load the XML file provided XElement userInterfaceElement = XElement.Load(filepath); //Load the interface component - return LoadFromXmlComponent(userInterfaceElement); + return LoadFromXmlComponent(world, userInterfaceElement); } catch (Exception e) { @@ -45,7 +46,7 @@ public IUserInterfaceComponent LoadUserInterface(string filepath) } } - private IUserInterfaceComponent LoadFromXmlComponent(XElement node, IUserInterfaceComponent parent = null) + private IUserInterfaceComponent LoadFromXmlComponent(IWorld world, XElement node, IUserInterfaceComponent parent = null) { try { @@ -54,6 +55,7 @@ private IUserInterfaceComponent LoadFromXmlComponent(XElement node, IUserInterfa parameters = node.Attributes().ToDictionary(attribute => attribute.Name.LocalName, attribute => attribute.Value); //Create a generic user interface component IUserInterfaceComponent currentElement = UserInterfaceComponentFactory.CreateUserInterfaceComponent( + world, node.Name.LocalName, parent, AnchorFactory.CreateAnchor( @@ -69,7 +71,7 @@ private IUserInterfaceComponent LoadFromXmlComponent(XElement node, IUserInterfa foreach (XElement childElement in node.Elements()) { //Children are automatically added when created with a parent - LoadFromXmlComponent(childElement, currentElement); + LoadFromXmlComponent(world, childElement, currentElement); } } ); diff --git a/CorgEng.UserInterface/Popups/PopupManager.cs b/CorgEng.UserInterface/Popups/PopupManager.cs index 661acbbb..07dae531 100644 --- a/CorgEng.UserInterface/Popups/PopupManager.cs +++ b/CorgEng.UserInterface/Popups/PopupManager.cs @@ -1,6 +1,7 @@ using CorgEng.Core.Dependencies; using CorgEng.Core.Modules; using CorgEng.DependencyInjection.Dependencies; +using CorgEng.GenericInterfaces.EntityComponentSystem; using CorgEng.GenericInterfaces.UserInterface.Anchors; using CorgEng.GenericInterfaces.UserInterface.Components; using CorgEng.GenericInterfaces.UserInterface.Popups; @@ -12,7 +13,6 @@ namespace CorgEng.UserInterface.Popups { - [Dependency] internal class PopupManager : IPopupManager { @@ -30,10 +30,11 @@ internal class PopupManager : IPopupManager /// private static IUserInterfaceComponent popupRootInterface; - [ModuleLoad(mainThread = true)] - public static void InitialisePopupInterface() + [WorldInitialise] + public static void InitialisePopupInterface(IWorld world) { popupRootInterface = UserInterfaceComponentFactory.CreateGenericUserInterfaceComponent( + world, null, AnchorFactory.CreateAnchor( AnchorDetailFactory.CreateAnchorDetails(AnchorDirections.LEFT, AnchorUnits.PIXELS, 0), diff --git a/CorgEng.UserInterface/Systems/UserInterfaceClickSystem.cs b/CorgEng.UserInterface/Systems/UserInterfaceClickSystem.cs index 39941e09..0727fdc7 100644 --- a/CorgEng.UserInterface/Systems/UserInterfaceClickSystem.cs +++ b/CorgEng.UserInterface/Systems/UserInterfaceClickSystem.cs @@ -16,7 +16,7 @@ public class UserInterfaceClickSystem : EntitySystem public override EntitySystemFlags SystemFlags => EntitySystemFlags.CLIENT_SYSTEM; - public override void SystemSetup() + public override void SystemSetup(IWorld world) { RegisterLocalEvent(OnUserInterfaceElementClicked); RegisterLocalEvent(OnUserInterfaceActionElementClicked); diff --git a/CorgEng.UserInterface/UserInterfaceComponents/UserInterfaceBox.cs b/CorgEng.UserInterface/UserInterfaceComponents/UserInterfaceBox.cs index 71ab3121..0390e42d 100644 --- a/CorgEng.UserInterface/UserInterfaceComponents/UserInterfaceBox.cs +++ b/CorgEng.UserInterface/UserInterfaceComponents/UserInterfaceBox.cs @@ -1,5 +1,6 @@ using CorgEng.Core; using CorgEng.Core.Dependencies; +using CorgEng.GenericInterfaces.EntityComponentSystem; using CorgEng.GenericInterfaces.Logging; using CorgEng.GenericInterfaces.UserInterface.Anchors; using CorgEng.GenericInterfaces.UserInterface.Components; @@ -31,12 +32,12 @@ public BoxStyle Style get => boxRenderObject.Style; } - public UserInterfaceBox(IUserInterfaceComponent parent, IAnchor anchorDetails, IDictionary arguments) : base(parent, anchorDetails, arguments) + public UserInterfaceBox(IWorld world, IUserInterfaceComponent parent, IAnchor anchorDetails, IDictionary arguments) : base(world, parent, anchorDetails, arguments) { Setup(arguments); } - public UserInterfaceBox(IAnchor anchorDetails, IDictionary arguments) : base(anchorDetails, arguments) + public UserInterfaceBox(IWorld world, IAnchor anchorDetails, IDictionary arguments) : base(world, anchorDetails, arguments) { Setup(arguments); } diff --git a/CorgEng.UserInterface/UserInterfaceComponents/UserInterfaceButton.cs b/CorgEng.UserInterface/UserInterfaceComponents/UserInterfaceButton.cs index 75681842..d655626e 100644 --- a/CorgEng.UserInterface/UserInterfaceComponents/UserInterfaceButton.cs +++ b/CorgEng.UserInterface/UserInterfaceComponents/UserInterfaceButton.cs @@ -1,4 +1,5 @@ using CorgEng.Core; +using CorgEng.GenericInterfaces.EntityComponentSystem; using CorgEng.GenericInterfaces.UserInterface.Anchors; using CorgEng.GenericInterfaces.UserInterface.Components; using CorgEng.GenericInterfaces.UtilityTypes; @@ -15,11 +16,11 @@ namespace CorgEng.UserInterface.Components internal class UserInterfaceButton : UserInterfaceBox { - public UserInterfaceButton(IUserInterfaceComponent parent, IAnchor anchorDetails, IDictionary arguments) : base(parent, anchorDetails, arguments) + public UserInterfaceButton(IWorld world, IUserInterfaceComponent parent, IAnchor anchorDetails, IDictionary arguments) : base(world, parent, anchorDetails, arguments) { } - public UserInterfaceButton(IAnchor anchorDetails, IDictionary arguments) : base(anchorDetails, arguments) + public UserInterfaceButton(IWorld world, IAnchor anchorDetails, IDictionary arguments) : base(world, anchorDetails, arguments) { } diff --git a/CorgEng.UserInterface/UserInterfaceComponents/UserInterfaceComponent.cs b/CorgEng.UserInterface/UserInterfaceComponents/UserInterfaceComponent.cs index 43ec6cc0..e26212af 100644 --- a/CorgEng.UserInterface/UserInterfaceComponents/UserInterfaceComponent.cs +++ b/CorgEng.UserInterface/UserInterfaceComponents/UserInterfaceComponent.cs @@ -28,9 +28,6 @@ internal class UserInterfaceComponent : RenderCore, IUserInterfaceComponent [UsingDependency] private static ILogger Logger; - [UsingDependency] - private static IEntityFactory EntityFactory; - /// /// The anchor details for this component /// @@ -85,7 +82,7 @@ public IAnchor Anchor /// so we can have a component based user interface model despite /// the oversight of not implementing it this way initially /// - public IEntity ComponentHolder { get; } = EntityFactory.CreateEmptyEntity(null); + public IEntity ComponentHolder { get; } /// /// Is this component fullscreen? @@ -142,6 +139,8 @@ public IAnchor Anchor public IDictionary Parameters { get; private set; } = null; + public IWorld World => world; + /// /// A unique identifier for this component. /// @@ -154,7 +153,7 @@ public IAnchor Anchor private static object lockObject = new object(); - public UserInterfaceComponent(IUserInterfaceComponent parent, IAnchor anchorDetails, IDictionary arguments) : this(anchorDetails, arguments) + public UserInterfaceComponent(IWorld world, IUserInterfaceComponent parent, IAnchor anchorDetails, IDictionary arguments) : this(world, anchorDetails, arguments) { //Set the parent Parent = parent; @@ -162,8 +161,9 @@ public UserInterfaceComponent(IUserInterfaceComponent parent, IAnchor anchorDeta Parent?.AddChild(this); } - public UserInterfaceComponent(IAnchor anchorDetails, IDictionary arguments) + public UserInterfaceComponent(IWorld world, IAnchor anchorDetails, IDictionary arguments) : base(world) { + ComponentHolder = world.EntityManager.CreateEmptyEntity(null); Parameters = arguments; RegisterArguments(); ParseArguments(arguments); diff --git a/CorgEng.UserInterface/UserInterfaceComponents/UserInterfaceComponentFactory.cs b/CorgEng.UserInterface/UserInterfaceComponents/UserInterfaceComponentFactory.cs index 53e6fc45..d0f89efd 100644 --- a/CorgEng.UserInterface/UserInterfaceComponents/UserInterfaceComponentFactory.cs +++ b/CorgEng.UserInterface/UserInterfaceComponents/UserInterfaceComponentFactory.cs @@ -1,4 +1,5 @@ using CorgEng.DependencyInjection.Dependencies; +using CorgEng.GenericInterfaces.EntityComponentSystem; using CorgEng.GenericInterfaces.UserInterface.Anchors; using CorgEng.GenericInterfaces.UserInterface.Components; using CorgEng.UserInterface.UserInterfaceComponents; @@ -16,45 +17,45 @@ internal class UserInterfaceComponentFactory : IUserInterfaceComponentFactory private static Dictionary Empty { get; } = new Dictionary(); - public IUserInterfaceComponent CreateGenericUserInterfaceComponent(IUserInterfaceComponent parent, IAnchor anchorDetails, Action preInitialiseAction) + public IUserInterfaceComponent CreateGenericUserInterfaceComponent(IWorld world, IUserInterfaceComponent parent, IAnchor anchorDetails, Action preInitialiseAction) { - UserInterfaceComponent createdComponent = new UserInterfaceComponent(parent, anchorDetails, Empty); + UserInterfaceComponent createdComponent = new UserInterfaceComponent(world, parent, anchorDetails, Empty); preInitialiseAction?.Invoke(createdComponent); createdComponent.Initialize(); return createdComponent; } - public IUserInterfaceComponent CreateGenericUserInterfaceComponent(IAnchor anchorDetails, Action preInitialiseAction) + public IUserInterfaceComponent CreateGenericUserInterfaceComponent(IWorld world, IAnchor anchorDetails, Action preInitialiseAction) { - UserInterfaceComponent createdComponent = new UserInterfaceComponent(anchorDetails, Empty); + UserInterfaceComponent createdComponent = new UserInterfaceComponent(world, anchorDetails, Empty); preInitialiseAction?.Invoke(createdComponent); createdComponent.Initialize(); return createdComponent; } - public IUserInterfaceComponent CreateUserInterfaceComponent(string componentType, IUserInterfaceComponent parent, IAnchor anchorDetails, IDictionary arguments, Action preInitialiseAction) + public IUserInterfaceComponent CreateUserInterfaceComponent(IWorld world, string componentType, IUserInterfaceComponent parent, IAnchor anchorDetails, IDictionary arguments, Action preInitialiseAction) { UserInterfaceComponent createdComponent; switch (componentType) { case "BoxComponent": - createdComponent = new UserInterfaceBox(parent, anchorDetails, arguments); + createdComponent = new UserInterfaceBox(world, parent, anchorDetails, arguments); break; case "UserInterface": case "UserInterfaceComponent": - createdComponent = new UserInterfaceComponent(parent, anchorDetails, arguments); + createdComponent = new UserInterfaceComponent(world, parent, anchorDetails, arguments); break; case "UserInterfaceButton": - createdComponent = new UserInterfaceButton(parent, anchorDetails, arguments); + createdComponent = new UserInterfaceButton(world, parent, anchorDetails, arguments); break; case "DropdownComponent": - createdComponent = new UserInterfaceDropdown(parent, anchorDetails, arguments); + createdComponent = new UserInterfaceDropdown(world, parent, anchorDetails, arguments); break; case "TextComponent": - createdComponent = new UserInterfaceText(parent, anchorDetails, arguments); + createdComponent = new UserInterfaceText(world, parent, anchorDetails, arguments); break; case "IconComponent": - createdComponent = new UserInterfaceIcon(parent, anchorDetails, arguments); + createdComponent = new UserInterfaceIcon(world, parent, anchorDetails, arguments); break; default: throw new NotImplementedException($"The component {componentType} is not recognised."); @@ -64,9 +65,9 @@ public IUserInterfaceComponent CreateUserInterfaceComponent(string componentType return createdComponent; } - public IUserInterfaceComponent CreateUserInterfaceComponent(string componentType, IAnchor anchorDetails, IDictionary arguments, Action preInitialiseAction) + public IUserInterfaceComponent CreateUserInterfaceComponent(IWorld world, string componentType, IAnchor anchorDetails, IDictionary arguments, Action preInitialiseAction) { - return CreateUserInterfaceComponent(componentType, null, anchorDetails, arguments, preInitialiseAction); + return CreateUserInterfaceComponent(world, componentType, null, anchorDetails, arguments, preInitialiseAction); } } } diff --git a/CorgEng.UserInterface/UserInterfaceComponents/UserInterfaceDropdown.cs b/CorgEng.UserInterface/UserInterfaceComponents/UserInterfaceDropdown.cs index 31157b64..82ad7d81 100644 --- a/CorgEng.UserInterface/UserInterfaceComponents/UserInterfaceDropdown.cs +++ b/CorgEng.UserInterface/UserInterfaceComponents/UserInterfaceDropdown.cs @@ -1,4 +1,5 @@ -using CorgEng.GenericInterfaces.UserInterface.Anchors; +using CorgEng.GenericInterfaces.EntityComponentSystem; +using CorgEng.GenericInterfaces.UserInterface.Anchors; using CorgEng.GenericInterfaces.UserInterface.Components; using CorgEng.UserInterface.Components; using System; @@ -31,12 +32,12 @@ internal class UserInterfaceDropdown : UserInterfaceBox private List expandButton = new List(); - public UserInterfaceDropdown(IAnchor anchorDetails, IDictionary arguments) : base(anchorDetails, arguments) + public UserInterfaceDropdown(IWorld world, IAnchor anchorDetails, IDictionary arguments) : base(world, anchorDetails, arguments) { ToggleExpansion(); } - public UserInterfaceDropdown(IUserInterfaceComponent parent, IAnchor anchorDetails, IDictionary arguments) : base(parent, anchorDetails, arguments) + public UserInterfaceDropdown(IWorld world, IUserInterfaceComponent parent, IAnchor anchorDetails, IDictionary arguments) : base(world, parent, anchorDetails, arguments) { ToggleExpansion(); } diff --git a/CorgEng.UserInterface/UserInterfaceComponents/UserInterfaceIcon.cs b/CorgEng.UserInterface/UserInterfaceComponents/UserInterfaceIcon.cs index 88c8f51c..35cd6c3e 100644 --- a/CorgEng.UserInterface/UserInterfaceComponents/UserInterfaceIcon.cs +++ b/CorgEng.UserInterface/UserInterfaceComponents/UserInterfaceIcon.cs @@ -1,6 +1,7 @@ using CorgEng.Constants; using CorgEng.Core; using CorgEng.Core.Dependencies; +using CorgEng.GenericInterfaces.EntityComponentSystem; using CorgEng.GenericInterfaces.Logging; using CorgEng.GenericInterfaces.Rendering.Icons; using CorgEng.GenericInterfaces.Rendering.Textures; @@ -37,12 +38,12 @@ internal class UserInterfaceIcon : UserInterfaceComponent /// protected override bool ScreencastInclude { get; } = false; - public UserInterfaceIcon(IAnchor anchorDetails, IDictionary arguments) : base(anchorDetails, arguments) + public UserInterfaceIcon(IWorld world, IAnchor anchorDetails, IDictionary arguments) : base(world, anchorDetails, arguments) { Init(arguments); } - public UserInterfaceIcon(IUserInterfaceComponent parent, IAnchor anchorDetails, IDictionary arguments) : base(parent, anchorDetails, arguments) + public UserInterfaceIcon(IWorld world, IUserInterfaceComponent parent, IAnchor anchorDetails, IDictionary arguments) : base(world, parent, anchorDetails, arguments) { Init(arguments); } diff --git a/CorgEng.UserInterface/UserInterfaceComponents/UserInterfaceText.cs b/CorgEng.UserInterface/UserInterfaceComponents/UserInterfaceText.cs index e8680151..dfc4ee7a 100644 --- a/CorgEng.UserInterface/UserInterfaceComponents/UserInterfaceText.cs +++ b/CorgEng.UserInterface/UserInterfaceComponents/UserInterfaceText.cs @@ -1,5 +1,6 @@ using CorgEng.Core; using CorgEng.Core.Dependencies; +using CorgEng.GenericInterfaces.EntityComponentSystem; using CorgEng.GenericInterfaces.Font.Fonts; using CorgEng.GenericInterfaces.Rendering.Cameras.Isometric; using CorgEng.GenericInterfaces.Rendering.Renderers.SpriteRendering; @@ -44,12 +45,12 @@ internal class UserInterfaceText : UserInterfaceComponent private IFont font; - public UserInterfaceText(IAnchor anchorDetails, IDictionary arguments) : base(anchorDetails, arguments) + public UserInterfaceText(IWorld world, IAnchor anchorDetails, IDictionary arguments) : base(world, anchorDetails, arguments) { Setup(arguments); } - public UserInterfaceText(IUserInterfaceComponent parent, IAnchor anchorDetails, IDictionary arguments) : base(parent, anchorDetails, arguments) + public UserInterfaceText(IWorld world, IUserInterfaceComponent parent, IAnchor anchorDetails, IDictionary arguments) : base(world, parent, anchorDetails, arguments) { Setup(arguments); } diff --git a/CorgEng.UtilityTypes/BindableProperties/BindableProperty.cs b/CorgEng.UtilityTypes/BindableProperties/BindableProperty.cs index cc3caea1..5f4de780 100644 --- a/CorgEng.UtilityTypes/BindableProperties/BindableProperty.cs +++ b/CorgEng.UtilityTypes/BindableProperties/BindableProperty.cs @@ -1,19 +1,28 @@ using CorgEng.GenericInterfaces.UtilityTypes; using System; +using System.Threading.Tasks; namespace CorgEng.UtilityTypes.BindableProperties { public class BindableProperty : IBindableProperty { - private T _value = default; - public T Value + protected T _value = default; + public virtual T Value { get => _value; set { if (value == null && (!typeof(T).IsGenericType || typeof(T).GetGenericTypeDefinition() != typeof(Nullable<>))) throw new ArgumentNullException("Attempting to set a bindable property of non-nullable type to null."); + // Stop listening for old value changes + if (_value != null && value is IListenable listener) + listener.ValueChanged -= ChangeReact; + // Update the value _value = value; + // Start listening for value changes + if (_value != null && value is IListenable listener2) + listener2.ValueChanged += ChangeReact; + // Indicate that we have changed TriggerChanged(); } } @@ -23,9 +32,17 @@ public T Value public BindableProperty(T value) { _value = value; + // If the value is a listenable type, start listening + if (value is IListenable listener) + listener.ValueChanged += ChangeReact; } - public void TriggerChanged() + private void ChangeReact(object? sender, EventArgs e) + { + TriggerChanged(); + } + + public virtual void TriggerChanged() { ValueChanged?.Invoke(_value, EventArgs.Empty); } diff --git a/CorgEng.UtilityTypes/Linecasting/LineCaster.cs b/CorgEng.UtilityTypes/Linecasting/LineCaster.cs index fc69bc73..a9cf90fd 100644 --- a/CorgEng.UtilityTypes/Linecasting/LineCaster.cs +++ b/CorgEng.UtilityTypes/Linecasting/LineCaster.cs @@ -17,7 +17,7 @@ internal class LineCaster : ILineCaster private const float VERY_SMALL_VALUE = 0.00001f; [UsingDependency] - private static IWorld World = null!; + private static IEntityPositionTracker World = null!; /// /// Lazilly evaluated enumerable of locations between start and end. diff --git a/CorgEng.UtilityTypes/Monads/Result.cs b/CorgEng.UtilityTypes/Monads/Result.cs new file mode 100644 index 00000000..10fd698f --- /dev/null +++ b/CorgEng.UtilityTypes/Monads/Result.cs @@ -0,0 +1,47 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace CorgEng.UtilityTypes.Monads +{ + public class Result + { + + public virtual bool HasValue { get; } = true; + + private T value; + + public Result(T value) + { + this.value = value; + } + + public Result Fail(Action failureAction) + { + if (HasValue) + return this; + failureAction(); + return this; + } + + public Result Then(Action successAction) + { + if (!HasValue) + return this; + successAction(value); + return this; + } + + } + + public class Failure : Result + { + + public Failure() : base(default) + { } + + public override bool HasValue => false; + } +} diff --git a/CorgEng.UtilityTypes/Vectors/Vector.cs b/CorgEng.UtilityTypes/Vectors/Vector.cs index bdff54fa..343dc5aa 100644 --- a/CorgEng.UtilityTypes/Vectors/Vector.cs +++ b/CorgEng.UtilityTypes/Vectors/Vector.cs @@ -20,13 +20,13 @@ public struct Vector : IVector, ICustomSerialisationBehaviour public Vector(params T[] values) { Values = values; - OnChange = null; + ValueChanged = null; sizeOfT = Marshal.SizeOf(typeof(T)); } private T[] Values; - public event EventHandler OnChange; + public event EventHandler ValueChanged; /// /// Overload for the [] operator, returns the element of the vector. @@ -36,7 +36,7 @@ public T this[int x] get { return Values[x]; } set { Values[x] = value; - OnChange?.Invoke(this, EventArgs.Empty); + ValueChanged?.Invoke(this, EventArgs.Empty); } } @@ -379,7 +379,7 @@ public void DeserialiseFrom(BinaryReader binaryReader) //Construct //new T[length] causes a stack overflow exception. Values = new T[length]; - OnChange = null; + ValueChanged = null; sizeOfT = Marshal.SizeOf(typeof(T)); //Read Ts for (int i = length - 1; i >= 0; i--) diff --git a/CorgEng.World/EntitySystems/WorldSystem.cs b/CorgEng.World/EntitySystems/WorldSystem.cs index 0cd736a6..edb90fd8 100644 --- a/CorgEng.World/EntitySystems/WorldSystem.cs +++ b/CorgEng.World/EntitySystems/WorldSystem.cs @@ -19,11 +19,11 @@ public class WorldSystem : EntitySystem { [UsingDependency] - private static IWorld WorldAccess = null!; + private static IEntityPositionTracker WorldAccess = null!; public override EntitySystemFlags SystemFlags { get; } = EntitySystemFlags.HOST_SYSTEM; - public override void SystemSetup() + public override void SystemSetup(IWorld world) { RegisterLocalEvent(OnEntityCreated); RegisterLocalEvent(OnEntityLocationChanged); @@ -46,7 +46,7 @@ private void OnEntityCreated(IEntity entity, TrackComponent trackComponent, Comp if (trackComponent.Parent.HasComponent()) return; //Add the entity to the world - WorldAccess.AddEntity(trackComponent.Key, trackComponent, trackComponent.Transform.Position.X, trackComponent.Transform.Position.Y, 0); + WorldAccess.AddEntity(trackComponent.Key, trackComponent, trackComponent.Transform.Position.Value.X, trackComponent.Transform.Position.Value.Y, 0); trackComponent.isTracking = true; } @@ -68,7 +68,7 @@ private void OnEntityLocationChanged(IEntity entity, TrackComponent trackCompone { //Start tracking trackComponent.isTracking = true; - WorldAccess.AddEntity(trackComponent.Key, trackComponent, trackComponent.Transform.Position.X, trackComponent.Transform.Position.Y, 0); + WorldAccess.AddEntity(trackComponent.Key, trackComponent, trackComponent.Transform.Position.Value.X, trackComponent.Transform.Position.Value.Y, 0); } } diff --git a/CorgEng.World/WorldTracking/World.cs b/CorgEng.World/WorldTracking/EntityPositionTracker.cs similarity index 98% rename from CorgEng.World/WorldTracking/World.cs rename to CorgEng.World/WorldTracking/EntityPositionTracker.cs index 1df46cd1..a89ee4e9 100644 --- a/CorgEng.World/WorldTracking/World.cs +++ b/CorgEng.World/WorldTracking/EntityPositionTracker.cs @@ -17,7 +17,7 @@ namespace CorgEng.World.WorldTracking { [Dependency] - internal class World : IWorld + internal class EntityPositionTracker : IEntityPositionTracker { [UsingDependency]