mirror of
https://github.com/R2Northstar/NorthstarLauncher
synced 2026-08-23 20:26:04 -04:00
234 lines
5.1 KiB
C++
234 lines
5.1 KiB
C++
#include "plugins.h"
|
|
#include "pluginmanager.h"
|
|
#include "squirrel/squirrel.h"
|
|
#include "util/wininfo.h"
|
|
#include "core/sourceinterface.h"
|
|
#include "logging/logging.h"
|
|
#include "dedicated/dedicated.h"
|
|
|
|
bool isValidSquirrelIdentifier(std::string s)
|
|
{
|
|
if (!s.size())
|
|
return false; // identifiers can't be empty
|
|
if (s[0] <= 57)
|
|
return false; // identifier can't start with a number
|
|
for (char& c : s)
|
|
{
|
|
// only allow underscores, 0-9, A-Z and a-z
|
|
if ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || c == '_')
|
|
continue;
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
Plugin::Plugin(std::string path)
|
|
: m_location(path)
|
|
{
|
|
HMODULE pluginModule = GetModuleHandleA(path.c_str());
|
|
|
|
if (pluginModule)
|
|
{
|
|
// plugins may refuse to get unloaded for any reason so we need to prevent them getting loaded twice when reloading plugins
|
|
NS::log::PLUGINSYS->warn("Plugin has already been loaded");
|
|
return;
|
|
}
|
|
|
|
m_handle = LoadLibraryExA(path.c_str(), 0, LOAD_LIBRARY_SEARCH_USER_DIRS | LOAD_LIBRARY_SEARCH_DEFAULT_DIRS);
|
|
|
|
NS::log::PLUGINSYS->info("loaded plugin handle {}", static_cast<void*>(m_handle));
|
|
|
|
if (!m_handle)
|
|
{
|
|
NS::log::PLUGINSYS->error("Failed to load plugin '{}' (Error: {})", path, GetLastError());
|
|
return;
|
|
}
|
|
|
|
m_initData = {.pluginHandle = m_handle};
|
|
|
|
m_pCreateInterface = (CreateInterfaceFn)GetProcAddress(m_handle, "CreateInterface");
|
|
|
|
if (!m_pCreateInterface)
|
|
{
|
|
NS::log::PLUGINSYS->error("Plugin at '{}' does not expose CreateInterface()", path);
|
|
return;
|
|
}
|
|
|
|
m_pluginId = (IPluginId*)m_pCreateInterface(PLUGIN_ID_VERSION, 0);
|
|
|
|
if (!m_pluginId)
|
|
{
|
|
NS::log::PLUGINSYS->error("Could not load IPluginId interface of plugin at '{}'", path);
|
|
return;
|
|
}
|
|
|
|
const char* name = m_pluginId->GetString(PluginString::NAME);
|
|
const char* logName = m_pluginId->GetString(PluginString::LOG_NAME);
|
|
const char* dependencyName = m_pluginId->GetString(PluginString::DEPENDENCY_NAME);
|
|
int64_t context = m_pluginId->GetField(PluginField::CONTEXT);
|
|
|
|
m_runOnServer = context & PluginContext::DEDICATED;
|
|
m_runOnClient = context & PluginContext::CLIENT;
|
|
|
|
if (!name)
|
|
{
|
|
NS::log::PLUGINSYS->error("Could not load name of plugin at '{}'", path);
|
|
return;
|
|
}
|
|
|
|
if (!logName)
|
|
{
|
|
NS::log::PLUGINSYS->error("Could not load logName of plugin {}", name);
|
|
return;
|
|
}
|
|
|
|
if (!dependencyName)
|
|
{
|
|
NS::log::PLUGINSYS->error("Could not load dependencyName of plugin {}", name);
|
|
return;
|
|
}
|
|
|
|
m_name = std::string(name);
|
|
m_logName = std::string(logName);
|
|
m_dependencyName = std::string(dependencyName);
|
|
|
|
if (!isValidSquirrelIdentifier(m_dependencyName))
|
|
{
|
|
NS::log::PLUGINSYS->error("Dependency name \"{}\" of plugin {} is not valid", dependencyName, name);
|
|
return;
|
|
}
|
|
|
|
m_callbacks = (IPluginCallbacks*)m_pCreateInterface("PluginCallbacks001", 0);
|
|
|
|
if (!m_callbacks)
|
|
{
|
|
NS::log::PLUGINSYS->error("Could not create callback interface of plugin {}", name);
|
|
return;
|
|
}
|
|
|
|
m_logger = std::make_shared<ColoredLogger>(m_logName, NS::Colors::PLUGIN);
|
|
RegisterLogger(m_logger);
|
|
|
|
if (IsDedicatedServer() && !m_runOnServer)
|
|
{
|
|
NS::log::PLUGINSYS->info("Unloading {} because it's not supposed to run on dedicated servers", m_name);
|
|
return;
|
|
}
|
|
|
|
if (!IsDedicatedServer() && !m_runOnClient)
|
|
{
|
|
NS::log::PLUGINSYS->info("Unloading {} because it's only supposed to run on dedicated servers", m_name);
|
|
return;
|
|
}
|
|
|
|
m_valid = true;
|
|
}
|
|
|
|
bool Plugin::Unload() const
|
|
{
|
|
if (!m_handle)
|
|
return true;
|
|
|
|
if (IsValid())
|
|
{
|
|
bool unloaded = m_callbacks->Unload();
|
|
|
|
if (!unloaded)
|
|
return false;
|
|
}
|
|
|
|
if (!FreeLibrary(m_handle))
|
|
{
|
|
NS::log::PLUGINSYS->error("Failed to unload plugin at '{}'", m_location);
|
|
return false;
|
|
}
|
|
|
|
g_pPluginManager->RemovePlugin(m_handle);
|
|
return true;
|
|
}
|
|
|
|
void Plugin::Reload() const
|
|
{
|
|
bool unloaded = Unload();
|
|
|
|
if (!unloaded)
|
|
return;
|
|
|
|
g_pPluginManager->LoadPlugin(fs::path(m_location), true);
|
|
}
|
|
|
|
void Plugin::Log(spdlog::level::level_enum level, char* msg) const
|
|
{
|
|
m_logger->log(level, msg);
|
|
}
|
|
|
|
bool Plugin::IsValid() const
|
|
{
|
|
return m_valid && m_pCreateInterface && m_pluginId && m_callbacks && m_handle;
|
|
}
|
|
|
|
const std::string& Plugin::GetName() const
|
|
{
|
|
return m_name;
|
|
}
|
|
|
|
const std::string& Plugin::GetLogName() const
|
|
{
|
|
return m_logName;
|
|
}
|
|
|
|
const std::string& Plugin::GetDependencyName() const
|
|
{
|
|
return m_dependencyName;
|
|
}
|
|
|
|
const std::string& Plugin::GetLocation() const
|
|
{
|
|
return m_location;
|
|
}
|
|
|
|
bool Plugin::ShouldRunOnServer() const
|
|
{
|
|
return m_runOnServer;
|
|
}
|
|
|
|
bool Plugin::ShouldRunOnClient() const
|
|
{
|
|
return m_runOnClient;
|
|
}
|
|
|
|
void* Plugin::CreateInterface(const char* name, int* status) const
|
|
{
|
|
return m_pCreateInterface(name, status);
|
|
}
|
|
|
|
void Plugin::Init(bool reloaded) const
|
|
{
|
|
m_callbacks->Init(g_NorthstarModule, &m_initData, reloaded);
|
|
}
|
|
|
|
void Plugin::Finalize() const
|
|
{
|
|
m_callbacks->Finalize();
|
|
}
|
|
|
|
void Plugin::OnSqvmCreated(CSquirrelVM* sqvm) const
|
|
{
|
|
m_callbacks->OnSqvmCreated(sqvm);
|
|
}
|
|
|
|
void Plugin::OnSqvmDestroying(CSquirrelVM* sqvm) const
|
|
{
|
|
NS::log::PLUGINSYS->info("destroying sqvm {}", sqvm->vmContext);
|
|
m_callbacks->OnSqvmDestroying(sqvm);
|
|
}
|
|
|
|
void Plugin::OnLibraryLoaded(HMODULE module, const char* name) const
|
|
{
|
|
m_callbacks->OnLibraryLoaded(module, name);
|
|
}
|
|
|
|
void Plugin::RunFrame() const
|
|
{
|
|
m_callbacks->RunFrame();
|
|
}
|