core/systems/PluginSys.h
author David Anderson <dvander@alliedmods.net>
Fri Feb 16 03:47:10 2007 +0000 (2007-02-16)
changeset 501 e209a7296936
parent 487 3410298035cb
child 536 164f7d3482ef
permissions -rw-r--r--
removed this potential feature, not useful enough
     1 /**
     2  * ===============================================================
     3  * SourceMod (C)2004-2007 AlliedModders LLC.  All rights reserved.
     4  * ===============================================================
     5  *
     6  * This file is not open source and may not be copied without explicit
     7  * written permission of AlliedModders LLC.  This file may not be redistributed 
     8  * in whole or significant part.
     9  * For information, see LICENSE.txt or http://www.sourcemod.net/license.php
    10  *
    11  * Version: $Id$
    12  */
    13 
    14 #ifndef _INCLUDE_SOURCEMOD_PLUGINSYSTEM_H_
    15 #define _INCLUDE_SOURCEMOD_PLUGINSYSTEM_H_
    16 
    17 #include <time.h>
    18 #include <errno.h>
    19 #include <sys/stat.h>
    20 #include <IPluginSys.h>
    21 #include <IHandleSys.h>
    22 #include <sh_list.h>
    23 #include <sh_stack.h>
    24 #include <sh_vector.h>
    25 #include "sm_globals.h"
    26 #include "vm/sp_vm_basecontext.h"
    27 #include "PluginInfoDatabase.h"
    28 #include "sm_trie.h"
    29 #include "sourcemod.h"
    30 #include <IRootConsoleMenu.h>
    31 #include "convar_sm.h"
    32 
    33 using namespace SourceHook;
    34 
    35 /**
    36  * NOTES:
    37  *
    38  *  Currently this system needs a lot of work but it's good skeletally.  Plugin creation 
    39  * is done without actually compiling anything.  This is done by Load functions in the 
    40  * manager.  This will need a rewrite when we add context switching.
    41  *
    42  *  The plugin object itself has a few things to note.  The most important is that it stores
    43  * a table of function objects.  The manager marshals allocation and freeing of these objects.
    44  * The plugin object can be in erroneous states, they are:
    45  *   Plugin_Error   --> Some error occurred any time during or after compilation.
    46  *						This error can be cleared since the plugin itself is valid.
    47  *						However, the state itself being set prevents any runtime action.
    48  *   Plugin_BadLoad	--> The plugin failed to load entirely and nothing can be done to save it.
    49  *
    50  *  If a plugin fails to load externally, it is never added to the internal tracker.  However, 
    51  * plugins that failed to load from the internal loading mechanism are always tracked.  This 
    52  * allows users to see which automatically loaded plugins failed, and makes the interface a bit
    53  * more flexible.
    54  *
    55  *  Once a plugin is compiled, it sets its own state to Plugin_Created.  This state is still invalid
    56  * for execution.  SourceMod is a two pass system, and even though the second pass is not implemented
    57  * yet, it is structured so Plugin_Created must be switched to Plugin_Running in the second pass.  When
    58  * implemented, a Created plugin will be switched to Error in the second pass if it not loadable.
    59  *
    60  *  The two pass loading mechanism is described below.  Modules/natives are not implemented yet.
    61  * PASS ONE: All loadable plugins are found and have the following steps performed:
    62  *			 1.  Loading and compilation is attempted.
    63  *			 2.  If successful, all natives from Core are added.
    64  *			 3.  OnPluginLoad() is called.
    65  *			 4.  If failed, any user natives are scrapped and the process halts here.
    66  *			 5.  If successful, the plugin is ready for Pass 2.
    67  * INTERMEDIATE:
    68  *			 1.  All forced modules are loaded.
    69  * PASS TWO: All loaded plugins are found and have these steps performed:
    70  *			 1. Any modules referenced in the plugin that are not already loaded, are loaded.
    71  *			 2. If any module fails to load and the plugin requires it, load fails and jump to step 6.
    72  *			 3. If any natives are unresolved, check if they are found in the user-natives pool.
    73  *			 4. If yes, load succeeds.  If not, natives are passed through a native acceptance filter.
    74  *			 5. If the filter fails, the plugin is marked as failed.
    75  *			 6. If the plugin has failed to load at this point, any dynamic natives it has added are scrapped.
    76  *			    Furthermore, any plugin that referenced these natives must now have pass 2 re-ran.
    77  * PASS THREE (not a real pass):
    78  *			 7. Once all plugins are deemed to be loaded, OnPluginStart() is called
    79  */
    80 
    81 #define SM_CONTEXTVAR_MYSELF	0
    82 
    83 struct ContextPair
    84 {
    85 	ContextPair() : base(NULL), ctx(NULL), co(NULL)
    86 	{
    87 	};
    88 	BaseContext *base;
    89 	sp_context_t *ctx;
    90 	ICompilation *co;
    91 	IVirtualMachine *vm;
    92 };
    93 
    94 enum LoadRes
    95 {
    96 	LoadRes_Successful,
    97 	LoadRes_AlreadyLoaded,
    98 	LoadRes_Failure
    99 };
   100 
   101 class CPlugin : public IPlugin
   102 {
   103 	friend class CPluginManager;
   104 	friend class CFunction;
   105 public:
   106 	CPlugin(const char *file);
   107 	~CPlugin();
   108 public:
   109 	virtual PluginType GetType() const;
   110 	virtual SourcePawn::IPluginContext *GetBaseContext() const;
   111 	virtual sp_context_t *GetContext() const;
   112 	virtual const sm_plugininfo_t *GetPublicInfo() const;
   113 	virtual const char *GetFilename() const;
   114 	virtual bool IsDebugging() const;
   115 	virtual PluginStatus GetStatus() const;
   116 	virtual bool SetPauseState(bool paused);
   117 	virtual unsigned int GetSerial() const;
   118 	virtual const sp_plugin_t *GetPluginStructure() const;
   119 	virtual IdentityToken_t *GetIdentity() const;
   120 	virtual bool SetProperty(const char *prop, void *ptr);
   121 	virtual bool GetProperty(const char *prop, void **ptr, bool remove=false);
   122 public:
   123 	/**
   124 	 * Creates a plugin object with default values.
   125 	 *   If an error buffer is specified, and an error occurs, the error will be copied to the buffer
   126 	 * and NULL will be returned.
   127 	 *   If an error buffer is not specified, the error will be copied to an internal buffer and 
   128 	 * a valid (but error-stated) CPlugin will be returned.
   129 	 */
   130 	static CPlugin *CreatePlugin(const char *file, char *error, size_t maxlength);
   131 public:
   132 	/**
   133 	 * Starts the initial compilation of a plugin.
   134 	 * Returns false if another compilation exists or there is a current context set.
   135 	 */
   136 	ICompilation *StartMyCompile(IVirtualMachine *vm);
   137 	/** 
   138 	 * Finalizes a compilation.  If error buffer is NULL, the error is saved locally.
   139 	 */
   140 	bool FinishMyCompile(char *error, size_t maxlength);
   141 	void CancelMyCompile();
   142 
   143 	/**
   144 	 * Sets an error state on the plugin
   145 	 */
   146 	void SetErrorState(PluginStatus status, const char *error_fmt, ...);
   147 
   148 	/**
   149 	 * Initializes the plugin's identity information
   150 	 */
   151 	void InitIdentity();
   152 
   153 	/**
   154 	 * Calls the OnPluginLoad function, and sets any failed states if necessary.
   155 	 * NOTE: Valid pre-states are: Plugin_Created
   156 	 * NOTE: If validated, plugin state is changed to Plugin_Loaded
   157 	 *
   158 	 * If the error buffer is NULL, the error message is cached locally.
   159 	 */
   160 	bool Call_AskPluginLoad(char *error, size_t maxlength);
   161 
   162 	/**
   163 	 * Calls the OnPluginStart function.
   164 	 * NOTE: Valid pre-states are: Plugin_Created
   165 	 * NOTE: Post-state will be Plugin_Running
   166 	 */
   167 	void Call_OnPluginStart();
   168 
   169 	/**
   170 	 * Calls the OnPluginEnd function.
   171 	 */
   172 	void Call_OnPluginEnd();
   173 
   174 	/**
   175 	 * Toggles debug mode in the plugin
   176 	 */
   177 	bool ToggleDebugMode(bool debug, char *error, size_t maxlength);
   178 
   179 	/**
   180 	 * Returns true if a plugin is usable.
   181 	 */
   182 	bool IsRunnable() const;
   183 
   184 	/**
   185 	* Adds a language file index to the plugin's list.
   186 	*/
   187 	void AddLangFile(unsigned int index);
   188 
   189 	/**
   190 	* Get language file count for this plugin.
   191 	*/
   192 	size_t GetLangFileCount() const;
   193 
   194 	/**
   195 	* Get language file index based on the vector index.
   196 	*/
   197 	unsigned int GetLangFileByIndex(unsigned int index) const;
   198 public:
   199 	/**
   200 	* Returns the modification time during last plugin load.
   201 	*/
   202 	time_t GetTimeStamp() const;
   203 
   204 	/**
   205 	* Returns the current modification time of the plugin file.
   206 	*/
   207 	time_t GetFileTimeStamp();
   208 
   209 	/** 
   210 	 * Returns true if the plugin was running, but is now invalid.
   211 	 */
   212 	bool WasRunning();
   213 protected:
   214 	void UpdateInfo();
   215 	void SetTimeStamp(time_t t);
   216 private:
   217 	ContextPair m_ctx;
   218 	PluginType m_type;
   219 	char m_filename[PLATFORM_MAX_PATH+1];
   220 	PluginStatus m_status;
   221 	unsigned int m_serial;
   222 	sm_plugininfo_t m_info;
   223 	sp_plugin_t *m_plugin;
   224 	char m_errormsg[256];
   225 	time_t m_LastAccess;
   226 	IdentityToken_t *m_ident;
   227 	Handle_t m_handle;
   228 	bool m_WasRunning;
   229 	CVector<unsigned int> m_PhraseFiles;
   230 	Trie *m_pProps;
   231 };
   232 
   233 class CPluginManager : 
   234 	public IPluginManager,
   235 	public SMGlobalClass,
   236 	public IHandleTypeDispatch,
   237 	public IRootConsoleCommand
   238 {
   239 	friend class CPlugin;
   240 public:
   241 	CPluginManager();
   242 	~CPluginManager();
   243 public:
   244 	/* Implements iterator class */
   245 	class CPluginIterator : public IPluginIterator
   246 	{
   247 	public:
   248 		CPluginIterator(List<CPlugin *> *mylist);
   249 		virtual ~CPluginIterator();
   250 		virtual bool MorePlugins();
   251 		virtual IPlugin *GetPlugin();
   252 		virtual void NextPlugin();
   253 		void Release();
   254 	public:
   255 		void Reset();
   256 	private:
   257 		List<CPlugin *> *mylist;
   258 		List<CPlugin *>::iterator current;
   259 	};
   260 	friend class CPluginManager::CPluginIterator;
   261 public: //IPluginManager
   262 	IPlugin *LoadPlugin(const char *path, 
   263 								bool debug,
   264 								PluginType type,
   265 								char error[],
   266 								size_t err_max,
   267 								bool *wasloaded);
   268 	bool UnloadPlugin(IPlugin *plugin);
   269 	IPlugin *FindPluginByContext(const sp_context_t *ctx);
   270 	unsigned int GetPluginCount();
   271 	IPluginIterator *GetPluginIterator();
   272 	void AddPluginsListener(IPluginsListener *listener);
   273 	void RemovePluginsListener(IPluginsListener *listener);
   274 public: //SMGlobalClass
   275 	void OnSourceModAllInitialized();
   276 	void OnSourceModShutdown();
   277 public: //IHandleTypeDispatch
   278 	void OnHandleDestroy(HandleType_t type, void *object);
   279 public: //IRootConsoleCommand
   280 	void OnRootConsoleCommand(const char *command, unsigned int argcount);
   281 public:
   282 	/**
   283 	 * Loads all plugins not yet loaded
   284 	 */
   285 	void LoadAll_FirstPass(const char *config, const char *basedir);
   286 
   287 	/**
   288 	 * Runs the second loading pass for all plugins
   289 	 */
   290 	void LoadAll_SecondPass();
   291 
   292 	/**
   293 	 * Tests a plugin file mask against a local folder.
   294 	 * The alias is searched backwards from localdir - i.e., given this input:
   295 	 *   csdm/ban        csdm/ban
   296 	 *   ban             csdm/ban
   297 	 *   csdm/ban        optional/csdm/ban
   298 	 * All of these will return true for an alias match.  
   299 	 * Wildcards are allowed in the filename.
   300 	 */
   301 	bool TestAliasMatch(const char *alias, const char *localdir);
   302 
   303 	/** 
   304 	 * Returns whether anything loaded will be a late load.
   305 	 */
   306 	bool IsLateLoadTime() const;
   307 
   308 	/**
   309 	 * Adds natives from core into the native pool.
   310 	 */
   311 	void RegisterNativesFromCore(sp_nativeinfo_t *natives);
   312 
   313 	/**
   314 	 * Converts a Handle to an IPlugin if possible.
   315 	 */
   316 	IPlugin *PluginFromHandle(Handle_t handle, HandleError *err);
   317 
   318 	/**
   319 	 * Finds a plugin based on its index. (starts on index 1)
   320 	 */
   321 	CPlugin *GetPluginByOrder(int num);
   322 
   323 	/** 
   324 	 * Internal version of FindPluginByContext()
   325 	 */
   326 	inline CPlugin *GetPluginByCtx(const sp_context_t *ctx)
   327 	{
   328 		return reinterpret_cast<CPlugin *>(ctx->user[SM_CONTEXTVAR_MYSELF]);
   329 	}
   330 
   331 	/**
   332 	 * Gets status text for a status code 
   333 	 */
   334 	const char *GetStatusText(PluginStatus status);
   335 
   336 	/**
   337 	* Reload or update plugins on level shutdown.
   338 	*/
   339 	void ReloadOrUnloadPlugins();
   340 
   341 private:
   342 	LoadRes _LoadPlugin(CPlugin **pPlugin, const char *path, bool debug, PluginType type, char error[], size_t err_max);
   343 
   344 	void LoadAutoPlugin(const char *plugin);
   345 
   346 	/**
   347 	 * Recursively loads all plugins in the given directory.
   348 	 */
   349 	void LoadPluginsFromDir(const char *basedir, const char *localdir);
   350 
   351 	/**
   352 	 * Adds a plugin object.  This is wrapped by LoadPlugin functions.
   353 	 */
   354 	void AddPlugin(CPlugin *pPlugin);
   355 
   356 	/**
   357 	 * Runs the second loading pass on a plugin.
   358 	 */
   359 	bool RunSecondPass(CPlugin *pPlugin, char *error, size_t maxlength);
   360 
   361 	/**
   362 	 * Adds any globally registered natives to a plugin
   363 	 */
   364 	void AddCoreNativesToPlugin(CPlugin *pPlugin);
   365 
   366 	/**
   367 	 * Runs an extension pass on a plugin.
   368 	 */
   369 	bool LoadOrRequireExtensions(CPlugin *pPlugin, unsigned int pass, char *error, size_t maxlength);
   370 
   371 	void _SetPauseState(CPlugin *pPlugin, bool pause);
   372 protected:
   373 	/**
   374 	 * Caching internal objects
   375 	 */
   376 	void ReleaseIterator(CPluginIterator *iter);
   377 	inline IdentityToken_t *GetIdentity()
   378 	{
   379 		return m_MyIdent;
   380 	}
   381 private:
   382 	List<IPluginsListener *> m_listeners;
   383 	List<CPlugin *> m_plugins;
   384 	List<sp_nativeinfo_t *> m_natives;
   385 	CStack<CPluginManager::CPluginIterator *> m_iters;
   386 	CPluginInfoDatabase m_PluginInfo;
   387 	Trie *m_LoadLookup;
   388 	bool m_AllPluginsLoaded;
   389 	IdentityToken_t *m_MyIdent;
   390 };
   391 
   392 extern CPluginManager g_PluginSys;
   393 
   394 #endif //_INCLUDE_SOURCEMOD_PLUGINSYSTEM_H_