Completed work on exposing subsystems to scene events

This commit is contained in:
WiggleWizard
2024-09-19 21:43:04 +01:00
parent edce52bb17
commit a487d31c48
9 changed files with 134 additions and 55 deletions

View File

@@ -42,7 +42,7 @@ namespace Nuake
{
Window::Get()->OnWindowSetScene().AddStatic(&Engine::OnWindowSetScene);
ScriptingEngineNet::Get().AddListener<ScriptingEngineNet::GameAssemblyLoadedDelegate>(&Engine::OnScriptingEngineGameAssemblyLoaded);
ScriptingEngineNet::Get().OnGameAssemblyLoaded().AddStatic(&Engine::OnScriptingEngineGameAssemblyLoaded);
AudioManager::Get().Initialize();
PhysicsManager::Get().Init();

View File

@@ -60,6 +60,8 @@ namespace Nuake
protected:
static void OnWindowSetScene(Ref<Scene> oldScene, Ref<Scene> newScene);
static void InitializeCoreSubsystems();
static void OnScriptingEngineUninitialize();
static void OnScriptingEngineGameAssemblyLoaded();
static void OnScenePreInitialize(Ref<Scene> scene);

View File

@@ -2,21 +2,32 @@
#include <functional>
#include <vector>
#include <algorithm>
#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<size_t>(-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<typename... Args>
class MulticastDelegate
{
public:
using DelegateID = size_t;
// Add a callable with bound variables (supports no arguments as well)
template<typename Callable, typename... BoundArgs>
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<typename Callable, typename Obj, typename... BoundArgs>
DelegateID AddObject(Callable&& func, Obj* object, BoundArgs&&... boundArgs)
template<typename Obj, typename Callable, typename... BoundArgs>
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>(args)...);
if (delegate.active)
{
delegate.function(std::forward<Args>(args)...);
}
}
}
private:
using DelegatePair = std::pair<DelegateID, std::function<void(Args...)>>;
std::vector<DelegatePair> delegates; // Vector of (ID, callable) pairs
DelegateID nextID = 0; // Unique ID generator
struct Delegate
{
bool active = false;
std::function<void(Args...)> function;
};
// A vector of delegates with active state
std::vector<Delegate> delegates;
// List of reusable slots
std::vector<size_t> 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<void(Args...)>& func)
{
if (id >= delegates.size())
delegates.resize(id + 1);
delegates[id] = { true, func };
}
};

View File

@@ -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<class T>
void ScriptingEngineNet::AddListener(const T& delegate) {}
template <>
void ScriptingEngineNet::AddListener<ScriptingEngineNet::GameAssemblyLoadedDelegate>(const GameAssemblyLoadedDelegate& delegate)
{
listenersGameAssemblyLoaded.push_back(delegate);
}
std::vector<CompilationError> ScriptingEngineNet::BuildProjectAssembly(Ref<Project> project)
{
const std::string sanitizedProjectName = String::Sanitize(project->Name);
@@ -541,10 +534,7 @@ namespace Nuake
}
}
for (auto& delegate : listenersGameAssemblyLoaded)
{
delegate();
}
onGameAssemblyLoadedDelegate.Broadcast();
}
}

View File

@@ -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<std::string, NetGameScriptObject> GetPointEntities() const { return pointEntityTypes; }
std::unordered_map<std::string, UIWidgetObject> GetUIWidgets() const { return uiWidgets; }
template<class T> 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<uint32_t, Coral::ManagedObject> entityToManagedObjects;
std::map<std::pair<UUID, UUID>, Coral::ManagedObject> widgetUUIDToManagedObjects;
std::vector<GameAssemblyLoadedDelegate> listenersGameAssemblyLoaded;
OnGameAssemblyLoadedDelegate onGameAssemblyLoadedDelegate;
OnUninitializeDelegate onUninitializeDelegate;
ScriptingEngineNet();
~ScriptingEngineNet();

View File

@@ -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;
}

View File

@@ -23,7 +23,9 @@ namespace Nuake
virtual void OnScenePostInitialize(Ref<Scene> scene) {}
virtual void OnScenePreDestroy(Ref<Scene> scene) {}
private:
protected:
void OnScriptEngineUninitialize();
bool canEverTick = false;
};
}

View File

@@ -1,6 +1,8 @@
#include "EngineSubsystemScriptable.h"
#include "Coral/Type.hpp"
#include "src/Scripting/ScriptingEngineNet.h"
#include <Coral/Type.hpp>
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> scene)
{
if (!cSharpObjectInstance.IsValid())
return;
if (cSharpObjectInstance.GetType().GetTypeId() == -1)
return;
cSharpObjectInstance.InvokeMethod("InternalOnScenePreDestroy");
}
void EngineSubsystemScriptable::OnScriptEngineUninitialize()
{
if (!cSharpObjectInstance.IsValid())
return;
cSharpObjectInstance.Destroy();
}
}

View File

@@ -1,6 +1,7 @@
#pragma once
#include "EngineSubsystem.h"
#include "src/Core/MulticastDelegate.h"
#include <Coral/ManagedObject.hpp>
@@ -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> scene) override;
virtual void OnScenePreDestroy(Ref<Scene> scene) override;
private:
protected:
void OnScriptEngineUninitialize();
DelegateHandle scriptEngineUninitializeDelegateHandle;
Coral::ManagedObject cSharpObjectInstance;
};
}