dvander@370: /** ds@653: * vim: set ts=4 : ds@1330: * ============================================================================= dvander@1231: * SourceMod dvander@1231: * Copyright (C) 2004-2007 AlliedModders LLC. All rights reserved. ds@1330: * ============================================================================= dvander@370: * ds@1330: * This program is free software; you can redistribute it and/or modify it under ds@1330: * the terms of the GNU General Public License, version 3.0, as published by the ds@1330: * Free Software Foundation. dvander@1231: * ds@1330: * This program is distributed in the hope that it will be useful, but WITHOUT ds@1330: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS ds@1330: * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ds@1330: * details. dvander@1231: * ds@1330: * You should have received a copy of the GNU General Public License along with ds@1330: * this program. If not, see . dvander@1231: * ds@1330: * As a special exception, AlliedModders LLC gives you permission to link the ds@1330: * code of this program (as well as its derivative works) to "Half-Life 2," the ds@1330: * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software ds@1330: * by the Valve Corporation. You must obey the GNU General Public License in ds@1330: * all respects for all other code used. Additionally, AlliedModders LLC grants ds@1330: * this exception to all derivative works. AlliedModders LLC defines further ds@1330: * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), ds@1330: * or . dvander@370: * dvander@370: * Version: $Id$ dvander@370: */ dvander@370: dvander@173: #ifndef _INCLUDE_SOURCEMOD_PLUGINSYSTEM_H_ dvander@173: #define _INCLUDE_SOURCEMOD_PLUGINSYSTEM_H_ dvander@173: dvander@211: #include faluco@333: #include faluco@333: #include dvander@173: #include dvander@220: #include ds@611: #include dvander@174: #include dvander@174: #include faluco@389: #include dvander@601: #include dvander@173: #include "sm_globals.h" dvander@262: #include "vm/sp_vm_basecontext.h" dvander@203: #include "PluginInfoDatabase.h" dvander@211: #include "sm_trie.h" dvander@220: #include "sourcemod.h" dvander@309: #include faluco@1541: #if defined ORANGEBOX_BUILD faluco@1541: #include "convar_sm_ob.h" faluco@1541: #else ds@457: #include "convar_sm.h" faluco@1541: #endif dvander@173: dvander@174: using namespace SourceHook; dvander@174: dvander@209: /** dvander@209: * NOTES: dvander@209: * dvander@209: * Currently this system needs a lot of work but it's good skeletally. Plugin creation dvander@209: * is done without actually compiling anything. This is done by Load functions in the dvander@209: * manager. This will need a rewrite when we add context switching. dvander@209: * dvander@209: * The plugin object itself has a few things to note. The most important is that it stores dvander@209: * a table of function objects. The manager marshals allocation and freeing of these objects. dvander@209: * The plugin object can be in erroneous states, they are: dvander@209: * Plugin_Error --> Some error occurred any time during or after compilation. dvander@209: * This error can be cleared since the plugin itself is valid. dvander@209: * However, the state itself being set prevents any runtime action. dvander@209: * Plugin_BadLoad --> The plugin failed to load entirely and nothing can be done to save it. dvander@209: * dvander@209: * If a plugin fails to load externally, it is never added to the internal tracker. However, dvander@209: * plugins that failed to load from the internal loading mechanism are always tracked. This dvander@209: * allows users to see which automatically loaded plugins failed, and makes the interface a bit dvander@209: * more flexible. dvander@211: * dvander@211: * Once a plugin is compiled, it sets its own state to Plugin_Created. This state is still invalid dvander@211: * for execution. SourceMod is a two pass system, and even though the second pass is not implemented dvander@211: * yet, it is structured so Plugin_Created must be switched to Plugin_Running in the second pass. When dvander@211: * implemented, a Created plugin will be switched to Error in the second pass if it not loadable. dvander@211: * dvander@211: * The two pass loading mechanism is described below. Modules/natives are not implemented yet. dvander@211: * PASS ONE: All loadable plugins are found and have the following steps performed: dvander@211: * 1. Loading and compilation is attempted. dvander@211: * 2. If successful, all natives from Core are added. dvander@211: * 3. OnPluginLoad() is called. dvander@211: * 4. If failed, any user natives are scrapped and the process halts here. dvander@211: * 5. If successful, the plugin is ready for Pass 2. dvander@211: * INTERMEDIATE: dvander@211: * 1. All forced modules are loaded. dvander@211: * PASS TWO: All loaded plugins are found and have these steps performed: dvander@211: * 1. Any modules referenced in the plugin that are not already loaded, are loaded. dvander@211: * 2. If any module fails to load and the plugin requires it, load fails and jump to step 6. dvander@211: * 3. If any natives are unresolved, check if they are found in the user-natives pool. dvander@211: * 4. If yes, load succeeds. If not, natives are passed through a native acceptance filter. dvander@211: * 5. If the filter fails, the plugin is marked as failed. dvander@211: * 6. If the plugin has failed to load at this point, any dynamic natives it has added are scrapped. dvander@211: * Furthermore, any plugin that referenced these natives must now have pass 2 re-ran. dvander@211: * PASS THREE (not a real pass): ds@426: * 7. Once all plugins are deemed to be loaded, OnPluginStart() is called dvander@209: */ dvander@209: dvander@174: #define SM_CONTEXTVAR_MYSELF 0 dvander@174: dvander@173: struct ContextPair dvander@173: { dvander@209: ContextPair() : base(NULL), ctx(NULL), co(NULL) dvander@173: { dvander@173: }; dvander@262: BaseContext *base; dvander@173: sp_context_t *ctx; dvander@209: ICompilation *co; dvander@209: IVirtualMachine *vm; dvander@173: }; dvander@173: dvander@605: struct FakeNative dvander@605: { dvander@605: IPluginContext *ctx; dvander@605: IPluginFunction *call; dvander@605: String name; dvander@605: SPVM_NATIVE_FUNC func; dvander@605: }; dvander@605: faluco@383: enum LoadRes faluco@383: { faluco@383: LoadRes_Successful, faluco@383: LoadRes_AlreadyLoaded, dvander@1436: LoadRes_Failure, dvander@1436: LoadRes_NeverLoad faluco@383: }; faluco@383: dvander@978: struct AutoConfig dvander@978: { dvander@978: String autocfg; dvander@978: String folder; dvander@978: bool create; dvander@978: }; dvander@978: faluco@1112: class CPlugin; faluco@1112: struct WeakNative faluco@1112: { faluco@1112: WeakNative(CPlugin *plugin, uint32_t index) faluco@1112: { faluco@1112: pl = plugin; faluco@1112: idx = index; faluco@1112: } faluco@1112: CPlugin *pl; faluco@1112: uint32_t idx; faluco@1112: }; faluco@1112: dvander@173: class CPlugin : public IPlugin dvander@173: { dvander@174: friend class CPluginManager; dvander@176: friend class CFunction; dvander@173: public: dvander@209: CPlugin(const char *file); dvander@209: ~CPlugin(); dvander@209: public: faluco@641: PluginType GetType(); faluco@641: SourcePawn::IPluginContext *GetBaseContext(); faluco@641: sp_context_t *GetContext(); faluco@641: const sm_plugininfo_t *GetPublicInfo(); faluco@641: const char *GetFilename(); faluco@641: bool IsDebugging(); faluco@641: PluginStatus GetStatus(); faluco@641: bool SetPauseState(bool paused); faluco@641: unsigned int GetSerial(); faluco@641: const sp_plugin_t *GetPluginStructure(); faluco@641: IdentityToken_t *GetIdentity(); faluco@641: bool SetProperty(const char *prop, void *ptr); faluco@641: bool GetProperty(const char *prop, void **ptr, bool remove=false); dvander@173: public: dvander@209: /** dvander@209: * Creates a plugin object with default values. dvander@209: * If an error buffer is specified, and an error occurs, the error will be copied to the buffer dvander@209: * and NULL will be returned. dvander@209: * If an error buffer is not specified, the error will be copied to an internal buffer and dvander@209: * a valid (but error-stated) CPlugin will be returned. dvander@209: */ dvander@209: static CPlugin *CreatePlugin(const char *file, char *error, size_t maxlength); dvander@209: public: dvander@209: /** dvander@209: * Starts the initial compilation of a plugin. dvander@209: * Returns false if another compilation exists or there is a current context set. dvander@209: */ dvander@209: ICompilation *StartMyCompile(IVirtualMachine *vm); dvander@209: /** dvander@209: * Finalizes a compilation. If error buffer is NULL, the error is saved locally. dvander@209: */ dvander@209: bool FinishMyCompile(char *error, size_t maxlength); dvander@209: void CancelMyCompile(); dvander@229: dvander@209: /** dvander@209: * Sets an error state on the plugin dvander@209: */ dvander@209: void SetErrorState(PluginStatus status, const char *error_fmt, ...); dvander@211: dvander@211: /** dvander@229: * Initializes the plugin's identity information dvander@229: */ dvander@229: void InitIdentity(); dvander@229: dvander@229: /** dvander@211: * Calls the OnPluginLoad function, and sets any failed states if necessary. dvander@211: * NOTE: Valid pre-states are: Plugin_Created dvander@282: * NOTE: If validated, plugin state is changed to Plugin_Loaded dvander@211: * dvander@211: * If the error buffer is NULL, the error message is cached locally. dvander@211: */ dvander@211: bool Call_AskPluginLoad(char *error, size_t maxlength); dvander@211: dvander@211: /** ds@426: * Calls the OnPluginStart function. dvander@211: * NOTE: Valid pre-states are: Plugin_Created dvander@282: * NOTE: Post-state will be Plugin_Running dvander@211: */ ds@426: void Call_OnPluginStart(); dvander@282: dvander@282: /** ds@426: * Calls the OnPluginEnd function. dvander@282: */ ds@426: void Call_OnPluginEnd(); faluco@290: faluco@290: /** faluco@1221: * Calls the OnAllPluginsLoaded function. faluco@1221: */ faluco@1221: void Call_OnAllPluginsLoaded(); faluco@1221: faluco@1221: /** dvander@315: * Toggles debug mode in the plugin dvander@315: */ dvander@309: bool ToggleDebugMode(bool debug, char *error, size_t maxlength); faluco@290: faluco@290: /** dvander@315: * Returns true if a plugin is usable. dvander@315: */ dvander@615: bool IsRunnable(); faluco@389: faluco@389: /** dvander@536: * Adds a language file index to the plugin's list. dvander@536: */ faluco@389: void AddLangFile(unsigned int index); faluco@389: faluco@389: /** dvander@536: * Get language file count for this plugin. dvander@536: */ dvander@615: size_t GetLangFileCount(); faluco@389: faluco@389: /** dvander@536: * Get language file index based on the vector index. dvander@536: */ dvander@615: unsigned int GetLangFileByIndex(unsigned int index); dvander@211: public: faluco@333: /** dvander@536: * Returns the modification time during last plugin load. dvander@536: */ dvander@615: time_t GetTimeStamp(); faluco@333: faluco@333: /** dvander@536: * Returns the current modification time of the plugin file. dvander@536: */ faluco@333: time_t GetFileTimeStamp(); dvander@282: dvander@282: /** dvander@282: * Returns true if the plugin was running, but is now invalid. dvander@282: */ dvander@282: bool WasRunning(); dvander@536: dvander@536: Handle_t GetMyHandle(); dvander@978: dvander@978: void AddConfig(bool autoCreate, const char *cfg, const char *folder); dvander@978: unsigned int GetConfigCount(); dvander@978: AutoConfig *GetConfig(unsigned int i); faluco@1112: inline void AddLibrary(const char *name) faluco@1089: { faluco@1112: m_Libraries.push_back(name); faluco@1089: } dvander@1124: void LibraryActions(bool dropping); dvander@173: protected: dvander@173: void UpdateInfo(); faluco@333: void SetTimeStamp(time_t t); dvander@605: void DependencyDropped(CPlugin *pOwner); dvander@173: private: dvander@209: ContextPair m_ctx; dvander@174: PluginType m_type; ds@652: char m_filename[PLATFORM_MAX_PATH]; dvander@173: PluginStatus m_status; dvander@173: unsigned int m_serial; dvander@173: sm_plugininfo_t m_info; dvander@173: sp_plugin_t *m_plugin; dvander@209: char m_errormsg[256]; dvander@211: time_t m_LastAccess; dvander@229: IdentityToken_t *m_ident; dvander@220: Handle_t m_handle; dvander@282: bool m_WasRunning; faluco@389: CVector m_PhraseFiles; dvander@605: List m_dependents; dvander@605: List m_dependsOn; dvander@605: List m_fakeNatives; dvander@1122: List m_WeakNatives; faluco@1112: List m_RequiredLibs; faluco@1112: List m_Libraries; dvander@486: Trie *m_pProps; dvander@605: bool m_FakeNativesMissing; faluco@1112: bool m_LibraryMissing; dvander@978: CVector m_configs; dvander@1630: bool m_bGotAllLoaded; dvander@601: }; dvander@601: dvander@220: class CPluginManager : dvander@220: public IPluginManager, dvander@220: public SMGlobalClass, dvander@309: public IHandleTypeDispatch, dvander@309: public IRootConsoleCommand dvander@174: { dvander@176: friend class CPlugin; dvander@174: public: dvander@174: CPluginManager(); dvander@176: ~CPluginManager(); dvander@174: public: dvander@203: /* Implements iterator class */ dvander@174: class CPluginIterator : public IPluginIterator dvander@174: { dvander@174: public: dvander@213: CPluginIterator(List *mylist); dvander@174: virtual ~CPluginIterator(); dvander@174: virtual bool MorePlugins(); dvander@174: virtual IPlugin *GetPlugin(); dvander@174: virtual void NextPlugin(); dvander@282: void Release(); dvander@174: public: dvander@174: void Reset(); dvander@174: private: dvander@213: List *mylist; dvander@213: List::iterator current; dvander@174: }; dvander@174: friend class CPluginManager::CPluginIterator; dvander@203: public: //IPluginManager dvander@282: IPlugin *LoadPlugin(const char *path, dvander@174: bool debug, dvander@174: PluginType type, dvander@174: char error[], ds@755: size_t maxlength, faluco@383: bool *wasloaded); dvander@282: bool UnloadPlugin(IPlugin *plugin); dvander@282: IPlugin *FindPluginByContext(const sp_context_t *ctx); dvander@282: unsigned int GetPluginCount(); dvander@282: IPluginIterator *GetPluginIterator(); dvander@282: void AddPluginsListener(IPluginsListener *listener); dvander@282: void RemovePluginsListener(IPluginsListener *listener); dvander@220: public: //SMGlobalClass dvander@282: void OnSourceModAllInitialized(); dvander@282: void OnSourceModShutdown(); dvander@220: public: //IHandleTypeDispatch dvander@282: void OnHandleDestroy(HandleType_t type, void *object); dvander@309: public: //IRootConsoleCommand faluco@1541: void OnRootConsoleCommand(const char *cmdname, const CCommand &command); dvander@190: public: dvander@203: /** dvander@211: * Loads all plugins not yet loaded dvander@203: */ dvander@211: void LoadAll_FirstPass(const char *config, const char *basedir); dvander@211: dvander@211: /** dvander@211: * Runs the second loading pass for all plugins dvander@211: */ dvander@211: void LoadAll_SecondPass(); dvander@203: dvander@203: /** dvander@203: * Tests a plugin file mask against a local folder. dvander@203: * The alias is searched backwards from localdir - i.e., given this input: dvander@203: * csdm/ban csdm/ban dvander@203: * ban csdm/ban dvander@203: * csdm/ban optional/csdm/ban dvander@203: * All of these will return true for an alias match. dvander@203: * Wildcards are allowed in the filename. dvander@203: */ dvander@209: bool TestAliasMatch(const char *alias, const char *localdir); dvander@211: dvander@211: /** dvander@211: * Returns whether anything loaded will be a late load. dvander@211: */ faluco@290: bool IsLateLoadTime() const; dvander@233: dvander@233: /** dvander@233: * Adds natives from core into the native pool. dvander@233: */ dvander@233: void RegisterNativesFromCore(sp_nativeinfo_t *natives); dvander@247: dvander@247: /** dvander@247: * Converts a Handle to an IPlugin if possible. dvander@247: */ dvander@247: IPlugin *PluginFromHandle(Handle_t handle, HandleError *err); faluco@290: faluco@290: /** dvander@309: * Finds a plugin based on its index. (starts on index 1) dvander@309: */ faluco@290: CPlugin *GetPluginByOrder(int num); dvander@309: dvander@1638: int GetOrderOfPlugin(IPlugin *pl); dvander@1638: ds@457: /** ds@457: * Internal version of FindPluginByContext() ds@457: */ ds@457: inline CPlugin *GetPluginByCtx(const sp_context_t *ctx) ds@457: { ds@457: return reinterpret_cast(ctx->user[SM_CONTEXTVAR_MYSELF]); ds@457: } ds@457: dvander@309: /** dvander@309: * Gets status text for a status code dvander@309: */ dvander@309: const char *GetStatusText(PluginStatus status); dvander@315: faluco@333: /** faluco@333: * Reload or update plugins on level shutdown. faluco@333: */ faluco@333: void ReloadOrUnloadPlugins(); faluco@333: ds@611: /** ds@611: * Add public functions from all running or paused ds@611: * plugins to the specified forward if the names match. ds@611: */ ds@611: void AddFunctionsToForward(const char *name, IChangeableForward *pForward); ds@611: faluco@1221: /** faluco@1221: * Iterates through plugins to call OnAllPluginsLoaded. faluco@1221: */ faluco@1221: void AllPluginsLoaded(); faluco@1221: dvander@900: CPlugin *GetPluginFromIdentity(IdentityToken_t *pToken); dvander@900: dvander@1018: void Shutdown(); dvander@1018: dvander@1572: void OnLibraryAction(const char *lib, bool is_a_plugin, bool drop); dvander@1124: dvander@1124: bool LibraryExists(const char *lib); faluco@1348: faluco@1348: bool ReloadPlugin(CPlugin *pl); dvander@1630: dvander@1630: void UnloadAll(); dvander@209: private: ds@755: LoadRes _LoadPlugin(CPlugin **pPlugin, const char *path, bool debug, PluginType type, char error[], size_t maxlength); dvander@298: dvander@298: void LoadAutoPlugin(const char *plugin); dvander@298: dvander@209: /** dvander@209: * Recursively loads all plugins in the given directory. dvander@209: */ dvander@209: void LoadPluginsFromDir(const char *basedir, const char *localdir); dvander@209: dvander@209: /** dvander@211: * Adds a plugin object. This is wrapped by LoadPlugin functions. dvander@211: */ dvander@211: void AddPlugin(CPlugin *pPlugin); dvander@211: dvander@211: /** dvander@211: * Runs the second loading pass on a plugin. dvander@209: */ dvander@298: bool RunSecondPass(CPlugin *pPlugin, char *error, size_t maxlength); dvander@211: dvander@211: /** dvander@211: * Adds any globally registered natives to a plugin dvander@211: */ dvander@211: void AddCoreNativesToPlugin(CPlugin *pPlugin); dvander@315: dvander@315: /** dvander@315: * Runs an extension pass on a plugin. dvander@315: */ dvander@315: bool LoadOrRequireExtensions(CPlugin *pPlugin, unsigned int pass, char *error, size_t maxlength); faluco@430: faluco@1112: /** faluco@1112: * Manages required natives. faluco@1112: */ faluco@1112: bool FindOrRequirePluginDeps(CPlugin *pPlugin, char *error, size_t maxlength); faluco@1112: faluco@430: void _SetPauseState(CPlugin *pPlugin, bool pause); dvander@174: protected: dvander@209: /** dvander@209: * Caching internal objects dvander@209: */ dvander@174: void ReleaseIterator(CPluginIterator *iter); dvander@900: public: dvander@229: inline IdentityToken_t *GetIdentity() dvander@229: { dvander@229: return m_MyIdent; dvander@229: } dvander@601: public: dvander@601: bool AddFakeNative(IPluginFunction *pFunction, const char *name, SPVM_FAKENATIVE_FUNC func); dvander@601: private: dvander@601: void AddFakeNativesToPlugin(CPlugin *pPlugin); faluco@1112: void TryRefreshDependencies(CPlugin *pOther); dvander@174: private: dvander@174: List m_listeners; dvander@213: List m_plugins; dvander@174: CStack m_iters; dvander@203: CPluginInfoDatabase m_PluginInfo; dvander@211: Trie *m_LoadLookup; dvander@211: bool m_AllPluginsLoaded; dvander@229: IdentityToken_t *m_MyIdent; dvander@1382: Trie *m_pCoreNatives; dvander@601: dvander@601: /* Dynamic native stuff */ dvander@601: List m_Natives; dvander@601: Trie *m_pNativeLookup; dvander@601: dvander@1630: bool m_LoadingLocked; dvander@174: }; dvander@174: dvander@211: extern CPluginManager g_PluginSys; dvander@174: dvander@173: #endif //_INCLUDE_SOURCEMOD_PLUGINSYSTEM_H_