implemented createModule() and getModule()

This commit is contained in:
Timerix 2025-06-20 21:51:56 +05:00
parent a288d0961f
commit 9a283a2904
13 changed files with 166 additions and 70 deletions

View File

@ -7,19 +7,14 @@
namespace ougge { namespace ougge {
EngineModule::EngineModule(Engine& engine, const std::string& name) IEngineModule::IEngineModule(Engine& engine)
: engine(engine), name(name) : engine(engine)
{} {}
void EngineModule::beginFrame() {} void IEngineModule::beginFrame() {}
void EngineModule::endFrame() {} void IEngineModule::endFrame() {}
void Engine::addModule(EngineModule* m){
modules.push_back(m);
}
void Engine::startLoop() void Engine::startLoop()
{ {
if(loop_running) if(loop_running)
@ -50,10 +45,10 @@ void Engine::stopLoop(){
loop_running = false; loop_running = false;
} }
void Engine::handleModuleError(EngineModule* module, const char* type, const char* method, const char* error){ void Engine::handleModuleError(IEngineModule& module, const char* type, const char* method, const char* error){
std::string error_message = ougge_format( std::string error_message = ougge_format(
"Catched %s at %s.%s(): %s", "Catched %s at %s.%s(): %s",
type, module->name.c_str(), method, error); type, module.getName(), method, error);
std::cerr<<error_message<<std::endl; std::cerr<<error_message<<std::endl;
error_messages.push_back(error_message); error_messages.push_back(error_message);
} }
@ -62,10 +57,10 @@ void Engine::tryDrawFrame(){
auto it = modules.begin(); auto it = modules.begin();
while(it != modules.end()) while(it != modules.end())
{ {
EngineModule* module = *it; IEngineModule& module = **it;
it++; it++;
try { try {
module->beginFrame(); module.beginFrame();
} }
catch(const std::exception& e){ catch(const std::exception& e){
handleModuleError(module, "exception", "beginFrame", e.what()); handleModuleError(module, "exception", "beginFrame", e.what());
@ -81,17 +76,13 @@ void Engine::tryDrawFrame(){
} }
} }
//TODO: frame expeption display
// if(draw_error_window)
// gui::drawErrorWindow(error_message, &draw_error_window);
it = modules.end(); it = modules.end();
while(it != modules.begin()) while(it != modules.begin())
{ {
it--; it--;
EngineModule* module = *it; IEngineModule& module = **it;
try { try {
module->endFrame(); module.endFrame();
} }
catch(const std::exception& e){ catch(const std::exception& e){
handleModuleError(module, "exception", "endFrame", e.what()); handleModuleError(module, "exception", "endFrame", e.what());

View File

@ -4,32 +4,36 @@
#include "common/std.hpp" #include "common/std.hpp"
#include "common/UsefulException.hpp" #include "common/UsefulException.hpp"
#include "common/ougge_format.hpp" #include "common/ougge_format.hpp"
#include "common/type_name_demangled.hpp"
#include <type_traits>
#include <memory>
#include <iostream>
namespace ougge { namespace ougge {
class Engine; class Engine;
class EngineModule { class IEngineModule {
public: public:
EngineModule() = delete; IEngineModule() = delete;
EngineModule(const EngineModule&) = delete; IEngineModule(const IEngineModule&) = delete;
EngineModule& operator=(const EngineModule&) = delete; IEngineModule& operator=(const IEngineModule&) = delete;
EngineModule(EngineModule&&) = delete; IEngineModule(IEngineModule&&) = delete;
EngineModule& operator=(EngineModule&&) = delete; IEngineModule& operator=(IEngineModule&&) = delete;
Engine& engine; Engine& engine;
const std::string name; virtual const std::string& getName() = 0;
virtual void beginFrame(); virtual void beginFrame();
virtual void endFrame(); virtual void endFrame();
protected: protected:
EngineModule(Engine& engine, const std::string& name); IEngineModule(Engine& engine);
}; };
class Engine { class Engine {
std::vector<EngineModule*> modules; std::vector<std::unique_ptr<IEngineModule>> modules;
bool loop_running = false; bool loop_running = false;
public: public:
@ -43,14 +47,60 @@ public:
Engine(Engine&&) = delete; Engine(Engine&&) = delete;
Engine& operator=(Engine&&) = delete; Engine& operator=(Engine&&) = delete;
void addModule(EngineModule* m);
// start game loop on the current thread // start game loop on the current thread
void startLoop(); void startLoop();
void stopLoop(); void stopLoop();
//TODO: add std::enable_if
template<typename ModuleT, typename... ConstructorArgsT>
ModuleT& createModule(ConstructorArgsT&&... args){
static_assert(std::is_base_of<IEngineModule, ModuleT>::value);
auto& module_type_name = ougge_type_name<ModuleT>();
//TODO: replace with some logger call
std::cout<<"Initializing module '"<<module_type_name<<"'"<<std::endl;
size_t module_index;
bool module_exists;
getOrCreateModuleIndex<ModuleT>(&module_index, &module_exists);
if(module_exists){
throw UsefulException(ougge_format("can't create second instance of module '%s'",
module_type_name.c_str()));
}
//TODO: std::forward(args)...
modules.push_back(std::make_unique<ModuleT>(*this, args...));
return *static_cast<ModuleT*>(modules[module_index].get());
}
//TODO: add std::enable_if
template<typename ModuleT>
ModuleT& getModule(){
static_assert(std::is_base_of<IEngineModule, ModuleT>::value);
size_t module_index;
bool module_exists;
getOrCreateModuleIndex<ModuleT>(&module_index, &module_exists);
if(!module_exists){
auto& module_type_name = ougge_type_name<ModuleT>();
throw UsefulException(ougge_format("engine has no module '%s'",
module_type_name.c_str()));
}
return *static_cast<ModuleT*>(modules[module_index].get());
}
private: private:
void tryDrawFrame(); void tryDrawFrame();
void handleModuleError(EngineModule *module, const char *type, const char *method, const char *error); void handleModuleError(IEngineModule& module, const char *type, const char *method, const char *error);
//TODO: add std::enable_if
template<typename ModuleT>
void getOrCreateModuleIndex(size_t* out_index, bool* out_module_exists){
static size_t module_index = modules.size();
*out_index = module_index;
// if module_index == modules.size(), then the module hasn't been pushed to the vector yet
*out_module_exists = module_index != modules.size();
}
}; };
} }

View File

@ -15,7 +15,7 @@ class UsefulException_ : public std::exception {
public: public:
UsefulException_(const std::string& msg, const std::string& _file, const std::string& _func, int line_n); UsefulException_(const std::string& msg, const std::string& _file, const std::string& _func, int line_n);
virtual char const* what() const noexcept; char const* what() const noexcept override;
}; };
#define useful_assert(EXPR, ERRMSG) if(!(EXPR)) throw UsefulException(ERRMSG); #define useful_assert(EXPR, ERRMSG) if(!(EXPR)) throw UsefulException(ERRMSG);

View File

@ -35,6 +35,7 @@ public:
std::enable_if_t<!std::is_void<RT>::value, RT> operator()(ArgTypes... args){ std::enable_if_t<!std::is_void<RT>::value, RT> operator()(ArgTypes... args){
if(func_ptr == nullptr) if(func_ptr == nullptr)
throw UsefulException("function_shared_ptr is null"); throw UsefulException("function_shared_ptr is null");
//TODO: think about std::forward(args)...
return func_ptr->operator()(args...); return func_ptr->operator()(args...);
} }

View File

@ -17,7 +17,6 @@ typedef double f64;
/// anonymous pointer without specified freeMembers() func /// anonymous pointer without specified freeMembers() func
typedef void* Pointer; typedef void* Pointer;
#define nameof(V) #V
#ifdef _MSC_VER #ifdef _MSC_VER
#pragma comment(lib, "mincore_downlevel.lib") // Support OS older than SDK #pragma comment(lib, "mincore_downlevel.lib") // Support OS older than SDK

View File

@ -0,0 +1,44 @@
// https://stackoverflow.com/a/53865723
#pragma once
#ifndef _MSC_VER
#include <cxxabi.h>
#endif
#include <memory>
#include <string>
#include <cstdlib>
template <class T>
std::string _ougge_type_name_demangled()
{
typedef typename std::remove_reference<T>::type TR;
const char* type_name_mangled = typeid(TR).name();
std::unique_ptr<char, void(*)(void*)> own
(
#ifndef _MSC_VER
abi::__cxa_demangle(type_name_mangled, nullptr, nullptr, nullptr),
#else
nullptr,
#endif
std::free
);
std::string r = own ? own.get() : type_name_mangled;
if (std::is_const<TR>::value)
r += " const";
if (std::is_volatile<TR>::value)
r += " volatile";
if (std::is_lvalue_reference<T>::value)
r += "&";
else if (std::is_rvalue_reference<T>::value)
r += "&&";
return r;
}
/// @return human-readable type name
template <class T>
const std::string& ougge_type_name(){
static std::string name = _ougge_type_name_demangled<T>();
return name;
}

View File

@ -8,18 +8,22 @@
using namespace ougge; using namespace ougge;
class TutelModule : public EngineModule { class TutelModule : public IEngineModule {
resources::CacheStorage<resources::Texture> textures; resources::CacheStorage<resources::Texture> textures;
modules::MainWindowSDL2& mainWindow;
public: public:
TutelModule(Engine& engine, resources::ResourceManager& resourceManager, modules::MainWindowSDL2& mainWindow) : TutelModule(Engine& engine, resources::ResourceManager& resourceManager) :
EngineModule(engine, nameof(TutelModule)), IEngineModule(engine),
textures(&resourceManager), textures(&resourceManager)
mainWindow(mainWindow)
{ {
//TODO: add something like `assert(requireModule(MainWindow))`
} }
virtual void beginFrame(){ const std::string& getName() override {
return ougge_type_name<TutelModule>();
}
void beginFrame() override {
auto& mainWindow = engine.getModule<modules::MainWindowSDL2>();
resources::Texture* tutel = textures.tryGetOrCreate("tutel.png", mainWindow.sdl_renderer); resources::Texture* tutel = textures.tryGetOrCreate("tutel.png", mainWindow.sdl_renderer);
if(tutel == nullptr){ if(tutel == nullptr){
throw new UsefulException("couldn't find resource 'tutel.png'"); throw new UsefulException("couldn't find resource 'tutel.png'");
@ -33,31 +37,27 @@ public:
}; };
int main(int argc, const char** argv){ void createExampleObject(Engine& engine){
try { std::cout<<"creating ExampleObject"<<std::endl;
std::cout<<"initializing ResourceManager"; auto& monoSystem = engine.getModule<modules::MonoGameObjectSystem>();
resources::ResourceManager resourceManager;
std::cout<<"initializing Engine";
Engine engine;
std::cout<<"initializing MainWindowSDL2"<<std::endl;
modules::MainWindowSDL2 mainWindow (engine, "ougge", resourceManager);
engine.addModule(&mainWindow);
std::cout<<"initializing MonoGameObjectSystem"<<std::endl;
modules::MonoGameObjectSystem monoSystem (engine, 64*1024);
engine.addModule(&monoSystem);
std::cout<<"createing ExampleObject"<<std::endl;
game::GameObject& exampleObj = monoSystem.createGameObject(); game::GameObject& exampleObj = monoSystem.createGameObject();
std::string componentClassName = "Ougge.ExampleComponent"; std::string componentClassName = "Ougge.ExampleComponent";
if(!monoSystem.tryCreateComponent(exampleObj, componentClassName)) if(!monoSystem.tryCreateComponent(exampleObj, componentClassName))
throw UsefulException(ougge_format("couldn't create component '%s'", componentClassName.c_str())); throw UsefulException(ougge_format("couldn't create component '%s'", componentClassName.c_str()));
}
std::cout<<"initializing TutelModule"<<std::endl; int main(int argc, const char** argv){
TutelModule tutel (engine, resourceManager, mainWindow); try {
engine.addModule(&tutel); std::cout<<"Initializing Engine"<<std::endl;
Engine engine;
std::cout<<"Initializing ResourceManager"<<std::endl;
resources::ResourceManager resourceManager;
engine.createModule<modules::MainWindowSDL2>("ougge", resourceManager);
engine.createModule<modules::MonoGameObjectSystem>(64*1024);
createExampleObject(engine);
engine.createModule<TutelModule>(resourceManager);
std::cout<<"main loop start"<<std::endl; std::cout<<"main loop start"<<std::endl;
engine.startLoop(); engine.startLoop();

View File

@ -10,6 +10,10 @@ using namespace ougge::gui;
namespace ougge::modules { namespace ougge::modules {
const std::string& MainWindowSDL2::getName() {
return ougge_type_name<MainWindowSDL2>();
}
f32 MainWindowSDL2::getDPI(){ f32 MainWindowSDL2::getDPI(){
i32 w=0, h=0; i32 w=0, h=0;
SDL_GetRendererOutputSize(sdl_renderer, &w, &h); SDL_GetRendererOutputSize(sdl_renderer, &w, &h);
@ -24,7 +28,7 @@ f32 MainWindowSDL2::getDPI(){
MainWindowSDL2::MainWindowSDL2(Engine& engine, MainWindowSDL2::MainWindowSDL2(Engine& engine,
const std::string& window_title, const std::string& window_title,
resources::ResourceManager& resourceManager) resources::ResourceManager& resourceManager)
: EngineModule(engine, nameof(MainWindowSDL2)) : IEngineModule(engine)
{ {
SDL_TRY(SDL_Init(SDL_INIT_EVERYTHING)); SDL_TRY(SDL_Init(SDL_INIT_EVERYTHING));
SDL_version v; SDL_version v;

View File

@ -17,7 +17,7 @@ namespace ougge::modules {
#define default_font_path "fonts/DroidSans.ttf" #define default_font_path "fonts/DroidSans.ttf"
class MainWindowSDL2 : public EngineModule { class MainWindowSDL2 : public IEngineModule {
public: public:
f32 default_font_size = 14.0f; f32 default_font_size = 14.0f;
ImVec4 clear_color = RGBAHexToF(35,35,50,255); ImVec4 clear_color = RGBAHexToF(35,35,50,255);
@ -34,8 +34,9 @@ public:
resources::ResourceManager& resourceManager); resources::ResourceManager& resourceManager);
~MainWindowSDL2(); ~MainWindowSDL2();
virtual void beginFrame(); const std::string& getName() override;
virtual void endFrame(); void beginFrame() override;
void endFrame() override;
f32 getDPI(); f32 getDPI();
private: private:

View File

@ -2,8 +2,12 @@
namespace ougge::modules { namespace ougge::modules {
const std::string& MonoGameObjectSystem::getName() {
return ougge_type_name<MonoGameObjectSystem>();
}
MonoGameObjectSystem::MonoGameObjectSystem(Engine& engine, u32 max_game_objects) : MonoGameObjectSystem::MonoGameObjectSystem(Engine& engine, u32 max_game_objects) :
EngineModule(engine, nameof(MonoGameObjectSystem)), IEngineModule(engine),
gameObjectPool(max_game_objects) gameObjectPool(max_game_objects)
{ {
engineManagedAssembly = mono.loadAssembly("Ougge.dll"); engineManagedAssembly = mono.loadAssembly("Ougge.dll");

View File

@ -7,7 +7,7 @@
namespace ougge::modules { namespace ougge::modules {
class MonoGameObjectSystem : public EngineModule { class MonoGameObjectSystem : public IEngineModule {
Mono::RuntimeJIT mono; Mono::RuntimeJIT mono;
game::GameObjectPool gameObjectPool; game::GameObjectPool gameObjectPool;
u64 obj_id = 0; u64 obj_id = 0;
@ -21,7 +21,8 @@ public:
MonoGameObjectSystem(Engine& engine, u32 max_game_objects); MonoGameObjectSystem(Engine& engine, u32 max_game_objects);
virtual void beginFrame(); const std::string& getName() override;
void beginFrame() override;
game::GameObject& createGameObject(); game::GameObject& createGameObject();
bool tryCreateComponent(game::GameObject& obj, const std::string& componentClassName); bool tryCreateComponent(game::GameObject& obj, const std::string& componentClassName);

View File

@ -4,10 +4,10 @@ class MemoryStreamBuf : public std::streambuf {
public: public:
MemoryStreamBuf(void* p, const std::size_t n); MemoryStreamBuf(void* p, const std::size_t n);
virtual std::istream::pos_type seekoff( std::istream::pos_type seekoff(
std::istream::off_type off, std::istream::off_type off,
std::ios_base::seekdir dir, std::ios_base::seekdir dir,
std::ios_base::openmode which); std::ios_base::openmode which) override;
}; };
MemoryStreamBuf::MemoryStreamBuf(void* _p, const std::size_t n){ MemoryStreamBuf::MemoryStreamBuf(void* _p, const std::size_t n){

View File

@ -23,6 +23,7 @@ public:
using ResourceMap = std::unordered_map<std::string, ResourceFactory>; using ResourceMap = std::unordered_map<std::string, ResourceFactory>;
//TODO: rewrite ResourceManager as a module
class ResourceManager { class ResourceManager {
std::list<std::shared_ptr<ResourceMap>> _resourceMaps; std::list<std::shared_ptr<ResourceMap>> _resourceMaps;
static std::shared_ptr<ResourceMap> _embeddedResourcesMap; static std::shared_ptr<ResourceMap> _embeddedResourcesMap;