diff --git a/editor/Editor.cpp b/editor/Editor.cpp index 2a58a78..13fa27d 100644 --- a/editor/Editor.cpp +++ b/editor/Editor.cpp @@ -160,53 +160,18 @@ void Editor::reloadScriptLib() [](const auto& e) -> bool { return e.template get<0>().instance == nullptr; } )); - m_listScriptNames = {}; - m_listScriptParameters = {}; - m_makeScriptInstance = {}; - if (m_project.scriptLib().empty()) - m_scriptLibHandle.reset(); - else - m_scriptLibHandle.reset(dlLoad(m_project.scriptLib().string().c_str())); - - if (m_scriptLibHandle == nullptr) - throw std::runtime_error(std::format("unable to load shared lib : {}", m_project.scriptLib().string())); - - void* listScriptNamesSym = getSym(m_scriptLibHandle.get(), "listScriptNames"); - void* listScriptParametersSym = getSym(m_scriptLibHandle.get(), "listScriptParameters"); - void* makeScriptInstanceSym = getSym(m_scriptLibHandle.get(), "makeScriptInstance"); - - if (listScriptNamesSym == nullptr || listScriptParametersSym == nullptr || makeScriptInstanceSym == nullptr) { - m_scriptLibHandle.reset(); - throw std::runtime_error(std::format("unable to get symbols in lib : {}", m_project.scriptLib().string())); + m_listScriptNames = {}; + m_listScriptParameters = {}; + m_makeScriptInstance = {}; + return; } - m_listScriptNames = [listScriptNamesSym]() -> std::vector { - const char** names = nullptr; - unsigned long count = 0; - reinterpret_cast(listScriptNamesSym)(&names, &count); - std::vector output; - for (unsigned long i = 0; i < count; i++) - if (names[i] != nullptr) - output.emplace_back(names[i]); - return output; - }; - - m_listScriptParameters = [listScriptParametersSym](const std::string& name) -> std::vector { - const GE::ScriptParameterDescriptor* parameters = nullptr; - unsigned long count = 0; - reinterpret_cast(listScriptParametersSym)(name.c_str(), ¶meters, &count); - std::vector output; - output.reserve(count); - for (unsigned long i = 0; i < count; i++) - output.emplace_back(parameters[i]); - return output; - }; - - m_makeScriptInstance = [makeScriptInstanceSym](const std::string& name) -> std::shared_ptr { - return std::shared_ptr(reinterpret_cast(makeScriptInstanceSym)(name.c_str())); - }; + GE::ScriptLibraryManager manager(m_project.scriptLib()); + m_listScriptNames = manager.listScriptNamesFunction(); + m_listScriptParameters = manager.listScriptParametersFunction(); + m_makeScriptInstance = manager.makeScriptInstanceFunction(); } void Editor::startGame() diff --git a/editor/Editor.hpp b/editor/Editor.hpp index 1f2893f..20e3926 100644 --- a/editor/Editor.hpp +++ b/editor/Editor.hpp @@ -18,22 +18,15 @@ #include #include #include -#include +#include #include -#include - #include #include #include -#include #include -#include -#include #include -#include -#include namespace GE_Editor { @@ -53,16 +46,6 @@ class Editor : public GE::Application ~Editor() override = default; private: - struct DlHandleDeleter - { - void operator()(DlHandle handle) const - { - if (handle != nullptr) - dlFree(handle); - } - }; - using UniqueDlHandle = std::unique_ptr, DlHandleDeleter>; - void loadProject(const std::filesystem::path&); void saveEditedScene(); void saveProject(); @@ -76,11 +59,6 @@ class Editor : public GE::Application void rebuildFrameGraph(); void renderImgui(); - UniqueDlHandle m_scriptLibHandle; // need to be before m_edited scene so it is destroyed after - std::function()> m_listScriptNames; - std::function(const std::string&)> m_listScriptParameters; - std::function(const std::string&)> m_makeScriptInstance; - std::filesystem::path m_projectFilePath; Project m_project; @@ -97,6 +75,10 @@ class Editor : public GE::Application std::pair m_viewportSize = {0, 0}; GE::FrameGraph m_frameGraph; // TODO move into render (something like render->setGraph()) + GE::ListScriptNamesFn m_listScriptNames; + GE::ListScriptParametersFn m_listScriptParameters; + GE::MakeScriptInstanceFn m_makeScriptInstance; + public: Editor& operator=(const Editor&) = delete; Editor& operator=(Editor&&) = delete; diff --git a/editor/UI/EntityInspectorPanel.cpp b/editor/UI/EntityInspectorPanel.cpp index 31bd5ad..36b0323 100644 --- a/editor/UI/EntityInspectorPanel.cpp +++ b/editor/UI/EntityInspectorPanel.cpp @@ -12,7 +12,7 @@ #include #include #include -#include +#include #include @@ -92,11 +92,15 @@ template<> void EntityInspectorPanel::componentEditWidget() { GE::ScriptComponent& scriptComponent = m_entity.get(); + if (!m_listScriptNames || !m_listScriptParameters) + { + ImGui::TextDisabled("No script library loaded"); + return; + } const char* previewValue = scriptComponent.name.empty() ? "none" : scriptComponent.name.c_str(); if (ImGui::BeginCombo("Script##ScriptComponent_name", previewValue)) { - assert(m_listScriptNames); for (const std::string& scriptName : m_listScriptNames()) { const bool isSelected = scriptComponent.name == scriptName; @@ -104,7 +108,6 @@ void EntityInspectorPanel::componentEditWidget() { scriptComponent.name = scriptName; scriptComponent.parameters.clear(); - assert(m_listScriptParameters); for (const GE::ScriptParameterDescriptor& parameter : m_listScriptParameters(scriptName)) { auto [parameterIt, inserted] = scriptComponent.parameters.try_emplace(parameter.name, parameter.defaultValue); @@ -150,8 +153,8 @@ void EntityInspectorPanel::componentEditWidget() EntityInspectorPanel::EntityInspectorPanel( const GE::Entity& entity, - std::function()> listScriptNames, - std::function(const std::string&)> listScriptParameters + GE::ListScriptNamesFn listScriptNames, + GE::ListScriptParametersFn listScriptParameters ) : m_entity(entity) , m_listScriptNames(std::move(listScriptNames)) diff --git a/editor/UI/EntityInspectorPanel.hpp b/editor/UI/EntityInspectorPanel.hpp index 36cf109..f32a951 100644 --- a/editor/UI/EntityInspectorPanel.hpp +++ b/editor/UI/EntityInspectorPanel.hpp @@ -11,10 +11,9 @@ #define ENTITYINSPECTORPANEL_HPP #include -#include +#include + #include -#include -#include namespace GE_Editor { @@ -28,8 +27,8 @@ class EntityInspectorPanel EntityInspectorPanel( const GE::Entity& entity, - std::function()> listScriptNames, - std::function(const std::string&)> listScriptParameters + GE::ListScriptNamesFn listScriptNames, + GE::ListScriptParametersFn listScriptParameters ); EntityInspectorPanel& onEntityDelete(std::function&& f) { return m_onEntityDelete = std::move(f), *this; } @@ -44,8 +43,8 @@ class EntityInspectorPanel void addComponentPopUp(); GE::Entity m_entity; - std::function()> m_listScriptNames; - std::function(const std::string&)> m_listScriptParameters; + GE::ListScriptNamesFn m_listScriptNames; + GE::ListScriptParametersFn m_listScriptParameters; std::function m_onEntityDelete; diff --git a/include/Game-Engine/Game.hpp b/include/Game-Engine/Game.hpp index d58917e..118399b 100644 --- a/include/Game-Engine/Game.hpp +++ b/include/Game-Engine/Game.hpp @@ -13,13 +13,10 @@ #include "Game-Engine/InputContext.hpp" #include "Game-Engine/Scene.hpp" #include "Game-Engine/AssetManager.hpp" -#include "Game-Engine/Script.hpp" +#include "Game-Engine/ScriptLibraryManager.hpp" #include -#include #include -#include -#include namespace GE { @@ -41,8 +38,8 @@ class GE_API Game Game( AssetManager* assetManager, - std::function(const std::string&)> makeScriptInstance, - std::function(const std::string&)> listScriptParameters, + MakeScriptInstanceFn makeScriptInstance, + ListScriptParametersFn listScriptParameters, const Descriptor& descriptor ); @@ -54,8 +51,8 @@ class GE_API Game ~Game(); private: - std::function(const std::string&)> m_makeScriptInstance; - std::function(const std::string&)> m_listScriptParameters; + MakeScriptInstanceFn m_makeScriptInstance; + ListScriptParametersFn m_listScriptParameters; std::map m_scenes; Scene* m_activeScene = nullptr; InputContext m_inputContext; diff --git a/include/Game-Engine/Script.hpp b/include/Game-Engine/Script.hpp index 15903f4..f310adc 100644 --- a/include/Game-Engine/Script.hpp +++ b/include/Game-Engine/Script.hpp @@ -71,10 +71,6 @@ class GE_API Script template concept ScriptClass = std::derived_from; -using MakeScriptInstanceFn = Script* (*)(const char*); -using ListScriptNamesFn = void (*)(const char***, unsigned long*); -using ListScriptParametersFn = void (*)(const char*, const ScriptParameterDescriptor**, unsigned long*); - class GE_API ScriptRegistry { public: diff --git a/include/Game-Engine/ScriptLibraryManager.hpp b/include/Game-Engine/ScriptLibraryManager.hpp new file mode 100644 index 0000000..3ed89d5 --- /dev/null +++ b/include/Game-Engine/ScriptLibraryManager.hpp @@ -0,0 +1,60 @@ +/* + * --------------------------------------------------- + * ScriptLibraryManager.hpp + * + * Author: Thomas Choquet + * --------------------------------------------------- + */ + +#ifndef SCRIPT_LIBRARY_MANAGER_HPP +#define SCRIPT_LIBRARY_MANAGER_HPP + +#include "Game-Engine/Export.hpp" +#include "Game-Engine/Script.hpp" + +#include +#include +#include +#include +#include + +namespace GE +{ + + +using ListScriptNamesFn = std::function()>; +using ListScriptParametersFn = std::function(const std::string&)>; +using MakeScriptInstanceFn = std::function(const std::string&)>; + +class GE_API ScriptLibraryManager +{ +public: + + ScriptLibraryManager(const std::filesystem::path& path); + ScriptLibraryManager(const ScriptLibraryManager&) = delete; + ScriptLibraryManager(ScriptLibraryManager&&) = default; + + ListScriptNamesFn listScriptNamesFunction() const; + ListScriptParametersFn listScriptParametersFunction() const; + MakeScriptInstanceFn makeScriptInstanceFunction() const; + + ~ScriptLibraryManager(); + + ScriptLibraryManager& operator=(const ScriptLibraryManager&) = delete; + ScriptLibraryManager& operator=(ScriptLibraryManager&&) = default; + +private: + using MakeScriptInstanceSym = Script* (*)(const char*); + using ListScriptNamesSym = void (*)(const char***, unsigned long*); + using ListScriptParametersSym = void (*)(const char*, const ScriptParameterDescriptor**, unsigned long*); + + std::shared_ptr m_libraryHandle; + + ListScriptNamesSym m_listScriptNames = nullptr; + ListScriptParametersSym m_listScriptParameters = nullptr; + MakeScriptInstanceSym m_makeScriptInstance = nullptr; +}; + +} // namespace GE + +#endif // SCRIPT_LIBRARY_MANAGER_HPP diff --git a/src/Game.cpp b/src/Game.cpp index feea4ad..55a33e2 100644 --- a/src/Game.cpp +++ b/src/Game.cpp @@ -7,12 +7,15 @@ */ #include "Game-Engine/Game.hpp" +#include "Game-Engine/ScriptLibraryManager.hpp" #include "Game-Engine/ECSView.hpp" #include "Game-Engine/Scene.hpp" #include "Game-Engine/AssetManager.hpp" +#include #include #include +#include #include namespace GE @@ -24,8 +27,8 @@ namespace void setupScene( Game& game, Scene& scene, - const std::function(const std::string&)>& makeScriptInstance, - const std::function(const std::string&)>& listScriptParameters + const MakeScriptInstanceFn& makeScriptInstance, + const ListScriptParametersFn& listScriptParameters ) { scene.load(); @@ -33,8 +36,8 @@ void setupScene( | ECSView() | std::views::transform([&](auto id) { return Entity{ &scene.ecsWorld(), id }; })) { - assert(makeScriptInstance); - assert(listScriptParameters); + if (!makeScriptInstance || !listScriptParameters) + throw std::runtime_error(std::format("unable to create script '{}' without a loaded script library", entity.get().name)); ScriptComponent& scriptComponent = entity.get(); scriptComponent.instance = makeScriptInstance(scriptComponent.name); assert(scriptComponent.instance); @@ -65,8 +68,8 @@ void tearDownScene(Game& game, Scene& scene) Game::Game( AssetManager* assetManager, - std::function(const std::string&)> makeScriptInstance, - std::function(const std::string&)> listScriptParameters, + MakeScriptInstanceFn makeScriptInstance, + ListScriptParametersFn listScriptParameters, const Descriptor& descriptor ) : m_makeScriptInstance(std::move(makeScriptInstance)) diff --git a/src/ScriptLibraryManager.cpp b/src/ScriptLibraryManager.cpp new file mode 100644 index 0000000..4e342b5 --- /dev/null +++ b/src/ScriptLibraryManager.cpp @@ -0,0 +1,99 @@ +/* + * --------------------------------------------------- + * ScriptLibraryManager.cpp + * + * Author: Thomas Choquet + * --------------------------------------------------- + */ + +#include "Game-Engine/ScriptLibraryManager.hpp" + +#include + +#include +#include +namespace GE +{ + +ScriptLibraryManager::ScriptLibraryManager(const std::filesystem::path& path) +{ + DlHandle handle = dlLoad(path.string().c_str()); + if (handle == nullptr) + throw std::runtime_error(std::format("unable to load shared lib : {}", path.string())); + + m_libraryHandle = std::shared_ptr(handle, [](void* ptr){ + if (ptr != nullptr) + dlFree(ptr); + }); + m_listScriptNames = reinterpret_cast(getSym(handle, "listScriptNames")); + m_listScriptParameters = reinterpret_cast(getSym(handle, "listScriptParameters")); + m_makeScriptInstance = reinterpret_cast(getSym(handle, "makeScriptInstance")); + + if (m_listScriptNames == nullptr || m_listScriptParameters == nullptr || m_makeScriptInstance == nullptr) + throw std::runtime_error(std::format("unable to get symbols in lib : {}", path.string())); +} + +ListScriptNamesFn ScriptLibraryManager::listScriptNamesFunction() const +{ + return [libraryHandle = m_libraryHandle, listScriptNamesFn = m_listScriptNames]() -> std::vector { + (void)libraryHandle; + if (listScriptNamesFn == nullptr) + return {}; + + const char** names = nullptr; + unsigned long count = 0; + listScriptNamesFn(&names, &count); + + std::vector output; + output.reserve(count); + for (unsigned long i = 0; i < count; i++) + if (names[i] != nullptr) + output.emplace_back(names[i]); + return output; + }; +} + +ListScriptParametersFn ScriptLibraryManager::listScriptParametersFunction() const +{ + return [libraryHandle = m_libraryHandle, listScriptParametersFn = m_listScriptParameters](const std::string& scriptName) -> std::vector { + (void)libraryHandle; + if (listScriptParametersFn == nullptr) + return {}; + + const ScriptParameterDescriptor* parameters = nullptr; + unsigned long count = 0; + listScriptParametersFn(scriptName.c_str(), ¶meters, &count); + + std::vector output; + output.reserve(count); + for (unsigned long i = 0; i < count; i++) + output.emplace_back(parameters[i]); + return output; + }; +} + +MakeScriptInstanceFn ScriptLibraryManager::makeScriptInstanceFunction() const +{ + return [libraryHandle = m_libraryHandle, makeScriptInstanceFn = m_makeScriptInstance](const std::string& scriptName) -> std::shared_ptr