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