dvander@370: /**
ds@653: * vim: set ts=4 :
ds@1330: * =============================================================================
dvander@1231: * SourceMod
ds@3099: * Copyright (C) 2004-2010 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@211: #include "sm_trie.h"
dvander@220: #include "sourcemod.h"
dvander@309: #include
skamonkey@3515: #if SOURCE_ENGINE >= SE_ALIENSWARM
ds@3099: #include "convar_sm_swarm.h"
skamonkey@3613: #elif SOURCE_ENGINE >= SE_LEFT4DEAD
ds@2400: #include "convar_sm_l4d.h"
skamonkey@3613: #elif SOURCE_ENGINE >= SE_ORANGEBOX
faluco@1541: #include "convar_sm_ob.h"
faluco@1541: #else
ds@457: #include "convar_sm.h"
faluco@1541: #endif
dvander@2042: #include "ITranslator.h"
asherkin@3601: #include "IGameConfigs.h"
dvander@2086: #include "NativeOwner.h"
dvander@2086: #include "ShareSys.h"
dvander@173:
dvander@2547: class CPlayer;
dvander@2547:
dvander@174: using namespace SourceHook;
dvander@174:
dvander@209: /**
dvander@209: * NOTES:
dvander@209: *
dvander@1923: * UPDATE 2008-03-11: These comments are horribly out of date. They paint a good overall
dvander@1923: * picture of how PluginSys works, but things like dependencies and fake natives have
dvander@1923: * complicated things quite a bit.
dvander@1923: *
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:
faluco@383: enum LoadRes
faluco@383: {
faluco@383: LoadRes_Successful,
faluco@383: LoadRes_AlreadyLoaded,
dvander@1436: LoadRes_Failure,
fyrenmoo@2675: LoadRes_SilentFailure,
dvander@1436: LoadRes_NeverLoad
faluco@383: };
faluco@383:
fyrenmoo@2675: enum APLRes
fyrenmoo@2675: {
fyrenmoo@2675: APLRes_Success,
fyrenmoo@2675: APLRes_Failure,
fyrenmoo@2675: APLRes_SilentFailure
fyrenmoo@2675: };
fyrenmoo@2675:
asherkin@3703: enum LibraryAction
asherkin@3703: {
asherkin@3703: LibraryAction_Removed,
asherkin@3703: LibraryAction_Added
asherkin@3703: };
asherkin@3703:
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:
dvander@2086: class CPlugin :
dvander@2086: public IPlugin,
dvander@2086: public CNativeOwner
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();
dvander@2193: void *GetPluginStructure();
faluco@641: const char *GetFilename();
faluco@641: bool IsDebugging();
faluco@641: PluginStatus GetStatus();
fyrenmoo@2675: bool IsSilentlyFailed();
fyrenmoo@2675: void SetSilentlyFailed(bool sf);
dvander@2193: const sm_plugininfo_t *GetPublicInfo();
faluco@641: bool SetPauseState(bool paused);
faluco@641: unsigned int GetSerial();
faluco@641: IdentityToken_t *GetIdentity();
dvander@1766: unsigned int CalcMemUsage();
faluco@641: bool SetProperty(const char *prop, void *ptr);
faluco@641: bool GetProperty(const char *prop, void **ptr, bool remove=false);
dvander@2086: void DropEverything();
dvander@2193: SourcePawn::IPluginRuntime *GetRuntime();
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@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: */
fyrenmoo@2675: APLRes 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: * Returns true if a plugin is usable.
dvander@315: */
dvander@615: bool IsRunnable();
faluco@389:
faluco@389: /**
dvander@2042: * Get languages info.
dvander@536: */
dvander@2042: IPhraseCollection *GetPhrases();
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@2086: bool AddFakeNative(IPluginFunction *pFunc, const char *name, SPVM_FAKENATIVE_FUNC func);
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: }
asherkin@3703: void LibraryActions(LibraryAction action);
dvander@2342: void SyncMaxClients(int max_clients);
dvander@173: protected:
dvander@2342: bool UpdateInfo();
faluco@333: void SetTimeStamp(time_t t);
dvander@605: void DependencyDropped(CPlugin *pOwner);
dvander@173: private:
dvander@174: PluginType m_type;
ds@652: char m_filename[PLATFORM_MAX_PATH];
dvander@173: PluginStatus m_status;
fyrenmoo@2675: bool m_bSilentlyFailed;
dvander@173: unsigned int m_serial;
dvander@173: sm_plugininfo_t m_info;
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;
dvander@2042: IPhraseCollection *m_pPhrases;
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@2105: int m_FileVersion;
dvander@2105: char m_DateTime[256];
dvander@2193: IPluginRuntime *m_pRuntime;
dvander@2193: IPluginContext *m_pContext;
dvander@2342: sp_pubvar_t *m_MaxClientsVar;
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();
asherkin@3601: ConfigResult OnSourceModConfigChanged(const char *key, const char *value, ConfigSource source, char *error, size_t maxlength);
dvander@2342: void OnSourceModMaxPlayersChanged(int newvalue);
dvander@220: public: //IHandleTypeDispatch
dvander@282: void OnHandleDestroy(HandleType_t type, void *object);
dvander@1766: bool GetHandleApproxSize(HandleType_t type, void *object, unsigned int *pSize);
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@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: */
dvander@2193: CPlugin *GetPluginByCtx(const sp_context_t *ctx);
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:
asherkin@3703: void OnLibraryAction(const char *lib, LibraryAction action);
dvander@1124:
dvander@1124: bool LibraryExists(const char *lib);
faluco@1348:
faluco@1348: bool ReloadPlugin(CPlugin *pl);
dvander@1630:
dvander@1630: void UnloadAll();
dvander@1902:
dvander@1902: CPlugin *FindPluginByConsoleArg(const char *arg);
dvander@2342:
dvander@2342: void SyncMaxClients(int max_clients);
dvander@2547:
dvander@2547: void ListPluginsToClient(CPlayer *player, const CCommand &args);
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@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: private:
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@211: Trie *m_LoadLookup;
dvander@211: bool m_AllPluginsLoaded;
dvander@229: IdentityToken_t *m_MyIdent;
dvander@601:
dvander@601: /* Dynamic native stuff */
dvander@601: List m_Natives;
dvander@601:
dvander@1630: bool m_LoadingLocked;
asherkin@3601:
asherkin@3601: // Config
asherkin@3601: bool m_bBlockBadPlugins;
asherkin@3703:
asherkin@3703: // Forwards
asherkin@3703: IForward *m_pOnLibraryAdded;
asherkin@3703: IForward *m_pOnLibraryRemoved;
dvander@174: };
dvander@174:
dvander@211: extern CPluginManager g_PluginSys;
dvander@174:
dvander@173: #endif //_INCLUDE_SOURCEMOD_PLUGINSYSTEM_H_
dvander@2906: