mirror of
https://github.com/quizhizhe/LiteLoaderBDS-1.16.40.git
synced 2025-06-06 12:03:39 +00:00
420 lines
14 KiB
C++
420 lines
14 KiB
C++
#include "Main/PluginManager.h"
|
|
#include <LLAPI.h>
|
|
#include <LoggerAPI.h>
|
|
#include <EventAPI.h>
|
|
#include <ScheduleAPI.h>
|
|
#include <Utils/StringHelper.h>
|
|
#include <Utils/WinHelper.h>
|
|
#include <Utils/STLHelper.h>
|
|
#include <I18nAPI.h>
|
|
#include <MC/Level.hpp>
|
|
#include <MC/Player.hpp>
|
|
#include <windows.h>
|
|
#include <string>
|
|
#include <unordered_map>
|
|
#include "../ScriptEngine/Main/Configs.h"
|
|
using namespace std;
|
|
|
|
extern Logger logger;
|
|
std::unordered_map<std::string, LL::Plugin> plugins;
|
|
|
|
bool LL::PluginManager::registerPlugin(HMODULE handle, std::string name, std::string desc, LL::Version version,
|
|
std::map<std::string, std::string> others) {
|
|
if (handle != nullptr) // DLL Plugin
|
|
{
|
|
if (getPlugin(handle) != nullptr) {
|
|
erase_if(plugins, [&handle](auto& data) { // Allow plugins to overwrite their own plugin registory
|
|
return data.second.handle == handle;
|
|
});
|
|
} else if (getPlugin(name) != nullptr) {
|
|
return false; // Reject overwriting other's data
|
|
}
|
|
}
|
|
|
|
LL::Plugin plugin{name, desc, version, others};
|
|
plugin.handle = handle;
|
|
try {
|
|
plugin.type = others.at("PluginType") == "Script Plugin" ? Plugin::PluginType::ScriptPlugin : Plugin::PluginType::DllPlugin;
|
|
others.erase("PluginType");
|
|
} catch (...) {
|
|
plugin.type = handle ? Plugin::PluginType::DllPlugin : Plugin::PluginType::ScriptPlugin;
|
|
}
|
|
|
|
try {
|
|
plugin.filePath = UTF82String(filesystem::path(str2wstr(others.at("PluginFilePath"))).lexically_normal().u8string());
|
|
others.erase("PluginFilePath");
|
|
} catch (...) {
|
|
if (handle)
|
|
plugin.filePath = GetModulePath(handle);
|
|
}
|
|
|
|
plugins.emplace(name, plugin);
|
|
return true;
|
|
}
|
|
|
|
bool LL::PluginManager::unRegisterPlugin(std::string name) {
|
|
auto plugin = getPlugin(name);
|
|
if (plugin == nullptr) {
|
|
return false;
|
|
} else {
|
|
plugins.erase(plugin->name);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// Helper
|
|
LL::Plugin* GetPlugin_Raw(std::string name, bool includeScriptPlugin) {
|
|
for (auto& it : plugins) {
|
|
if (it.second.name == name || UTF82String(filesystem::path(str2wstr(it.second.filePath)).filename().u8string()) == name) {
|
|
if (!includeScriptPlugin && it.second.type == LL::Plugin::PluginType::ScriptPlugin)
|
|
continue;
|
|
return &it.second;
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
LL::Plugin* LL::PluginManager::getPlugin(std::string name, bool includeScriptPlugin) {
|
|
auto res = GetPlugin_Raw(name, includeScriptPlugin);
|
|
if (res)
|
|
return res;
|
|
/* issue #510
|
|
try
|
|
{
|
|
name = UTF82String(filesystem::path(str2wstr(name)).stem().u8string());
|
|
return GetPlugin_Raw(name, includeScriptPlugin);
|
|
}
|
|
catch(...)
|
|
{ }
|
|
*/
|
|
return nullptr;
|
|
}
|
|
|
|
LL::Plugin* LL::PluginManager::getPlugin(HMODULE handle) {
|
|
if (!handle)
|
|
return nullptr;
|
|
|
|
for (auto& it : plugins) {
|
|
if (it.second.handle == handle) {
|
|
return &it.second;
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
bool LL::PluginManager::hasPlugin(std::string name, bool includeScriptPlugin) {
|
|
return getPlugin(name, includeScriptPlugin) != nullptr;
|
|
}
|
|
|
|
std::unordered_map<std::string, LL::Plugin*> LL::PluginManager::getAllPlugins(bool includeScriptPlugin) {
|
|
std::unordered_map<std::string, LL::Plugin*> res;
|
|
for (auto& [k, v] : plugins) {
|
|
if (!includeScriptPlugin && v.type == Plugin::PluginType::ScriptPlugin)
|
|
continue;
|
|
res[k] = &v;
|
|
}
|
|
return res;
|
|
}
|
|
|
|
bool LL::PluginManager::loadPlugin(string pluginFilePath, bool outputResult, bool isHotLoad) {
|
|
// if (!LL::isDebugMode())
|
|
// return false;
|
|
try {
|
|
filesystem::path path(filesystem::path(str2wstr(pluginFilePath)).lexically_normal());
|
|
pluginFilePath = UTF82String(path.u8string());
|
|
|
|
string ext = UTF82String(path.extension().u8string());
|
|
if (ext != ".dll") {
|
|
if (filesystem::is_directory(path))
|
|
{
|
|
// Maybe uncompressed LLSE Script Plugin Package
|
|
Event::ScriptPluginManagerEvent ev;
|
|
ev.operation = Event::ScriptPluginManagerEvent::Operation::Load;
|
|
ev.target = pluginFilePath;
|
|
ev.pluginExtention = ext;
|
|
ev.pluginType = Event::ScriptPluginManagerEvent::PluginType::UncompressedPackage;
|
|
ev.call();
|
|
|
|
return ev.success;
|
|
}
|
|
else if(ext == ".llplugin")
|
|
{
|
|
// LLSE Script Plugin Package
|
|
Event::ScriptPluginManagerEvent ev;
|
|
ev.operation = Event::ScriptPluginManagerEvent::Operation::Load;
|
|
ev.target = pluginFilePath;
|
|
ev.pluginExtention = ext;
|
|
ev.pluginType = Event::ScriptPluginManagerEvent::PluginType::PluginPackage;
|
|
ev.call();
|
|
|
|
return ev.success;
|
|
}
|
|
auto validExts = LLSE_VALID_PLUGIN_EXTENSIONS;
|
|
if(!ext.empty() && std::find(validExts.begin(), validExts.end(), ext) != validExts.end())
|
|
{
|
|
// LLSE single-file script plugin
|
|
Event::ScriptPluginManagerEvent ev;
|
|
ev.operation = Event::ScriptPluginManagerEvent::Operation::Load;
|
|
ev.target = pluginFilePath;
|
|
ev.pluginExtention = ext;
|
|
ev.pluginType = Event::ScriptPluginManagerEvent::PluginType::SingleFile;
|
|
ev.call();
|
|
|
|
return ev.success;
|
|
}
|
|
else if (outputResult) {
|
|
logger.error(tr("ll.pluginManager.error.invalidFileType", ext, pluginFilePath));
|
|
}
|
|
return false;
|
|
}
|
|
|
|
if (pluginFilePath.find("/") == string::npos && pluginFilePath.find("\\") == string::npos && loadPlugin("plugins/" + pluginFilePath)) {
|
|
if (outputResult) {
|
|
logger.error(tr("ll.pluginManager.error.noValidPluginFound", pluginFilePath));
|
|
}
|
|
return false;
|
|
}
|
|
|
|
string pluginFileName = UTF82String(path.filename().u8string());
|
|
auto lib = LoadLibrary(str2wstr(pluginFilePath).c_str());
|
|
if (lib) {
|
|
if (getPlugin(lib) == nullptr) {
|
|
if (!RegisterPlugin(lib, pluginFileName, pluginFileName, LL::Version(1, 0, 0), {})) {
|
|
logger.error(tr("ll.pluginManager.error.failToRegisterPlugin", pluginFilePath));
|
|
if (getPlugin(pluginFileName)) {
|
|
logger.error(tr("ll.pluginManager.error.hasBeenRegistered", pluginFileName));
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
} else {
|
|
if (outputResult) {
|
|
logger.error(tr("ll.pluginManager.error.failToLoadPlugin", pluginFileName));
|
|
}
|
|
return false;
|
|
}
|
|
|
|
if (!getPlugin(lib))
|
|
return false;
|
|
// Call onPostInit
|
|
auto fn = GetProcAddress(getPlugin(lib)->handle, "onPostInit");
|
|
if (fn) {
|
|
try {
|
|
((void (*)())fn)();
|
|
return true;
|
|
} catch (std::exception& e) {
|
|
if (outputResult) {
|
|
logger.error("Plugin <{}> throws an std::exception in onPostInit", pluginFileName);
|
|
logger.error("Exception: ", TextEncoding::toUTF8(e.what()));
|
|
logger.error("Fail to init this plugin!");
|
|
}
|
|
return false;
|
|
} catch (...) {
|
|
if (outputResult) {
|
|
logger.error("Plugin <{}> throws an exception in onPostInit", pluginFileName);
|
|
logger.error("Fail to init this plugin!");
|
|
}
|
|
return false;
|
|
}
|
|
} else if (outputResult) {
|
|
logger.error("Fail to get the initialize point of the plugin {}!", pluginFileName);
|
|
return false;
|
|
}
|
|
|
|
// Hot Load
|
|
if (isHotLoad) {
|
|
callEventAtHotLoad(getPlugin(lib)->name);
|
|
}
|
|
return true;
|
|
} catch (const std::exception& e) {
|
|
if (outputResult) {
|
|
logger.error("Error occurred when loading plugin {}!", pluginFilePath);
|
|
logger.error(TextEncoding::toUTF8(e.what()));
|
|
}
|
|
return false;
|
|
} catch (...) {
|
|
if (outputResult) {
|
|
logger.error("Error occurred when loading plugin {}!", pluginFilePath);
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool LL::PluginManager::unloadPlugin(string pluginName, bool outputResult) {
|
|
// if (!LL::isDebugMode())
|
|
// return false;
|
|
try {
|
|
LL::Plugin* plugin = getPlugin(pluginName);
|
|
if (!plugin) {
|
|
if (outputResult) {
|
|
logger.error(tr("ll.pluginManager.error.pluginNotFound"));
|
|
}
|
|
return false;
|
|
}
|
|
|
|
if (plugin->type == Plugin::PluginType::ScriptPlugin) // Script Plugin
|
|
{
|
|
Event::ScriptPluginManagerEvent ev;
|
|
ev.operation = Event::ScriptPluginManagerEvent::Operation::Unload;
|
|
ev.target = pluginName;
|
|
ev.pluginExtention = UTF82String(filesystem::path(str2wstr(plugin->filePath)).extension().u8string());
|
|
ev.call();
|
|
|
|
return ev.success;
|
|
}
|
|
|
|
// Hot unload
|
|
// callEventAtHotUnload(pluginName);
|
|
// unRegisterPlugin(pluginName);
|
|
// removeAllEventListeners(pluginName);
|
|
|
|
logger.warn(tr("ll.pluginManager.warning.unloadDllNotFinished")); // TODO
|
|
return false;
|
|
} catch (const std::exception& e) {
|
|
if (outputResult) {
|
|
logger.error("Error occurred when unloading plugin {}!", pluginName);
|
|
logger.error(TextEncoding::toUTF8(e.what()));
|
|
}
|
|
return false;
|
|
} catch (...) {
|
|
if (outputResult) {
|
|
logger.error("Error occurred when unloading plugin {}!", pluginName);
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool LL::PluginManager::reloadPlugin(string pluginName, bool outputResult) {
|
|
// if (!LL::isDebugMode())
|
|
// return false;
|
|
try {
|
|
LL::Plugin* plugin = getPlugin(pluginName);
|
|
if (!plugin) {
|
|
if (outputResult) {
|
|
logger.error(tr("ll.pluginManager.error.pluginNotFound"));
|
|
}
|
|
return false;
|
|
}
|
|
|
|
if (plugin->type == Plugin::PluginType::ScriptPlugin) // Script Plugin
|
|
{
|
|
Event::ScriptPluginManagerEvent ev;
|
|
ev.operation = Event::ScriptPluginManagerEvent::Operation::Reload;
|
|
ev.target = pluginName;
|
|
ev.pluginExtention = UTF82String(filesystem::path(str2wstr(plugin->filePath)).extension().u8string());
|
|
ev.call();
|
|
return ev.success;
|
|
}
|
|
|
|
/*string path = plugin->filePath;
|
|
if (!unloadPlugin(pluginName))
|
|
return false;
|
|
return loadPlugin(path);*/
|
|
|
|
logger.warn(tr("ll.pluginManager.warning.reloadDllNotFinished")); // TODO
|
|
return false;
|
|
} catch (const std::exception& e) {
|
|
if (outputResult) {
|
|
logger.error("Error occurred when reloading plugin {}!", pluginName);
|
|
logger.error(TextEncoding::toUTF8(e.what()));
|
|
}
|
|
return false;
|
|
} catch (...) {
|
|
if (outputResult) {
|
|
logger.error("Error occurred when reloading plugin {}!", pluginName);
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
|
|
int LL::PluginManager::reloadAllPlugins(bool outputResult) {
|
|
// if (!LL::isDebugMode())
|
|
// return false;
|
|
try {
|
|
auto plugins = getAllPlugins(true);
|
|
int cnt = 0;
|
|
for (auto& [name, plugin] : plugins) {
|
|
if (plugin->type == Plugin::PluginType::ScriptPlugin) {
|
|
Event::ScriptPluginManagerEvent ev;
|
|
ev.operation = Event::ScriptPluginManagerEvent::Operation::Reload;
|
|
ev.target = plugin->name;
|
|
ev.pluginExtention = UTF82String(filesystem::path(str2wstr(plugin->filePath)).extension().u8string());
|
|
ev.call();
|
|
|
|
if (ev.success) {
|
|
++cnt;
|
|
}
|
|
} else {
|
|
if (reloadPlugin(name)) {
|
|
++cnt;
|
|
} else {
|
|
logger.error(tr("ll.pluginManager.reloadPlugin.fail", name));
|
|
}
|
|
}
|
|
}
|
|
return cnt;
|
|
} catch (const std::exception& e) {
|
|
if (outputResult) {
|
|
logger.error("Error occurred when reloading all plugins!");
|
|
logger.error(TextEncoding::toUTF8(e.what()));
|
|
}
|
|
return false;
|
|
} catch (...) {
|
|
if (outputResult) {
|
|
logger.error("Error occurred when reloading all plugins!");
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool LL::PluginManager::callEventAtHotLoad(std::string pluginName) {
|
|
// if (!LL::isDebugMode())
|
|
// return false;
|
|
Schedule::nextTick([pluginName]() {
|
|
Event::ServerStartedEvent().callToPlugin(pluginName); // ServerStartedEvent
|
|
});
|
|
|
|
Schedule::delay([pluginName]() {
|
|
auto players = Level::getAllPlayers();
|
|
for (auto& pl : players) // PlayerPreJoinEvent
|
|
{
|
|
Event::PlayerPreJoinEvent ev;
|
|
ev.mPlayer = pl;
|
|
ev.mXUID = pl->getXuid();
|
|
ev.mIP = pl->getIP();
|
|
ev.callToPlugin(pluginName);
|
|
}
|
|
for (auto& pl : players) // PlayerJoinEvent
|
|
{
|
|
Event::PlayerJoinEvent ev;
|
|
ev.mPlayer = pl;
|
|
ev.callToPlugin(pluginName);
|
|
}
|
|
},
|
|
20);
|
|
return true;
|
|
}
|
|
|
|
bool LL::PluginManager::callEventAtHotUnload(std::string pluginName) {
|
|
// if (!LL::isDebugMode())
|
|
// return false;
|
|
auto players = Level::getAllPlayers();
|
|
for (auto& pl : players) // PlayerLeftEvent
|
|
{
|
|
Event::PlayerLeftEvent ev;
|
|
ev.mPlayer = pl;
|
|
ev.mXUID = pl->getXuid();
|
|
ev.callToPlugin(pluginName);
|
|
}
|
|
|
|
Event::ServerStoppedEvent().callToPlugin(pluginName); // ServerStoppedEvent
|
|
return true;
|
|
}
|
|
|
|
// Helper
|
|
LIAPI bool RegisterPlugin(HMODULE handle, std::string name, std::string desc, LL::Version version,
|
|
std::map<std::string, std::string> others) {
|
|
others["PluginType"] = "DLL Plugin";
|
|
others["PluginFilePath"] = handle ? GetModulePath(handle) : name;
|
|
return LL::PluginManager::registerPlugin(handle, name, desc, version, others);
|
|
} |