og-northstar-launcher/primedev/plugins/plugins.cpp
2024-08-26 15:16:11 +02:00

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();
}