From a487d31c485ec374bb0e2a829e32d477f20538f1 Mon Sep 17 00:00:00 2001 From: WiggleWizard <1405402+WiggleWizard@users.noreply.github.com> Date: Thu, 19 Sep 2024 21:43:04 +0100 Subject: [PATCH] Completed work on exposing subsystems to scene events --- Nuake/Engine.cpp | 2 +- Nuake/Engine.h | 2 + Nuake/src/Core/MulticastDelegate.h | 100 +++++++++++++----- Nuake/src/Scripting/ScriptingEngineNet.cpp | 16 +-- Nuake/src/Scripting/ScriptingEngineNet.h | 10 +- Nuake/src/Subsystems/EngineSubsystem.cpp | 19 ++-- Nuake/src/Subsystems/EngineSubsystem.h | 4 +- .../Subsystems/EngineSubsystemScriptable.cpp | 28 ++++- .../Subsystems/EngineSubsystemScriptable.h | 8 +- 9 files changed, 134 insertions(+), 55 deletions(-) diff --git a/Nuake/Engine.cpp b/Nuake/Engine.cpp index 93439389..5dec3ecd 100644 --- a/Nuake/Engine.cpp +++ b/Nuake/Engine.cpp @@ -42,7 +42,7 @@ namespace Nuake { Window::Get()->OnWindowSetScene().AddStatic(&Engine::OnWindowSetScene); - ScriptingEngineNet::Get().AddListener(&Engine::OnScriptingEngineGameAssemblyLoaded); + ScriptingEngineNet::Get().OnGameAssemblyLoaded().AddStatic(&Engine::OnScriptingEngineGameAssemblyLoaded); AudioManager::Get().Initialize(); PhysicsManager::Get().Init(); diff --git a/Nuake/Engine.h b/Nuake/Engine.h index 42ea5b20..c9563837 100644 --- a/Nuake/Engine.h +++ b/Nuake/Engine.h @@ -60,6 +60,8 @@ namespace Nuake protected: static void OnWindowSetScene(Ref oldScene, Ref newScene); static void InitializeCoreSubsystems(); + + static void OnScriptingEngineUninitialize(); static void OnScriptingEngineGameAssemblyLoaded(); static void OnScenePreInitialize(Ref scene); diff --git a/Nuake/src/Core/MulticastDelegate.h b/Nuake/src/Core/MulticastDelegate.h index fb2a3160..ac5349e2 100644 --- a/Nuake/src/Core/MulticastDelegate.h +++ b/Nuake/src/Core/MulticastDelegate.h @@ -2,21 +2,32 @@ #include #include -#include -#define DECLARE_MULTICAST_DELEGATE(multicastDelegateName, ...) typedef MulticastDelegate<__VA_ARGS__> multicastDelegateName; +#define DECLARE_MULTICAST_DELEGATE(multicastDelegateName, ...) typedef MulticastDelegate<__VA_ARGS__> multicastDelegateName; + +struct DelegateHandle +{ + size_t id = InvalidHandle; + + static inline size_t InvalidHandle = static_cast(-1); + + bool IsValid() const { return id != InvalidHandle; } + void Reset() { id = InvalidHandle; } + + // Comparison operators for convenience + bool operator==(const DelegateHandle& other) const { return id == other.id; } + bool operator!=(const DelegateHandle& other) const { return id != other.id; } +}; template class MulticastDelegate { public: - using DelegateID = size_t; - // Add a callable with bound variables (supports no arguments as well) template - DelegateID AddStatic(Callable&& func, BoundArgs&&... boundArgs) + DelegateHandle AddStatic(Callable&& func, BoundArgs&&... boundArgs) { - DelegateID id = nextID++; + size_t id = GetNextID(); auto boundFunction = [=](Args... args) { if constexpr (sizeof...(Args) > 0) { @@ -27,14 +38,14 @@ public: func(boundArgs...); } }; - delegates.push_back({id, boundFunction}); - return id; + SetDelegate(id, boundFunction); + return DelegateHandle{ id }; } - template - DelegateID AddObject(Callable&& func, Obj* object, BoundArgs&&... boundArgs) + template + DelegateHandle AddRaw(Obj* object, Callable&& func, BoundArgs&&... boundArgs) { - DelegateID id = nextID++; + size_t id = GetNextID(); auto boundFunction = [=](Args... args) { if constexpr (sizeof...(Args) > 0) { @@ -45,42 +56,79 @@ public: (object->*func)(boundArgs...); } }; - delegates.push_back({id, boundFunction}); - return id; + SetDelegate(id, boundFunction); + return DelegateHandle{ id }; } // Remove a callable using the token returned by Add() - void Remove(DelegateID id) + void Remove(DelegateHandle& handle) { - auto it = std::remove_if(delegates.begin(), delegates.end(), [id](const auto& pair) - { - return pair.first == id; - }); + ASSERT(handle.IsValid()); - if (it != delegates.end()) + if (handle.IsValid() && handle.id < delegates.size()) { - delegates.erase(it, delegates.end()); + delegates[handle.id].active = false; + + // Mark this slot as reusable + freeIDs.push_back(handle.id); } + + // Invalidate the handle + handle.Reset(); } // Clear all delegates void Clear() { delegates.clear(); + freeIDs.clear(); + nextID = 0; } // Invoke all callables void Broadcast(Args... args) { - for (auto& [id, delegate] : delegates) + for (auto& delegate : delegates) { - delegate(std::forward(args)...); + if (delegate.active) + { + delegate.function(std::forward(args)...); + } } } private: - using DelegatePair = std::pair>; - - std::vector delegates; // Vector of (ID, callable) pairs - DelegateID nextID = 0; // Unique ID generator + struct Delegate + { + bool active = false; + std::function function; + }; + + // A vector of delegates with active state + std::vector delegates; + // List of reusable slots + std::vector freeIDs; + size_t nextID = 0; + + // Get the next available ID, either by reusing a free slot or by creating a new one + size_t GetNextID() + { + if (!freeIDs.empty()) + { + size_t id = freeIDs.back(); + freeIDs.pop_back(); + return id; + } + + return nextID++; + } + + // Set the delegate in the vector, makes the array larger if necessary + void SetDelegate(size_t id, const std::function& func) + { + if (id >= delegates.size()) + delegates.resize(id + 1); + + delegates[id] = { true, func }; + } }; diff --git a/Nuake/src/Scripting/ScriptingEngineNet.cpp b/Nuake/src/Scripting/ScriptingEngineNet.cpp index 75a404a5..b4371a6c 100644 --- a/Nuake/src/Scripting/ScriptingEngineNet.cpp +++ b/Nuake/src/Scripting/ScriptingEngineNet.cpp @@ -203,6 +203,8 @@ namespace Nuake managedObject.Destroy(); } + onUninitializeDelegate.Broadcast(); + Coral::GC::Collect(); Coral::GC::WaitForPendingFinalizers(); @@ -331,15 +333,6 @@ namespace Nuake return widgetUUIDToManagedObjects[std::make_pair(canvasUUID, uuid)]; } - template - void ScriptingEngineNet::AddListener(const T& delegate) {} - - template <> - void ScriptingEngineNet::AddListener(const GameAssemblyLoadedDelegate& delegate) - { - listenersGameAssemblyLoaded.push_back(delegate); - } - std::vector ScriptingEngineNet::BuildProjectAssembly(Ref project) { const std::string sanitizedProjectName = String::Sanitize(project->Name); @@ -541,10 +534,7 @@ namespace Nuake } } - for (auto& delegate : listenersGameAssemblyLoaded) - { - delegate(); - } + onGameAssemblyLoadedDelegate.Broadcast(); } } diff --git a/Nuake/src/Scripting/ScriptingEngineNet.h b/Nuake/src/Scripting/ScriptingEngineNet.h index 01b8ca2d..6d5ea092 100644 --- a/Nuake/src/Scripting/ScriptingEngineNet.h +++ b/Nuake/src/Scripting/ScriptingEngineNet.h @@ -25,6 +25,9 @@ namespace Nuake { class Project; + DECLARE_MULTICAST_DELEGATE(OnGameAssemblyLoadedDelegate) + DECLARE_MULTICAST_DELEGATE(OnUninitializeDelegate) + enum class ExposedVarTypes { Bool, @@ -115,8 +118,8 @@ namespace Nuake std::unordered_map GetPointEntities() const { return pointEntityTypes; } std::unordered_map GetUIWidgets() const { return uiWidgets; } - template void AddListener(const T& delegate); - template<> void AddListener(const GameAssemblyLoadedDelegate& delegate); + OnGameAssemblyLoadedDelegate& OnUninitialize() { return onUninitializeDelegate; } + OnUninitializeDelegate& OnGameAssemblyLoaded() { return onGameAssemblyLoadedDelegate; } private: const std::string m_Scope = "Nuake.Net"; @@ -149,7 +152,8 @@ namespace Nuake std::unordered_map entityToManagedObjects; std::map, Coral::ManagedObject> widgetUUIDToManagedObjects; - std::vector listenersGameAssemblyLoaded; + OnGameAssemblyLoadedDelegate onGameAssemblyLoadedDelegate; + OnUninitializeDelegate onUninitializeDelegate; ScriptingEngineNet(); ~ScriptingEngineNet(); diff --git a/Nuake/src/Subsystems/EngineSubsystem.cpp b/Nuake/src/Subsystems/EngineSubsystem.cpp index d3dc51a1..06193c7b 100644 --- a/Nuake/src/Subsystems/EngineSubsystem.cpp +++ b/Nuake/src/Subsystems/EngineSubsystem.cpp @@ -1,11 +1,18 @@ #include "EngineSubsystem.h" -void Nuake::EngineSubsystem::SetCanTick(bool canTick) +#include "src/Scripting/ScriptingEngineNet.h" + +namespace Nuake { - canEverTick = canTick; + void EngineSubsystem::SetCanTick(bool canTick) + { + canEverTick = canTick; + } + + bool EngineSubsystem::CanEverTick() const + { + return canEverTick; + } } -bool Nuake::EngineSubsystem::CanEverTick() const -{ - return canEverTick; -} + diff --git a/Nuake/src/Subsystems/EngineSubsystem.h b/Nuake/src/Subsystems/EngineSubsystem.h index f3e94b97..180190ea 100644 --- a/Nuake/src/Subsystems/EngineSubsystem.h +++ b/Nuake/src/Subsystems/EngineSubsystem.h @@ -23,7 +23,9 @@ namespace Nuake virtual void OnScenePostInitialize(Ref scene) {} virtual void OnScenePreDestroy(Ref scene) {} - private: + protected: + void OnScriptEngineUninitialize(); + bool canEverTick = false; }; } diff --git a/Nuake/src/Subsystems/EngineSubsystemScriptable.cpp b/Nuake/src/Subsystems/EngineSubsystemScriptable.cpp index f0f3f240..6feaa93d 100644 --- a/Nuake/src/Subsystems/EngineSubsystemScriptable.cpp +++ b/Nuake/src/Subsystems/EngineSubsystemScriptable.cpp @@ -1,6 +1,8 @@ #include "EngineSubsystemScriptable.h" -#include "Coral/Type.hpp" +#include "src/Scripting/ScriptingEngineNet.h" + +#include namespace Nuake { @@ -11,6 +13,16 @@ EngineSubsystemScriptable::EngineSubsystemScriptable(const Coral::ManagedObject& } +EngineSubsystemScriptable::~EngineSubsystemScriptable() +{ + if (!cSharpObjectInstance.IsValid()) + return; + + ScriptingEngineNet::Get().OnUninitialize().Remove(scriptEngineUninitializeDelegateHandle); + + cSharpObjectInstance.Destroy(); +} + Coral::ManagedObject& EngineSubsystemScriptable::GetManagedObjectInstance() { return cSharpObjectInstance; @@ -18,6 +30,8 @@ Coral::ManagedObject& EngineSubsystemScriptable::GetManagedObjectInstance() void EngineSubsystemScriptable::Initialize() { + scriptEngineUninitializeDelegateHandle = ScriptingEngineNet::Get().OnUninitialize().AddRaw(this, &EngineSubsystemScriptable::OnScriptEngineUninitialize); + if (!cSharpObjectInstance.IsValid()) return; @@ -52,12 +66,18 @@ void EngineSubsystemScriptable::OnScenePreDestroy(Ref scene) { if (!cSharpObjectInstance.IsValid()) return; - - if (cSharpObjectInstance.GetType().GetTypeId() == -1) - return; cSharpObjectInstance.InvokeMethod("InternalOnScenePreDestroy"); } + +void EngineSubsystemScriptable::OnScriptEngineUninitialize() +{ + if (!cSharpObjectInstance.IsValid()) + return; + + cSharpObjectInstance.Destroy(); +} + } diff --git a/Nuake/src/Subsystems/EngineSubsystemScriptable.h b/Nuake/src/Subsystems/EngineSubsystemScriptable.h index 2934431c..91c0f19c 100644 --- a/Nuake/src/Subsystems/EngineSubsystemScriptable.h +++ b/Nuake/src/Subsystems/EngineSubsystemScriptable.h @@ -1,6 +1,7 @@ #pragma once #include "EngineSubsystem.h" +#include "src/Core/MulticastDelegate.h" #include @@ -13,6 +14,7 @@ namespace Nuake { public: EngineSubsystemScriptable(const Coral::ManagedObject& object); + virtual ~EngineSubsystemScriptable(); Coral::ManagedObject& GetManagedObjectInstance(); @@ -23,7 +25,11 @@ namespace Nuake virtual void OnScenePostInitialize(Ref scene) override; virtual void OnScenePreDestroy(Ref scene) override; - private: + protected: + void OnScriptEngineUninitialize(); + + DelegateHandle scriptEngineUninitializeDelegateHandle; + Coral::ManagedObject cSharpObjectInstance; }; }