1
0
Fork 0

Plugin reload <plugin_name> feature (#4942)

+ Add `reload <pluginname>`
* Fixes #365

Co-authored-by: Alexander Harkness <me@bearbin.net>
Co-authored-by: pwnOrbitals <c.de-claverie@pm.me>
Co-authored-by: Tiger Wang <ziwei.tiger@outlook.com>
This commit is contained in:
[IPSA] Chris de Claverie 2020-09-28 00:15:03 +02:00 committed by GitHub
parent 410d6c0045
commit 9a548b3b3e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 89 additions and 28 deletions

View File

@ -37,10 +37,10 @@ lkolbly
LogicParrot LogicParrot
Luksor Luksor
M10360 M10360
maxluchterhand1
marmot21 marmot21
Masy98 Masy98
mathiascode mathiascode
maxluchterhand1
MaxwellScroggs MaxwellScroggs
mborland mborland
mBornand mBornand
@ -53,6 +53,7 @@ nesco
NiLSPACE (formerly STR_Warrior) NiLSPACE (formerly STR_Warrior)
p-mcgowan p-mcgowan
pokechu22 pokechu22
pwnOrbitals
rs2k rs2k
SamJBarney SamJBarney
Schwertspize Schwertspize
@ -64,8 +65,8 @@ structinf (xdot)
sweetgiorni sweetgiorni
Sxw1212 Sxw1212
Taugeshtu Taugeshtu
tigerw (Tiger Wang)
theophriene theophriene
tigerw (Tiger Wang)
tonibm19 tonibm19
TooAngel TooAngel
UltraCoderRU UltraCoderRU

View File

@ -675,6 +675,17 @@ cPluginManager.AddHook(cPluginManager.HOOK_CHAT, OnChatMessage);
}, },
Notes = "Queues the specified plugin to be unloaded. To avoid deadlocks, the unloading happens in the main tick thread asynchronously.", Notes = "Queues the specified plugin to be unloaded. To avoid deadlocks, the unloading happens in the main tick thread asynchronously.",
}, },
ReloadPlugin =
{
Params =
{
{
Name = "PluginName",
Type = "string",
},
},
Notes = "Queues the specified plugin to be reloaded. To avoid deadlocks, the reloading happens in the main tick thread asynchronously.",
}
}, },
Constants = Constants =
{ {

View File

@ -45,7 +45,7 @@ void cPlugin::Unload(void)
AString cPlugin::GetLocalFolder(void) const AString cPlugin::GetLocalFolder(void) const
{ {
return std::string("Plugins/") + m_FolderName; return "Plugins" + cFile::GetPathSeparator() + m_FolderName;
} }

View File

@ -165,35 +165,54 @@ void cPluginManager::InsertDefaultPlugins(cSettingsRepositoryInterface & a_Setti
void cPluginManager::Tick(float a_Dt) void cPluginManager::Tick(float a_Dt)
{ {
// Unload plugins that have been scheduled for unloading: decltype(m_PluginsNeedAction) PluginsNeedAction;
AStringVector PluginsToUnload;
{ {
cCSLock Lock(m_CSPluginsToUnload); cCSLock Lock(m_CSPluginsNeedAction);
std::swap(m_PluginsToUnload, PluginsToUnload); std::swap(m_PluginsNeedAction, PluginsNeedAction);
} }
for (auto & folder: PluginsToUnload)
// Process deferred actions:
for (auto & CurrentPlugin : PluginsNeedAction)
{ {
bool HasUnloaded = false; auto & Action = CurrentPlugin.first;
bool HasFound = false; auto & Folder = CurrentPlugin.second;
for (auto & plugin: m_Plugins)
bool WasLoaded = false;
bool WasFound = false;
for (auto & Plugin: m_Plugins)
{ {
if (plugin->GetFolderName() == folder) if (Plugin->GetFolderName() == Folder)
{ {
HasFound = true; WasFound = true;
if (plugin->IsLoaded()) if (Plugin->IsLoaded())
{ {
plugin->Unload(); switch (Action)
HasUnloaded = true; {
case PluginAction::Reload :
{
// Reload plugins by unloading, then loading:
Plugin->Unload();
Plugin->Load();
break;
}
case PluginAction::Unload :
{
// Unload plugins that have been scheduled for unloading:
Plugin->Unload();
break;
}
}
WasLoaded = true;
} }
} }
} }
if (!HasFound) if (!WasFound)
{ {
LOG("Cannot unload plugin in folder \"%s\", there's no such plugin folder", folder.c_str()); LOG("Cannot act on plugin in folder \"%s\", there's no such plugin folder", Folder.c_str());
} }
else if (!HasUnloaded) else if (!WasLoaded)
{ {
LOG("Cannot unload plugin in folder \"%s\", it has not been loaded.", folder.c_str()); LOG("Cannot act on plugin in folder \"%s\", it has not been loaded.", Folder.c_str());
} }
} // for plugin - m_Plugins[] } // for plugin - m_Plugins[]
@ -1317,8 +1336,18 @@ void cPluginManager::UnloadPluginsNow()
void cPluginManager::UnloadPlugin(const AString & a_PluginFolder) void cPluginManager::UnloadPlugin(const AString & a_PluginFolder)
{ {
cCSLock Lock(m_CSPluginsToUnload); cCSLock Lock(m_CSPluginsNeedAction);
m_PluginsToUnload.push_back(a_PluginFolder); m_PluginsNeedAction.emplace_back(PluginAction::Unload, a_PluginFolder);
}
void cPluginManager::ReloadPlugin(const AString & a_PluginFolder)
{
cCSLock Lock(m_CSPluginsNeedAction);
m_PluginsNeedAction.emplace_back(PluginAction::Reload, a_PluginFolder);
} }

View File

@ -162,6 +162,14 @@ public:
} ; // tolua_export } ; // tolua_export
/** Defines the deferred actions needed for a plugin */
enum class PluginAction
{
Reload,
Unload
};
/** Used as a callback for enumerating bound commands */ /** Used as a callback for enumerating bound commands */
class cCommandEnumCallback class cCommandEnumCallback
{ {
@ -303,6 +311,10 @@ public:
Note that this function returns before the plugin is unloaded, to avoid deadlocks. */ Note that this function returns before the plugin is unloaded, to avoid deadlocks. */
void UnloadPlugin(const AString & a_PluginFolder); // tolua_export void UnloadPlugin(const AString & a_PluginFolder); // tolua_export
/** Queues the specified plugin to be reloaded in the next call to Tick().
Note that this function returns before the plugin is unloaded, to avoid deadlocks. */
void ReloadPlugin(const AString & a_PluginFolder); // tolua_export
/** Loads the plugin from the specified plugin folder. /** Loads the plugin from the specified plugin folder.
Returns true if the plugin was loaded successfully or was already loaded before, false otherwise. */ Returns true if the plugin was loaded successfully or was already loaded before, false otherwise. */
bool LoadPlugin(const AString & a_PluginFolder); // tolua_export bool LoadPlugin(const AString & a_PluginFolder); // tolua_export
@ -408,13 +420,13 @@ private:
typedef std::map<AString, cCommandReg> CommandMap; typedef std::map<AString, cCommandReg> CommandMap;
/** FolderNames of plugins that should be unloaded. /** FolderNames of plugins that need an action (unload, reload, ...).
The plugins will be unloaded within the next call to Tick(), to avoid multithreading issues. The plugins will be acted upon within the next call to Tick(), to avoid multithreading issues.
Protected against multithreaded access by m_CSPluginsToUnload. */ Protected against multithreaded access by m_CSPluginsNeedAction. */
AStringVector m_PluginsToUnload; std::vector<std::pair<PluginAction, AString>> m_PluginsNeedAction;
/** Protects m_PluginsToUnload against multithreaded access. */ /** Protects m_PluginsToUnload against multithreaded access. */
mutable cCriticalSection m_CSPluginsToUnload; mutable cCriticalSection m_CSPluginsNeedAction;
/** All plugins that have been found in the Plugins folder. */ /** All plugins that have been found in the Plugins folder. */
cPluginPtrs m_Plugins; cPluginPtrs m_Plugins;

View File

@ -463,7 +463,15 @@ void cServer::ExecuteConsoleCommand(const AString & a_Cmd, cCommandOutputCallbac
} }
else if (split[0] == "reload") else if (split[0] == "reload")
{ {
cPluginManager::Get()->ReloadPlugins(); if (split.size() > 1)
{
cPluginManager::Get()->ReloadPlugin(split[1]);
a_Output.Out("Plugin reload scheduled");
}
else
{
cPluginManager::Get()->ReloadPlugins();
}
a_Output.Finished(); a_Output.Finished();
return; return;
} }