/* * Copyright (C) 2006-2016 Jacek Sieka, arnetheduck on gmail point com * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /** * @page PluginAPI Plugin API Information * @section General General * * ADCH++ contains a rather powerful plugin API that can be used to create advanced * plugins that change or add to ADCH++'s behaviour. Most plugins will need * PluginManager.h, ClientManager.h and Client.h included to work, even though the * other header files are available as well (they're more likely to change in future * versions though). You can use any method that is declared as DLL or is inline, the * others are meant to be internal to ADCH++, very likely to change/disappear and will * generate link errors (when compiling under windows anyway). When starting a plugin * project I strongly recommend that you take one of the existing plugins and modify * it to your needs (to get all compiler settings and base code right). * * @section Versions Versions * * Due to C++ name mangling, plugins are generally valid only for a certain version * of the ADCH++ plugin API. This version usually follows the main ADCH++ version, * unless a small update is made that I judge shouldn't affect plugins in any way. * Most of the time, recompiling the plugin should be enough, unless any major changes * have been made, and your plugin doesn't rely on the nasty internals. * * @section Threads Threads * * ADCH++ has two main threads running when operating. One handles all network * communication while the other does all other work (handle protocol data and * so on). All plugins are run in the worker thread, which is the only thread * visible to the API. You are only allowed to interact with ADCH++ from this * thread, as none of the API is thread safe, unless otherwise noted. This has a * few important consequences. First off, you can assume that your plugin will * only be called by this thread, which means that you don't have to worry about * multithreading issues unless you start threads by yourself. Second, any work you * do in a plugin halts all of ADCH++'s processing (apart from receiving/sending * buffered data), in other words, don't do any lengthy processing in the on methods, * as the whole of ADCH++ will suffer. Third, if you indeed start another thread, make * sure you don't use any API functions from it apart from those explicitly marked * as thread safe. To indicate from a plugin that you have work to do in the main * worker thread, call PluginManager::attention(). */ #ifndef ADCHPP_PLUGINMANAGER_H #define ADCHPP_PLUGINMANAGER_H #include "version.h" #include "Signal.h" #include "ClientManager.h" #include "Plugin.h" namespace adchpp { class SimpleXML; #ifdef _WIN32 #ifdef BUILDING_ADCHPP #define PLUGIN_API #else #define PLUGIN_API __declspec(dllexport) #endif typedef HMODULE plugin_t; #else // WIN32 #ifdef BUILDING_ADCHPP #define PLUGIN_API #else #define PLUGIN_API __attribute__ ((visibility("default"))) #endif typedef void* plugin_t; #endif // WIN32 /** * PLUGIN_API double pluginGetVersion() * This function should just return the constant PLUGINVERSIONFLOAT * so that the pluginmanager can determine if this plugin should * be loaded or not */ typedef int (*PLUGIN_GET_VERSION)(); /** * PLUGIN_API void pluginLoad() * This function is called when the hub is starting up and loading the plugin. * Here you should load any data your plugin might need and connect to any * Managers you might be interested in. Note; you also have to connect to * PluginManager itself to receive its events. * @return 0 if the plugin was loaded ok, != 0 otherwise (the number will be logged, * use as error code). Plugin dll will get unloaded without calling pluginUnload if the return * value is not 0 here. * @see pluginUnload */ typedef int (*PLUGIN_LOAD)(PluginManager *); /** * PLUGIN_API void pluginUnload() * Called when the hub is shutting down * @see pluginLoad */ typedef void (*PLUGIN_UNLOAD)(); class PluginManager { public: typedef std::unordered_map> Registry; /** * This is a thread-safe method to call when you need to perform some work * in the main ADCH++ worker thread. Your job will be executed once, when * time permits. */ ADCHPP_DLL void attention(const std::function& f); /** * Get a list of currently loaded plugins */ const StringList& getPluginList() const { return plugins; } void setPluginList(const StringList& pluginList) { plugins = pluginList; } /** * Get the plugin path as set in adchpp.xml */ const std::string& getPluginPath() const { return pluginPath; } void setPluginPath(const std::string& path) { pluginPath = path; } /** * Register a plugin data type to be used with Client::setPSD and friends. * When data is removed, the deleter function will automatically be called * with the data as parameter, allowing automatic life cycle managment for * plugin-specific data. */ PluginDataHandle registerPluginData(const PluginDataDeleter& deleter_) { return PluginDataHandle(new PluginData(deleter_)); } /** * Register a plugin interface under a name. * @return false if name was already registered and call fails */ bool registerPlugin(const std::string& name, shared_ptr ptr) { return registry.insert(std::make_pair(name, ptr)).second; } /** @return True if the plugin existed and was thus unregistered */ bool unregisterPlugin(const std::string& name) { return registry.erase(name) > 0; } /** * @return Plugin interface, or an empty pointer if not found */ shared_ptr getPlugin(const std::string& name) { auto i = registry.find(name); return i == registry.end() ? shared_ptr() : i->second; } /** * The full map of registered plugins. */ const Registry& getPlugins() const { return registry; } typedef SignalTraits::Signal CommandSignal; typedef CommandSignal::Slot CommandSlot; /** * Utility function to handle +-commands from clients * The parameters are the same as ClientManager::signalReceive, only that the parameters will * have been parsed already, and the function will only be called if the command name matches */ ADCHPP_DLL ClientManager::SignalReceive::Connection onCommand(const std::string& commandName, const CommandSlot& f); /// Handle +-commands set by another script, and possibly prevent them from being dispatched ADCHPP_DLL CommandSignal& getCommandSignal(const std::string& commandName); /** @internal */ void load(); /** @internal */ void shutdown(); ADCHPP_DLL Core &getCore(); private: friend class Core; PluginManager(Core &core) throw(); class PluginInfo { public: PluginInfo(plugin_t h, PLUGIN_GET_VERSION v, PLUGIN_LOAD l, PLUGIN_UNLOAD u) : handle(h), pluginGetVersion(v), pluginLoad(l), pluginUnload(u) { } plugin_t handle; PLUGIN_GET_VERSION pluginGetVersion; PLUGIN_LOAD pluginLoad; PLUGIN_UNLOAD pluginUnload; }; struct CommandDispatch { CommandDispatch(PluginManager &pm, const std::string& name_, const PluginManager::CommandSlot& f_); void operator()(Entity& e, AdcCommand& cmd, bool& ok); private: std::string name; PluginManager::CommandSlot f; PluginManager *pm; }; friend struct CommandDispatch; typedef std::vector PluginList; typedef PluginList::iterator PluginIter; PluginList active; Registry registry; StringList plugins; std::string pluginPath; Core &core; static const std::string className; bool loadPlugin(const std::string& file); typedef std::unordered_map CommandHandlers; CommandHandlers commandHandlers; bool handleCommand(Entity& e, const StringList& l); }; } #endif // PLUGINMANAGER_H