Module reflection system

This commit is contained in:
antopilo
2025-03-24 22:55:40 -04:00
parent c2d6a12aa5
commit 87ba90a6a0
5 changed files with 245 additions and 3 deletions

View File

@@ -2,12 +2,59 @@
#include "AudioSceneSystem.h"
#include "Nuake/Scene/SceneSystems.h"
#include "Nuake/Modules/ModuleDB.h"
#include <Nuake/Core/Logger.h>
void PlayAudio(int id, float volume)
{
}
void StopAudio()
{
}
NUAKEMODULE(AudioModule)
void AudioModule_Startup()
{
using namespace Nuake;
SceneSystemDB::Get().RegisterSceneSystem<AudioModule::AudioSystem>();
auto& module = ModuleDB::Get().RegisterModule<AudioModule>();
module.Description = "Core audio module.";
module.BindFunction<PlayAudio>("PlayAudio", "id", "volume");
module.BindFunction<StopAudio>("StopAudio");
Logger::Log("AudioModule exposed API:", "Module", VERBOSE);
auto reflection = module.Resolve();
for (auto [id, func] : reflection.func())
{
const std::string_view returnType = func.ret().info().name();
const std::string_view funcName = module.GetTypeName(id);
std::string msg = std::string(returnType) + " " + std::string(funcName) + "(";
auto argNames = module.GetFuncArgNames(id);
std::vector<std::string_view> args;
for (int i = 0; i < func.arity(); i++)
{
const std::string argType = std::string(func.arg(i).info().name());
args.push_back(argType);
msg += argType + " " + argNames[i];
if (i < func.arity() - 1)
{
msg += ", ";
}
}
msg += ")";
Logger::Log(msg, "", VERBOSE);
}
SceneSystemDB::Get().RegisterSceneSystem<Audio::AudioSystem>();
}
void AudioModule_Shutdown()

View File

