From edf013952ca2d110f190dc8cd16e5529846656e4 Mon Sep 17 00:00:00 2001 From: uniboi <64006268+uniboi@users.noreply.github.com> Date: Sun, 4 Feb 2024 02:14:46 +0100 Subject: Plugin interfaces (plugins v4) (#615) Replaces the current plugin api with source interfaces. - backwards compatible - no more json in binaries (wtf) - does not rely on structs from third party libraries (wtf) - actually initializes variables - no more basically unused classes The launcher exposes almost everything required by plugins in interfaces that allow for backwards compatibility. The only thing that's passed to a plugin directly is the northstar dll HWND and a struct of data that's different for each plugin. --- primedev/plugins/pluginmanager.cpp | 184 +++++++++++++++++++++++++++++++++++++ 1 file changed, 184 insertions(+) create mode 100644 primedev/plugins/pluginmanager.cpp (limited to 'primedev/plugins/pluginmanager.cpp') diff --git a/primedev/plugins/pluginmanager.cpp b/primedev/plugins/pluginmanager.cpp new file mode 100644 index 00000000..718e6956 --- /dev/null +++ b/primedev/plugins/pluginmanager.cpp @@ -0,0 +1,184 @@ +#include "pluginmanager.h" + +#include +#include "plugins.h" +#include "config/profile.h" +#include "core/convar/concommand.h" + +PluginManager* g_pPluginManager; + +const std::vector& PluginManager::GetLoadedPlugins() const +{ + return this->plugins; +} + +const std::optional PluginManager::GetPlugin(HMODULE handle) const +{ + for (const Plugin& plugin : GetLoadedPlugins()) + if (plugin.m_handle == handle) + return plugin; + return std::nullopt; +} + +void PluginManager::LoadPlugin(fs::path path, bool reloaded) +{ + Plugin plugin = Plugin(path.string()); + + if (!plugin.IsValid()) + { + NS::log::PLUGINSYS->warn("Unloading invalid plugin '{}'", path.string()); + plugin.Unload(); + return; + } + + plugins.push_back(plugin); + plugin.Init(reloaded); +} + +inline void FindPlugins(fs::path pluginPath, std::vector& paths) +{ + // ensure dirs exist + if (!fs::exists(pluginPath) || !fs::is_directory(pluginPath)) + { + return; + } + + for (const fs::directory_entry& entry : fs::directory_iterator(pluginPath)) + { + if (fs::is_regular_file(entry) && entry.path().extension() == ".dll") + paths.emplace_back(entry.path()); + } +} + +bool PluginManager::LoadPlugins(bool reloaded) +{ + if (strstr(GetCommandLineA(), "-noplugins") != NULL) + { + NS::log::PLUGINSYS->warn("-noplugins detected; skipping loading plugins"); + return false; + } + + fs::create_directories(GetThunderstoreModFolderPath()); + + std::vector paths; + + pluginPath = GetNorthstarPrefix() + "\\plugins"; + + fs::path libPath = fs::absolute(pluginPath + "\\lib"); + if (fs::exists(libPath) && fs::is_directory(libPath)) + AddDllDirectory(libPath.wstring().c_str()); + + FindPlugins(pluginPath, paths); + + // Special case for Thunderstore mods dir + std::filesystem::directory_iterator thunderstoreModsDir = fs::directory_iterator(GetThunderstoreModFolderPath()); + // Set up regex for `AUTHOR-MOD-VERSION` pattern + std::regex pattern(R"(.*\\([a-zA-Z0-9_]+)-([a-zA-Z0-9_]+)-(\d+\.\d+\.\d+))"); + for (fs::directory_entry dir : thunderstoreModsDir) + { + fs::path pluginsDir = dir.path() / "plugins"; + // Use regex to match `AUTHOR-MOD-VERSION` pattern + if (!std::regex_match(dir.path().string(), pattern)) + { + spdlog::warn("The following directory did not match 'AUTHOR-MOD-VERSION': {}", dir.path().string()); + continue; // skip loading package that doesn't match + } + + fs::path libDir = fs::absolute(pluginsDir / "lib"); + if (fs::exists(libDir) && fs::is_directory(libDir)) + AddDllDirectory(libDir.wstring().c_str()); + + FindPlugins(pluginsDir, paths); + } + + if (paths.empty()) + { + NS::log::PLUGINSYS->warn("Could not find any plugins. Skipped loading plugins"); + return false; + } + + for (fs::path path : paths) + { + LoadPlugin(path, reloaded); + } + + InformAllPluginsInitialized(); + + return true; +} + +void PluginManager::ReloadPlugins() +{ + for (const Plugin& plugin : this->GetLoadedPlugins()) + { + plugin.Unload(); + } + + this->plugins.clear(); + this->LoadPlugins(true); +} + +void PluginManager::RemovePlugin(HMODULE handle) +{ + for (size_t i = 0; i < plugins.size(); i++) + { + Plugin* plugin = &plugins[i]; + if (plugin->m_handle == handle) + { + plugins.erase(plugins.begin() + i); + return; + } + } +} + +void PluginManager::InformAllPluginsInitialized() const +{ + for (const Plugin& plugin : GetLoadedPlugins()) + { + plugin.Finalize(); + } +} + +void PluginManager::InformSqvmCreated(CSquirrelVM* sqvm) const +{ + for (const Plugin& plugin : GetLoadedPlugins()) + { + plugin.OnSqvmCreated(sqvm); + } +} + +void PluginManager::InformSqvmDestroying(CSquirrelVM* sqvm) const +{ + for (const Plugin& plugin : GetLoadedPlugins()) + { + plugin.OnSqvmDestroying(sqvm); + } +} + +void PluginManager::InformDllLoad(HMODULE module, fs::path path) const +{ + std::string fn = path.filename().string(); // without this the string gets freed immediately lmao + const char* filename = fn.c_str(); + for (const Plugin& plugin : GetLoadedPlugins()) + { + plugin.OnLibraryLoaded(module, filename); + } +} + +void PluginManager::RunFrame() const +{ + for (const Plugin& plugin : GetLoadedPlugins()) + { + plugin.RunFrame(); + } +} + +void ConCommand_reload_plugins(const CCommand& args) +{ + g_pPluginManager->ReloadPlugins(); +} + +ON_DLL_LOAD_RELIESON("engine.dll", PluginManager, ConCommand, (CModule module)) +{ + RegisterConCommand("reload_plugins", ConCommand_reload_plugins, "reloads plugins", FCVAR_NONE); +} -- cgit v1.2.3