diff --git a/CMakeLists.txt b/CMakeLists.txt index a554ed02f6..3524638d35 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -32,6 +32,7 @@ option(ENABLE_THEORA "Support video capture to OGG Theora? (Default: on)" ON) option(ENABLE_TOOLS "Build different tools? (Default: off)" OFF) option(NATIVE_OSX_APP "Support native OSX paths read data from (Default: off)" OFF) option(FAST_MATH "Build with unsafe fast-math compiller option (Default: off)" OFF) +option(ENABLE_PLUGINS "Build with experimental plugins support (Default: off)" OFF) if(NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Build type." FORCE) @@ -285,6 +286,10 @@ if(OCTREE_DEBUG) add_definitions(-DOCTREE_DEBUG) endif() +if(ENABLE_PLUGINS) + add_definitions(-DENABLE_PLUGINS) +endif() + include_directories("${CMAKE_SOURCE_DIR}/src" ${CMAKE_BINARY_DIR}) # configure a header file to pass some of the CMake settings diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 2be6428497..e74607f05c 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -17,6 +17,7 @@ add_subdirectory(celscript) # These compiled objects are merged with the celengine library add_subdirectory(celephem) add_subdirectory(celcompat) +add_subdirectory(celplugin) if (ENABLE_TOOLS) add_subdirectory(tools) diff --git a/src/celengine/parseobject.cpp b/src/celengine/parseobject.cpp index 2ce335047a..a341a660a3 100644 --- a/src/celengine/parseobject.cpp +++ b/src/celengine/parseobject.cpp @@ -22,8 +22,13 @@ #include #include #endif -#include -#include +#if defined(ENABLE_PLUGINS) +#include +#include +#else +#include +#include +#endif #include #include #include @@ -630,12 +635,15 @@ CreateSpiceRotation(Hash* rotationData, } #endif - +#if defined(ENABLE_PLUGINS) +static CachingOrbit* +#else static ScriptedOrbit* +#endif CreateScriptedOrbit(Hash* orbitData, const fs::path& path) { -#if !defined(CELX) +#if !defined(CELX) && !defined(ENABLE_PLUGINS) clog << "ScriptedOrbit not usable without scripting support.\n"; return nullptr; #else @@ -648,6 +656,12 @@ CreateScriptedOrbit(Hash* orbitData, return nullptr; } +#if defined(ENABLE_PLUGINS) + // If language name is missing assume Lua to be backward compatible + string language("LUA"); + orbitData->getString("Language", language); +#endif + // Module name is optional string moduleName; orbitData->getString("Module", moduleName); @@ -655,12 +669,29 @@ CreateScriptedOrbit(Hash* orbitData, Value* pathValue = new Value(path.string()); orbitData->addValue("AddonPath", *pathValue); +#if defined(ENABLE_PLUGINS) + auto pluginManager = celestia::plugin::GetPluginManager(); + if (pluginManager == nullptr) + { + fmt::print(cerr, "Error: PluginManager is not initialized!\n"); + return nullptr; + } + auto plugin = pluginManager->getScriptPlugin(language); + if (plugin == nullptr) + { + fmt::print(cerr, "Support for language {} is missing\n", language); + return nullptr; + } + + auto *scriptedOrbit = plugin->createScriptedOrbit(moduleName, funcName, orbitData); +#else ScriptedOrbit* scriptedOrbit = new ScriptedOrbit(); if (!scriptedOrbit->initialize(moduleName, funcName, orbitData)) { delete scriptedOrbit; scriptedOrbit = nullptr; } +#endif return scriptedOrbit; #endif @@ -1001,12 +1032,15 @@ CreatePrecessingRotationModel(Hash* rotationData, } } - +#if defined(ENABLE_PLUGINS) +static RotationModel* +#else static ScriptedRotation* +#endif CreateScriptedRotation(Hash* rotationData, const fs::path& path) { -#if !defined(CELX) +#if !defined(CELX) && !defined(ENABLE_PLUGINS) clog << "ScriptedRotation not usable without scripting support.\n"; return nullptr; #else @@ -1019,6 +1053,12 @@ CreateScriptedRotation(Hash* rotationData, return nullptr; } +#if defined(ENABLE_PLUGINS) + // If language name is missing assume Lua to be backward compatible + string language("LUA"); + rotationData->getString("LUA", language); +#endif + // Module name is optional string moduleName; rotationData->getString("Module", moduleName); @@ -1026,12 +1066,29 @@ CreateScriptedRotation(Hash* rotationData, Value* pathValue = new Value(path.string()); rotationData->addValue("AddonPath", *pathValue); +#if defined(ENABLE_PLUGINS) + auto pluginManager = celestia::plugin::GetPluginManager(); + if (pluginManager == nullptr) + { + fmt::print(cerr, "Error: PluginManager is not initialized!\n"); + return nullptr; + } + auto plugin = pluginManager->getScriptPlugin(language); + if (plugin == nullptr) + { + fmt::print(cerr, "Support for language {} is missing\n", language); + return nullptr; + } + + auto *scriptedRotation = plugin->createScriptedRotation(moduleName, funcName, rotationData); +#else ScriptedRotation* scriptedRotation = new ScriptedRotation(); if (!scriptedRotation->initialize(moduleName, funcName, rotationData)) { delete scriptedRotation; scriptedRotation = nullptr; } +#endif return scriptedRotation; #endif diff --git a/src/celephem/CMakeLists.txt b/src/celephem/CMakeLists.txt index 315867901b..d36927d43b 100644 --- a/src/celephem/CMakeLists.txt +++ b/src/celephem/CMakeLists.txt @@ -32,18 +32,6 @@ if(ENABLE_SPICE) ) endif() - -if(ENABLE_CELX) - list(APPEND CELEPHEM_SOURCES - scriptobject.cpp - scriptobject.h - scriptorbit.cpp - scriptorbit.h - scriptrotation.cpp - scriptrotation.h - ) -endif() - # These object files are merged in the celegine library add_library(celephem OBJECT ${CELEPHEM_SOURCES}) diff --git a/src/celestia/CMakeLists.txt b/src/celestia/CMakeLists.txt index ad0f1e2b81..773d78af2f 100644 --- a/src/celestia/CMakeLists.txt +++ b/src/celestia/CMakeLists.txt @@ -37,7 +37,7 @@ endif() add_library(celestia STATIC ${CELESTIA_SOURCES} $ $ - $) + $) #[[ add_library(celestia SHARED ${CELESTIA_SOURCES}) diff --git a/src/celestia/celestiacore.cpp b/src/celestia/celestiacore.cpp index 6238c0c0d9..58d3983106 100644 --- a/src/celestia/celestiacore.cpp +++ b/src/celestia/celestiacore.cpp @@ -6,7 +6,7 @@ // keyboard events. CelestiaCore then turns those events into calls // to Renderer and Simulation. // -// Copyright (C) 2001-2009, the Celestia Development Team +// Copyright (C) 2001-2019, the Celestia Development Team // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -54,11 +54,8 @@ #include #include #include - -#ifdef CELX -#include -#endif - +#include +#include #include "imagecapture.h" // TODO: proper gettext @@ -69,6 +66,7 @@ using namespace Eigen; using namespace std; using namespace celmath; using namespace celestia::scripts; +using namespace celestia::plugin; static const int DragThreshold = 3; @@ -146,9 +144,13 @@ CelestiaCore::CelestiaCore() : renderer(new Renderer()), timer(new Timer()), m_legacyPlugin(make_unique(this)), +#if defined(CELX) && !defined(ENABLE_PLUGINS) m_luaPlugin(make_unique(this)), - m_scriptMaps(make_shared()) +#endif + m_scriptMaps(make_shared()), + m_pluginManager(make_unique(this)) { + SetPluginManager(m_pluginManager.get()); for (int i = 0; i < KeyCount; i++) { @@ -165,6 +167,8 @@ CelestiaCore::CelestiaCore() : CelestiaCore::~CelestiaCore() { + SetPluginManager(nullptr); + if (movieCapture != nullptr) recordEnd(); @@ -300,7 +304,7 @@ void CelestiaCore::runScript(const fs::path& filename) if (m_script != nullptr) scriptState = sim->getPauseState() ? ScriptPaused : ScriptRunning; } -#ifdef CELX +#if defined(CELX) && !defined(ENABLE_PLUGINS) else if (m_luaPlugin->isOurFile(localeFilename)) { m_script = m_luaPlugin->loadScript(localeFilename); @@ -3601,7 +3605,7 @@ bool CelestiaCore::initSimulation(const fs::path& configFileName, } } -#ifdef CELX +#if defined(CELX) && !defined(ENABLE_PLUGINS) initLuaHook(progressNotifier); #endif @@ -4397,7 +4401,7 @@ bool CelestiaCore::referenceMarkEnabled(const string& refMark, Selection sel) co } -#ifdef CELX +#if defined(CELX) && !defined(ENABLE_PLUGINS) bool CelestiaCore::initLuaHook(ProgressNotifier* progressNotifier) { return CreateLuaEnvironment(this, config, progressNotifier); diff --git a/src/celestia/celestiacore.h b/src/celestia/celestiacore.h index fa1307910c..50909299a1 100644 --- a/src/celestia/celestiacore.h +++ b/src/celestia/celestiacore.h @@ -39,7 +39,14 @@ class Url; // class CelestiaWatcher; class CelestiaCore; - +namespace celestia +{ +namespace plugin +{ +class PluginManager; +class Plugin; +} +} // class astro::Date; typedef Watcher CelestiaWatcher; @@ -343,7 +350,7 @@ class CelestiaCore // : public Watchable protected: bool readStars(const CelestiaConfig&, ProgressNotifier*); void renderOverlay(); -#ifdef CELX +#if defined(CELX) && !defined(ENABLE_PLUGINS) bool initLuaHook(ProgressNotifier*); #endif // CELX @@ -399,9 +406,13 @@ class CelestiaCore // : public Watchable std::unique_ptr m_script; std::unique_ptr m_scriptHook; std::unique_ptr m_legacyPlugin; +#if defined(CELX) && !defined(ENABLE_PLUGINS) std::unique_ptr m_luaPlugin; +#endif std::shared_ptr m_scriptMaps; + std::unique_ptr m_pluginManager; + enum ScriptState { ScriptCompleted, diff --git a/src/celplugin/CMakeLists.txt b/src/celplugin/CMakeLists.txt new file mode 100644 index 0000000000..a9bafb92ce --- /dev/null +++ b/src/celplugin/CMakeLists.txt @@ -0,0 +1,10 @@ +# These object files are merged in the celegine library +set(CELPLUGIN_SOURCES + plugin-common.h + plugin.cpp + plugin.h + pluginmanager.cpp + pluginmanager.h +) +add_library(celplugin OBJECT ${CELPLUGIN_SOURCES}) + diff --git a/src/celplugin/host.cpp b/src/celplugin/host.cpp new file mode 100644 index 0000000000..dbcba13fea --- /dev/null +++ b/src/celplugin/host.cpp @@ -0,0 +1,48 @@ +#define PUBLIC_GET_INFO +#include +#include "plugin.h" +#include "pluginmanager.h" +#include +#ifdef _WIN32 +#include // _getcwd +#else +#include // getcwd +#endif + +using namespace celestia::plugin; + +int main() +{ +#ifdef _WIN32 + wchar_t *cwd = _wgetcwd(nullptr, 0); + if (cwd == nullptr) cwd = L""; +#else + char *cwd = getcwd(nullptr, 0); + if (cwd == nullptr) cwd = ""; +#endif + + PluginManager pm; + pm.setSearchDirectory(cwd); + const Plugin* p = pm.loadByName("myplug"); + if (p == nullptr) + { + std::cout << "load failed\n"; + return 1; + } + + const PluginInfo *pi = p->getPluginInfo(); + fmt::printf("APIVersion = %hx, Type = %hu, ID = %p\n", pi->APIVersion, pi->Type, fmt::ptr(pi->ID)); + + if (p->getType() == Scripting) + { + fmt::printf("%s\n", p->getScriptLanguage()); + fmt::printf("%p %p\n", fmt::ptr(p), fmt::ptr(pm.getScriptPlugin("lUa"))); + } + else + { + void (*myfn)() = reinterpret_cast(pi->ID); + (*myfn)(); + } + + return 0; +} diff --git a/src/celplugin/myplug.cpp b/src/celplugin/myplug.cpp new file mode 100644 index 0000000000..3dccecf964 --- /dev/null +++ b/src/celplugin/myplug.cpp @@ -0,0 +1,15 @@ +#include +#include "plugin-common.h" + +using namespace celestia::plugin; + +static void myfn() +{ + std::cout << "hi from plugin!\n"; +} + +CELESTIA_PLUGIN_ENTRYPOINT() +{ + static PluginInfo pinf(Scripting, "LUA"); + return &pinf; +} diff --git a/src/celplugin/plugin-common.h b/src/celplugin/plugin-common.h new file mode 100644 index 0000000000..626279e293 --- /dev/null +++ b/src/celplugin/plugin-common.h @@ -0,0 +1,73 @@ +// plugin-common.h +// +// Copyright (C) 2019, Celestia Development Team +// +// Common definitions for application and module sides +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +#pragma once + +#include + +#define CELESTIA_PLUGIN_ENTRY_NAME get_celestia_plugin_info +#define CELESTIA_PLUGIN_ENTRY_NAME_STR "get_celestia_plugin_info" + +#ifdef _WIN32 +#define CELESTIA_PLUGIN_EXPORTABLE extern "C" __declspec(dllexport) +#else +#define CELESTIA_PLUGIN_EXPORTABLE extern "C" __attribute__ ((visibility ("default"))) +#endif + +#define CELESTIA_PLUGIN_ENTRYPOINT CELESTIA_PLUGIN_EXPORTABLE celestia::plugin::PluginInfo* CELESTIA_PLUGIN_ENTRY_NAME + +namespace celestia +{ +namespace plugin +{ + +constexpr const uint16_t CurrentAPIVersion = 0x0107; + +enum PluginType : uint32_t +{ + TestPlugin = 0, + Scripting = 0x0001, + Rendering = 0x0002, + AudioInput = 0x0010, + AudioOutput = 0x0020, + VideoInput = 0x0040, + VideoOutput = 0x0080, +}; + +#pragma pack(push,1) +struct PluginInfo +{ + PluginInfo() = delete; + ~PluginInfo() = default; + PluginInfo(const PluginInfo&) = default; + PluginInfo(PluginInfo&&) = default; + PluginInfo& operator=(const PluginInfo&) = default; + PluginInfo& operator=(PluginInfo&&) = default; + + PluginInfo(uint16_t _APIVersion, PluginType _Type, const char *_ID) : + APIVersion(_APIVersion), + Type(_Type), + ID(_ID) + {} + PluginInfo(PluginType _Type, const char *_ID) : + PluginInfo(CurrentAPIVersion, _Type, _ID) + {} + + uint16_t APIVersion; + uint16_t Reserved1; + PluginType Type; + uint32_t Reserved2; + const char *ID; +}; +#pragma pack(pop) + +} +} diff --git a/src/celplugin/plugin.cpp b/src/celplugin/plugin.cpp new file mode 100644 index 0000000000..16602634de --- /dev/null +++ b/src/celplugin/plugin.cpp @@ -0,0 +1,164 @@ +// plugin.cpp +// +// Copyright (C) 2019, Celestia Development Team +// +// Plugin application side implementation +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +#include +#include +#include +#include "plugin.h" +#ifndef _WIN32 +#include +#endif + +namespace celestia +{ +namespace plugin +{ + +Plugin::~Plugin() +{ + if (m_handle != nullptr) + { +#ifdef _WIN32 + FreeLibrary(m_handle); +#else + dlclose(m_handle); +#endif + } +} + +Plugin::Plugin(Plugin &&other) : + IScriptPlugin(std::forward(other)) +{ + m_handle = other.m_handle; + other.m_handle = nullptr; + m_appCore = other.m_appCore; + + m_pluginInfo = other.m_pluginInfo; + m_func = other.m_func; +} + +bool Plugin::isSupportedVersion() const +{ + return m_pluginInfo->APIVersion == 0x0107; +} + +Plugin* Plugin::load(CelestiaCore *appCore, const fs::path& path) +{ + Plugin p(appCore); + +#ifdef _WIN32 + p.m_handle = ::LoadLibraryW(path.c_str()); + if (p.m_handle == nullptr) + { + fmt::print(std::cerr, "::LoadLibraryW({}) failed: {}\n", path); + return nullptr; + } +#else + p.m_handle = dlopen(path.c_str(), RTLD_NOW/*|RTLD_GLOBAL*/); + if (p.m_handle == nullptr) + { + fmt::print(std::cerr, "dlopen({}) failed: {}\n", path, dlerror()); + return nullptr; + } +#endif + + void *ptr = p.loadSym(CELESTIA_PLUGIN_ENTRY_NAME_STR); + if (ptr == nullptr) + return nullptr; + auto LoadPluginInfo = reinterpret_cast(ptr); + p.m_pluginInfo = (*LoadPluginInfo)(); + + if (p.m_pluginInfo == nullptr) + { + fmt::print(std::cerr, "plugin doesn't have PluginInfo\n"); + return nullptr; + } + + if (!p.isSupportedVersion()) + { + fmt::print(std::cerr, "unsupported plugin API version {:#06x}\n", p.m_pluginInfo->APIVersion); + return nullptr; + } + + switch (p.getType()) + { + case TestPlugin: + break; + case Scripting: + ptr = p.loadSym("CreateScriptEnvironment"); + if (ptr != nullptr) + p.m_func.createScriptEnvironment = reinterpret_cast(ptr); + break; + default: + fmt::print(std::cerr, "unknown plugin type {} ({})\n", p.getType(), path); + return nullptr; + } + + return new Plugin(std::move(p)); +} + +void* Plugin::loadSym(const char* fn) const +{ +#ifdef _WIN32 + return ::GetProcAddress(m_handle, fn); +#else + void *ptr = dlsym(m_handle, fn); + char *error = dlerror(); + if (error != nullptr) + fmt::print(std::cerr, "dlsym({}) failed: {}\n", fn, error); + return ptr; +#endif +} + +bool Plugin::createScriptEnvironment(CelestiaCore *appCore, const CelestiaConfig *config, ProgressNotifier *progressNotifier) const +{ + auto fn = m_func.createScriptEnvironment; + return fn == nullptr ? false : (*fn)(appCore, config, progressNotifier); +} + +IScript* Plugin::createScript(CelestiaCore *appCore) const +{ + auto fn = m_func.createScript; + return fn == nullptr ? nullptr : (*fn)(appCore); +} + +RotationModel* Plugin::createScriptedRotation(const std::string& moduleName, const std::string& funcName, Hash* parameters) const +{ + auto fn = m_func.createScriptedRotation; + return fn == nullptr ? nullptr : (*fn)(moduleName, funcName, parameters); +} + +CachingOrbit* Plugin::createScriptedOrbit(const std::string& moduleName, const std::string& funcName, Hash* parameters) const +{ + auto fn = m_func.createScriptedOrbit; + return fn == nullptr ? nullptr : (*fn)(moduleName, funcName, parameters); +} + +bool Plugin::isOurFile(const fs::path &filename) const +{ + auto fn = m_func.isOurFile; + return fn == nullptr ? false : (*fn)(filename); +} + +std::unique_ptr Plugin::loadScript(const fs::path &filename) +{ + auto fn = m_func.loadScript; + return fn == nullptr ? nullptr : (*fn)(filename); +} + +Renderer* Plugin::createRenderer() const +{ + auto fn = m_func.createRenderer; + return fn == nullptr ? nullptr : (*fn)(); +} + +} +} diff --git a/src/celplugin/plugin.h b/src/celplugin/plugin.h new file mode 100644 index 0000000000..fe09e6af65 --- /dev/null +++ b/src/celplugin/plugin.h @@ -0,0 +1,125 @@ +// plugin.h +// +// Copyright (C) 2019, Celestia Development Team +// +// Plugin class +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +#pragma once + +#include +#include +#include +#include // IScriptPlugin, IScript +#include // Hash +#ifdef _WIN32 +#include +#endif +#include "plugin-common.h" + +class CelestiaCore; +class CelestiaConfig; +class ProgressNotifier; +class Renderer; +class CachingOrbit; +class RotationModel; + +namespace celestia +{ +namespace plugin +{ + +using scripts::IScript; +using scripts::IScriptPlugin; + +class PluginManager; + +class Plugin : public IScriptPlugin +{ + public: + Plugin() = delete; + explicit Plugin(CelestiaCore *appCore) : + IScriptPlugin(appCore) + {} + ~Plugin(); + Plugin(const Plugin&) = delete; + Plugin(Plugin&&); + Plugin& operator=(const Plugin&) = delete; + Plugin& operator=(Plugin&&) = default; + + void* loadSym(const char*) const; + +#ifdef PUBLIC_GET_INFO + const PluginInfo* getPluginInfo() const { return m_pluginInfo; } +#endif + bool isSupportedVersion() const; + PluginType getType() const { return m_pluginInfo->Type; } + + static Plugin* load(CelestiaCore*, const fs::path&); + + const char* getScriptLanguage() const { return m_pluginInfo->ID; } + + // pointers to plugin functions + /// scripting support + typedef bool(CreateScriptEnvironmentFunc)(CelestiaCore*, const CelestiaConfig*, ProgressNotifier*); + typedef IScript*(CreateScriptFunc)(CelestiaCore*); + typedef RotationModel*(CreateScriptedRotationFunc)(const std::string&, const std::string&, Hash*); + typedef CachingOrbit*(CreateScriptedOrbitFunc)(const std::string&, const std::string&, Hash*); + typedef bool (IsOurFile)(const fs::path&); + typedef std::unique_ptr(LoadScript)(const fs::path&); + + /// renderer support + typedef Renderer*(CreateRendererFunc)(); + + + /// scripting support + bool createScriptEnvironment(CelestiaCore *appCore, const CelestiaConfig *config, ProgressNotifier *progressNotifier) const; + IScript* createScript(CelestiaCore *appCore) const; + RotationModel* createScriptedRotation(const std::string& moduleName, const std::string& funcName, Hash* parameters) const; + CachingOrbit* createScriptedOrbit(const std::string& moduleName, const std::string& funcName, Hash* parameters) const; + bool isOurFile(const fs::path&) const; + std::unique_ptr loadScript(const fs::path&); + + /// renderer support + Renderer* createRenderer() const; + + private: + typedef PluginInfo*(RegisterFunc)(); +#ifndef PUBLIC_GET_INFO + const PluginInfo* getPluginInfo() const { return m_pluginInfo; } +#endif + +#ifdef _WIN32 + HMODULE m_handle { nullptr }; +#else + void *m_handle { nullptr }; +#endif + CelestiaCore *m_appCore; + PluginInfo *m_pluginInfo; + + union + { + struct + { + CreateScriptEnvironmentFunc *createScriptEnvironment; + CreateScriptFunc *createScript; + CreateScriptedRotationFunc *createScriptedRotation; + CreateScriptedOrbitFunc *createScriptedOrbit; + IsOurFile *isOurFile; + LoadScript *loadScript; + }; + struct + { + CreateRendererFunc *createRenderer; + }; + } m_func; + + friend class PluginManager; +}; // Plugin + +} +} diff --git a/src/celplugin/pluginmanager.cpp b/src/celplugin/pluginmanager.cpp new file mode 100644 index 0000000000..fc609e9b93 --- /dev/null +++ b/src/celplugin/pluginmanager.cpp @@ -0,0 +1,79 @@ +// pluginmanager.cpp +// +// Copyright (C) 2019, Celestia Development Team +// +// Plugin Manager implementation +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +#include +#include +#ifndef _WIN32 +#include +#endif +#include +#include "plugin.h" +#include "pluginmanager.h" + +namespace celestia +{ +namespace plugin +{ + +PluginManager::~PluginManager() +{ + for (auto p : m_plugins) + delete p; +} + +void PluginManager::setSearchDirectory(const fs::path &directory) +{ + m_directory = directory; +} + +const fs::path& PluginManager::getSearchDirectory() const +{ + return m_directory; +} + +const Plugin* PluginManager::loadByPath(const fs::path &path) +{ + auto p = Plugin::load(m_appCore, path); + if (p != nullptr) + m_plugins.push_back(p); + return p; +} + +const Plugin* PluginManager::loadByName(const std::string &name) +{ +#if defined(_WIN32) + return loadByPath(m_directory / fmt::sprintf("%s.dll", name)); +#else + return loadByPath(m_directory / fmt::sprintf("lib%s.so", name)); +#endif +} + +const Plugin* PluginManager::getScriptPlugin(const std::string &lang) const +{ + auto cmp = [&lang](const Plugin* p){ return compareIgnoringCase(p->getScriptLanguage(), lang) == 0; }; + auto it = std::find_if(m_plugins.begin(), m_plugins.end(), cmp); + return it != m_plugins.end() ? *it : nullptr; +} + +static PluginManager* pluginManager = nullptr; + +PluginManager* GetPluginManager() +{ + return pluginManager; +} + +void SetPluginManager(PluginManager *pm) +{ + pluginManager = pm; +} + +} +} diff --git a/src/celplugin/pluginmanager.h b/src/celplugin/pluginmanager.h new file mode 100644 index 0000000000..4a3e960f65 --- /dev/null +++ b/src/celplugin/pluginmanager.h @@ -0,0 +1,61 @@ +// pluginmanager.h +// +// Copyright (C) 2019, Celestia Development Team +// +// Plugin Manager class +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +#pragma once + +#include +#include +#include + +class CelestiaCore; + +namespace celestia +{ +namespace plugin +{ + +class Plugin; + +// +// PluginManager is an owner of all plugins +// +class PluginManager +{ + public: + PluginManager() = delete; + explicit PluginManager(CelestiaCore *appCore) : + m_appCore(appCore) + {} + ~PluginManager(); + PluginManager(const PluginManager&) = delete; + PluginManager(PluginManager&&) = delete; + PluginManager& operator=(const PluginManager&) = delete; + PluginManager& operator=(PluginManager&&) = delete; + + const Plugin* loadByPath(const fs::path&); + const Plugin* loadByName(const std::string&); + + void setSearchDirectory(const fs::path&); + const fs::path& getSearchDirectory() const; + + const Plugin* getScriptPlugin(const std::string&) const; + + private: + std::vector m_plugins; + fs::path m_directory; + CelestiaCore *m_appCore; +}; // PluginManager + +PluginManager* GetPluginManager(); +void SetPluginManager(PluginManager*); + +} +} diff --git a/src/celscript/common/script.h b/src/celscript/common/script.h index c05f57ff63..384efd8738 100644 --- a/src/celscript/common/script.h +++ b/src/celscript/common/script.h @@ -37,7 +37,11 @@ class IScriptPlugin IScriptPlugin() = delete; IScriptPlugin(CelestiaCore *appcore) : m_appCore(appcore) {}; IScriptPlugin(const IScriptPlugin&) = delete; +#ifdef ENABLE_PLUGINS + IScriptPlugin(IScriptPlugin&&) = default; +#else IScriptPlugin(IScriptPlugin&&) = delete; +#endif IScriptPlugin& operator=(const IScriptPlugin&) = delete; IScriptPlugin& operator=(IScriptPlugin&&) = delete; virtual ~IScriptPlugin() = default; diff --git a/src/celscript/lua/CMakeLists.txt b/src/celscript/lua/CMakeLists.txt index 919824fd40..ab297ee39c 100644 --- a/src/celscript/lua/CMakeLists.txt +++ b/src/celscript/lua/CMakeLists.txt @@ -24,8 +24,19 @@ set(CELX_SOURCES celx_rotation.h celx_vector.cpp celx_vector.h + luaplugin.cpp luascript.cpp luascript.h + scriptobject.cpp + scriptobject.h + scriptorbit.cpp + scriptorbit.h + scriptrotation.cpp + scriptrotation.h ) -add_library(celluascript OBJECT ${CELX_SOURCES}) +add_library(celluascript MODULE ${CELX_SOURCES}) + +if(ENABLE_PLUGINS) + target_compile_options(celluascript PUBLIC -fvisibility=hidden PUBLIC -fvisibility-inlines-hidden) +endif() diff --git a/src/celscript/lua/luaplugin.cpp b/src/celscript/lua/luaplugin.cpp new file mode 100644 index 0000000000..4b504d6fa2 --- /dev/null +++ b/src/celscript/lua/luaplugin.cpp @@ -0,0 +1,48 @@ +// luaplugin.cpp +// +// Copyright (C) 2019, the Celestia Development Team +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +#include +#include +#include "luascript.h" + +class CelestiaCore; +class CelestiaConfig; +class ProgressNotifier; +class Hash; +class CachingOrbit; +class RotationModel; + +using namespace celestia::plugin; +using namespace celestia::scripts; + +CELESTIA_PLUGIN_ENTRYPOINT() +{ + static celestia::plugin::PluginInfo pinf(celestia::plugin::Scripting, "LUA"); + return &pinf; +} + +CELESTIA_PLUGIN_EXPORTABLE bool CreateScriptEnvironment(CelestiaCore *appCore, const CelestiaConfig *config, ProgressNotifier *progressNotifier) +{ + return celestia::scripts::CreateLuaEnvironment(appCore, config, progressNotifier); +} + +CELESTIA_PLUGIN_EXPORTABLE LuaScript* CreateScript(CelestiaCore *appCore) +{ + return nullptr; +} + +RotationModel* CreateScriptedRotation(const std::string& moduleName, const std::string& funcName, Hash* parameters) +{ + return nullptr; +} + +CachingOrbit* CreateScriptedOrbit(const std::string& moduleName, const std::string& funcName, Hash* parameters) +{ + return nullptr; +} diff --git a/src/celscript/lua/luascript.cpp b/src/celscript/lua/luascript.cpp index ee99dbd445..fdbe8806da 100644 --- a/src/celscript/lua/luascript.cpp +++ b/src/celscript/lua/luascript.cpp @@ -13,12 +13,12 @@ #include #include #include -#include #include #include #include #include "celx_internal.h" #include "luascript.h" +#include "scriptobject.h" using namespace std; @@ -74,6 +74,7 @@ bool LuaScript::tick(double dt) return m_celxScript->tick(dt); } +#ifndef ENABLE_PLUGINS bool LuaScriptPlugin::isOurFile(const fs::path &p) const { auto ext = p.extension(); @@ -108,6 +109,7 @@ unique_ptr LuaScriptPlugin::loadScript(const fs::path &path) return script; } +#endif bool LuaHook::call(const char *method) const { diff --git a/src/celscript/lua/luascript.h b/src/celscript/lua/luascript.h index 308d477d25..f61d4fbf4a 100644 --- a/src/celscript/lua/luascript.h +++ b/src/celscript/lua/luascript.h @@ -11,6 +11,7 @@ #include #include +#include "celx.h" class LuaState; class CelestiaConfig; @@ -43,6 +44,7 @@ class LuaScript : public IScript friend class LuaScriptPlugin; }; +#ifndef ENABLE_PLUGINS class LuaScriptPlugin : public IScriptPlugin { public: @@ -57,6 +59,7 @@ class LuaScriptPlugin : public IScriptPlugin bool isOurFile(const fs::path&) const override; std::unique_ptr loadScript(const fs::path&) override; }; +#endif class LuaHook : public IScriptHook { diff --git a/src/celephem/scriptobject.cpp b/src/celscript/lua/scriptobject.cpp similarity index 100% rename from src/celephem/scriptobject.cpp rename to src/celscript/lua/scriptobject.cpp diff --git a/src/celephem/scriptobject.h b/src/celscript/lua/scriptobject.h similarity index 100% rename from src/celephem/scriptobject.h rename to src/celscript/lua/scriptobject.h diff --git a/src/celephem/scriptorbit.cpp b/src/celscript/lua/scriptorbit.cpp similarity index 100% rename from src/celephem/scriptorbit.cpp rename to src/celscript/lua/scriptorbit.cpp diff --git a/src/celephem/scriptorbit.h b/src/celscript/lua/scriptorbit.h similarity index 97% rename from src/celephem/scriptorbit.h rename to src/celscript/lua/scriptorbit.h index 2e656eb9ab..9bc7824f40 100644 --- a/src/celephem/scriptorbit.h +++ b/src/celscript/lua/scriptorbit.h @@ -13,7 +13,7 @@ #define _CELENGINE_SCRIPTORBIT_H_ #include -#include "orbit.h" +#include struct lua_State; diff --git a/src/celephem/scriptrotation.cpp b/src/celscript/lua/scriptrotation.cpp similarity index 100% rename from src/celephem/scriptrotation.cpp rename to src/celscript/lua/scriptrotation.cpp diff --git a/src/celephem/scriptrotation.h b/src/celscript/lua/scriptrotation.h similarity index 97% rename from src/celephem/scriptrotation.h rename to src/celscript/lua/scriptrotation.h index aea449f07b..0a4c4f138b 100644 --- a/src/celephem/scriptrotation.h +++ b/src/celscript/lua/scriptrotation.h @@ -13,7 +13,7 @@ #define _CELENGINE_SCRIPTROTATION_H_ #include -#include "rotation.h" +#include struct lua_State;