@@ -9,7 +9,7 @@
#include <future>
namespace AudioModule
namespace Audio
{
using namespace Nuake;

View File

@@ -1,7 +1,7 @@
#pragma once
#include "Nuake/Scene/Systems/System.h"
namespace AudioModule
namespace Audio
{
using namespace Nuake;

View File

@@ -1,7 +1,83 @@
#include "ExampleModule.h"
#include "Nuake/Modules/ModuleDB.h"
#include "Nuake/Core/Logger.h"
#include "Nuake/Resource/Serializer/ComponentSerializer.h"
#include "Nuake/Scene/Components/AudioEmitterComponent.h"
#include "Nuake/Scene/Components/LightComponent.h"
#include "Nuake/Scene/Components/Component.h"
#include <entt/entt.hpp>
#include "Thirdparty/entt/src/entt/meta/meta.hpp"
#include "Thirdparty/entt/src/entt/core/hashed_string.hpp"
namespace Nuake
{
class MyModuleComponent : public Component
{
NUAKECOMPONENT(MyModuleComponent, "My custom component")
static void InitializeComponentClass()
{
BindComponentField<&MyModuleComponent::MyFloat>("MyFloat", "My Float");
}
public:
float MyFloat = 1.0f;
};
}
void ExampleFunction() { }
void ExampleModuleLog(const std::string& hi)
{
Nuake::Logger::Log(hi, "ExampleModule", Nuake::VERBOSE);
}
NUAKEMODULE(ExampleModule)
void ExampleModule_Startup()
{
using namespace Nuake;
auto& module = ModuleDB::Get().RegisterModule<ExampleModule>();
module.Description = "This is an example module";
// This is to expose functions to the rest of the engine
module.BindFunction<ExampleFunction>("ExampleFunction");
module.BindFunction<ExampleModuleLog>("ExampleModuleLog", "hi");
Logger::Log("ExampleModule exposed API:", "Module", VERBOSE);
auto reflection = module.Resolve();
for (auto [id, func] : reflection.func())
{
const std::string_view returnType = func.ret().info().name();
const std::string_view funcName = module.GetTypeName(id);
std::string msg = std::string(returnType) + " " + std::string(funcName) + "(";
auto argNames = module.GetFuncArgNames(id);
std::vector<std::string_view> args;
for (int i = 0; i < func.arity(); i++)
{
const std::string argType = std::string(func.arg(i).info().name());
args.push_back(argType);
msg += argType + " " + argNames[i];
if (i < func.arity() - 1)
{
msg += ", ";
}
}
msg += ")";
Logger::Log(msg, "", VERBOSE);
}
// This is where you would initialize your module
// This function is called when the engine starts up
@@ -10,8 +86,27 @@ void ExampleModule_Startup()
// You can register custom asset bakers to support custom formats
// Example: See Assimp Module for reference
using namespace Nuake;
MyModuleComponent::InternalInitializeClass();
AudioEmitterComponent component;
LightComponent lightComponent;
ComponentSerializer serializer;
json jsonData = serializer.Serialize(component);
json jsonDataOG = component.Serialize();
//Logger::Log("Serialized reflected component: " + jsonData.dump(4), "ExampleModule", VERBOSE);
//Logger::Log("Serialized original component: " + jsonDataOG.dump(4), "ExampleModule", VERBOSE);
jsonData = serializer.Serialize(lightComponent);
jsonDataOG = lightComponent.Serialize();
//Logger::Log("Serialized reflected component: " + jsonData.dump(4), "ExampleModule", VERBOSE);
//Logger::Log("Serialized original component: " + jsonDataOG.dump(4), "ExampleModule", VERBOSE);
}
void ExampleModule_Shutdown()
{

View File

@@ -0,0 +1,100 @@
#pragma once
#include <string>
#include <any>
#include <vector>
#include <entt/entt.hpp>
#define NUAKEMODULE(name) \
using TypeNameMap = std::map<entt::id_type, std::string>; \
\
struct name \
{ \
const std::string Name = #name; \
std::string Description = "No Description"; \
entt::meta_factory<name> ModuleFactory = entt::meta<name>(); \
TypeNameMap TypeNames; \
\
std::map<entt::id_type, std::vector<std::string>> FuncArgNames; \
\
template<auto T, typename... Args> \
inline name& BindFunction(const std::string& name, Args... argNames) \
{ \
entt::id_type typeId = entt::hashed_string(name.c_str()); \
TypeNames[typeId] = name; \
FuncArgNames[typeId] = { argNames ... }; \
ModuleFactory.func<T>(typeId); \
return *this; \
} \
\
auto Resolve() \
{ \
return entt::resolve<name>(); \
} \
\
std::vector<std::string> GetFuncArgNames(entt::id_type id) \
{ \
return FuncArgNames[id]; \
} \
\
std::vector<std::string> GetAllFuncNames() \
{\
std::vector<std::string> names; \
names.reserve(TypeNames.size()); \
for (const auto& [_, name] : TypeNames) \
{ \
names.push_back(name);\
}\
return names;\
}\
\
std::string_view GetTypeName(entt::id_type id) \
{ \
if (TypeNames.contains(id)) \
{ \
return TypeNames[id]; \
} \
return "Unknown"; \
}\
};
namespace Nuake
{
// This is a singleton that knows about every modules and their public API
class ModuleDB
{
public:
ModuleDB() = default;
~ModuleDB() = default;
static ModuleDB& Get()
{
static ModuleDB instance;
return instance;
}
template<typename T>
T& RegisterModule()
{
Modules[typeid(T).name()] = T();
return std::any_cast<T&>(Modules[typeid(T).name()]);
}
template<typename T>
T& GetModule()
{
const std::string_view typeName = typeid(T).name();
if (!Modules.contains(typeName))
{
assert(false && "Module not found.");
return;
}
return std::any_cast<T>(Modules[typeName]);
}
private:
std::map<std::string, std::any> Modules;
};
}