core/systems/PluginSys.h
author David Anderson <dvander@alliedmods.net>
Mon Mar 12 07:08:05 2007 +0000 (2007-03-12)
changeset 601 fe5b368fcedc
parent 536 164f7d3482ef
child 605 0b7655d87148
permissions -rw-r--r--
initial import of dynamic native code for both the JIT and plugins
note: dependency resolution is not done yet!
     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 enum LoadRes
    96 {
    97 	LoadRes_Successful,
    98 	LoadRes_AlreadyLoaded,
    99 	LoadRes_Failure
   100 };
   101 
   102 class CPlugin : public IPlugin
   103 {
   104 	friend class CPluginManager;
   105 	friend class CFunction;
   106 public:
   107 	CPlugin(const char *file);
   108 	~CPlugin();
   109 public:
   110 	virtual PluginType GetType() const;
   111 	virtual SourcePawn::IPluginContext *GetBaseContext() const;
   112 	virtual sp_context_t *GetContext() const;
   113 	virtual const sm_plugininfo_t *GetPublicInfo() const;
   114 	virtual const char *GetFilename() const;
   115 	virtual bool IsDebugging() const;
   116 	virtual PluginStatus GetStatus() const;
   117 	virtual bool SetPauseState(bool paused);
   118 	virtual unsigned int GetSerial() const;
   119 	virtual const sp_plugin_t *GetPluginStructure() const;
   120 	virtual IdentityToken_t *GetIdentity() const;
   121 	virtual bool SetProperty(const char *prop, void *ptr);
   122 	virtual bool GetProperty(const char *prop, void **ptr, bool remove=false);
   123 public:
   124 	/**
   125 	 * Creates a plugin object with default values.
   126 	 *   If an error buffer is specified, and an error occurs, the error will be copied to the buffer
   127 	 * and NULL will be returned.
   128 	 *   If an error buffer is not specified, the error will be copied to an internal buffer and 
   129 	 * a valid (but error-stated) CPlugin will be returned.
   130 	 */
   131 	static CPlugin *CreatePlugin(const char *file, char *error, size_t maxlength);
   132 public:
   133 	/**
   134 	 * Starts the initial compilation of a plugin.
   135 	 * Returns false if another compilation exists or there is a current context set.
   136 	 */
   137 	ICompilation *StartMyCompile(IVirtualMachine *vm);
   138 	/** 
   139 	 * Finalizes a compilation.  If error buffer is NULL, the error is saved locally.
   140 	 */
   141 	bool FinishMyCompile(char *error, size_t maxlength);
   142 	void CancelMyCompile();
   143 
   144 	/**
   145 	 * Sets an error state on the plugin
   146 	 */
   147 	void SetErrorState(PluginStatus status, const char *error_fmt, ...);
   148 
   149 	/**
   150 	 * Initializes the plugin's identity information
   151 	 */
   152 	void InitIdentity();
   153 
   154 	/**
   155 	 * Calls the OnPluginLoad function, and sets any failed states if necessary.
   156 	 * NOTE: Valid pre-states are: Plugin_Created
   157 	 * NOTE: If validated, plugin state is changed to Plugin_Loaded
   158 	 *
   159 	 * If the error buffer is NULL, the error message is cached locally.
   160 	 */
   161 	bool Call_AskPluginLoad(char *error, size_t maxlength);
   162 
   163 	/**
   164 	 * Calls the OnPluginStart function.
   165 	 * NOTE: Valid pre-states are: Plugin_Created
   166 	 * NOTE: Post-state will be Plugin_Running
   167 	 */
   168 	void Call_OnPluginStart();
   169 
   170 	/**
   171 	 * Calls the OnPluginEnd function.
   172 	 */
   173 	void Call_OnPluginEnd();
   174 
   175 	/**
   176 	 * Toggles debug mode in the plugin
   177 	 */
   178 	bool ToggleDebugMode(bool debug, char *error, size_t maxlength);
   179 
   180 	/**
   181 	 * Returns true if a plugin is usable.
   182 	 */
   183 	bool IsRunnable() const;
   184 
   185 	/**
   186 	 * Adds a language file index to the plugin's list.
   187 	 */
   188 	void AddLangFile(unsigned int index);
   189 
   190 	/**
   191 	 * Get language file count for this plugin.
   192 	 */
   193 	size_t GetLangFileCount() const;
   194 
   195 	/**
   196 	 * Get language file index based on the vector index.
   197 	 */
   198 	unsigned int GetLangFileByIndex(unsigned int index) const;
   199 public:
   200 	/**
   201 	 * Returns the modification time during last plugin load.
   202 	 */
   203 	time_t GetTimeStamp() const;
   204 
   205 	/**
   206 	 * Returns the current modification time of the plugin file.
   207 	 */
   208 	time_t GetFileTimeStamp();
   209 
   210 	/** 
   211 	 * Returns true if the plugin was running, but is now invalid.
   212 	 */
   213 	bool WasRunning();
   214 
   215 	Handle_t GetMyHandle();
   216 protected:
   217 	void UpdateInfo();
   218 	void SetTimeStamp(time_t t);
   219 private:
   220 	ContextPair m_ctx;
   221 	PluginType m_type;
   222 	char m_filename[PLATFORM_MAX_PATH+1];
   223 	PluginStatus m_status;
   224 	unsigned int m_serial;
   225 	sm_plugininfo_t m_info;
   226 	sp_plugin_t *m_plugin;
   227 	char m_errormsg[256];
   228 	time_t m_LastAccess;
   229 	IdentityToken_t *m_ident;
   230 	Handle_t m_handle;
   231 	bool m_WasRunning;
   232 	CVector<unsigned int> m_PhraseFiles;
   233 	Trie *m_pProps;
   234 };
   235 
   236 struct FakeNative
   237 {
   238 	IPluginContext *ctx;
   239 	IPluginFunction *call;
   240 	String name;
   241 	SPVM_NATIVE_FUNC func;
   242 };
   243 
   244 class CPluginManager : 
   245 	public IPluginManager,
   246 	public SMGlobalClass,
   247 	public IHandleTypeDispatch,
   248 	public IRootConsoleCommand
   249 {
   250 	friend class CPlugin;
   251 public:
   252 	CPluginManager();
   253 	~CPluginManager();
   254 public:
   255 	/* Implements iterator class */
   256 	class CPluginIterator : public IPluginIterator
   257 	{
   258 	public:
   259 		CPluginIterator(List<CPlugin *> *mylist);
   260 		virtual ~CPluginIterator();
   261 		virtual bool MorePlugins();
   262 		virtual IPlugin *GetPlugin();
   263 		virtual void NextPlugin();
   264 		void Release();
   265 	public:
   266 		void Reset();
   267 	private:
   268 		List<CPlugin *> *mylist;
   269 		List<CPlugin *>::iterator current;
   270 	};
   271 	friend class CPluginManager::CPluginIterator;
   272 public: //IPluginManager
   273 	IPlugin *LoadPlugin(const char *path, 
   274 								bool debug,
   275 								PluginType type,
   276 								char error[],
   277 								size_t err_max,
   278 								bool *wasloaded);
   279 	bool UnloadPlugin(IPlugin *plugin);
   280 	IPlugin *FindPluginByContext(const sp_context_t *ctx);
   281 	unsigned int GetPluginCount();
   282 	IPluginIterator *GetPluginIterator();
   283 	void AddPluginsListener(IPluginsListener *listener);
   284 	void RemovePluginsListener(IPluginsListener *listener);
   285 public: //SMGlobalClass
   286 	void OnSourceModAllInitialized();
   287 	void OnSourceModShutdown();
   288 public: //IHandleTypeDispatch
   289 	void OnHandleDestroy(HandleType_t type, void *object);
   290 public: //IRootConsoleCommand
   291 	void OnRootConsoleCommand(const char *command, unsigned int argcount);
   292 public:
   293 	/**
   294 	 * Loads all plugins not yet loaded
   295 	 */
   296 	void LoadAll_FirstPass(const char *config, const char *basedir);
   297 
   298 	/**
   299 	 * Runs the second loading pass for all plugins
   300 	 */
   301 	void LoadAll_SecondPass();
   302 
   303 	/**
   304 	 * Tests a plugin file mask against a local folder.
   305 	 * The alias is searched backwards from localdir - i.e., given this input:
   306 	 *   csdm/ban        csdm/ban
   307 	 *   ban             csdm/ban
   308 	 *   csdm/ban        optional/csdm/ban
   309 	 * All of these will return true for an alias match.  
   310 	 * Wildcards are allowed in the filename.
   311 	 */
   312 	bool TestAliasMatch(const char *alias, const char *localdir);
   313 
   314 	/** 
   315 	 * Returns whether anything loaded will be a late load.
   316 	 */
   317 	bool IsLateLoadTime() const;
   318 
   319 	/**
   320 	 * Adds natives from core into the native pool.
   321 	 */
   322 	void RegisterNativesFromCore(sp_nativeinfo_t *natives);
   323 
   324 	/**
   325 	 * Converts a Handle to an IPlugin if possible.
   326 	 */
   327 	IPlugin *PluginFromHandle(Handle_t handle, HandleError *err);
   328 
   329 	/**
   330 	 * Finds a plugin based on its index. (starts on index 1)
   331 	 */
   332 	CPlugin *GetPluginByOrder(int num);
   333 
   334 	/** 
   335 	 * Internal version of FindPluginByContext()
   336 	 */
   337 	inline CPlugin *GetPluginByCtx(const sp_context_t *ctx)
   338 	{
   339 		return reinterpret_cast<CPlugin *>(ctx->user[SM_CONTEXTVAR_MYSELF]);
   340 	}
   341 
   342 	/**
   343 	 * Gets status text for a status code 
   344 	 */
   345 	const char *GetStatusText(PluginStatus status);
   346 
   347 	/**
   348 	* Reload or update plugins on level shutdown.
   349 	*/
   350 	void ReloadOrUnloadPlugins();
   351 
   352 private:
   353 	LoadRes _LoadPlugin(CPlugin **pPlugin, const char *path, bool debug, PluginType type, char error[], size_t err_max);
   354 
   355 	void LoadAutoPlugin(const char *plugin);
   356 
   357 	/**
   358 	 * Recursively loads all plugins in the given directory.
   359 	 */
   360 	void LoadPluginsFromDir(const char *basedir, const char *localdir);
   361 
   362 	/**
   363 	 * Adds a plugin object.  This is wrapped by LoadPlugin functions.
   364 	 */
   365 	void AddPlugin(CPlugin *pPlugin);
   366 
   367 	/**
   368 	 * Runs the second loading pass on a plugin.
   369 	 */
   370 	bool RunSecondPass(CPlugin *pPlugin, char *error, size_t maxlength);
   371 
   372 	/**
   373 	 * Adds any globally registered natives to a plugin
   374 	 */
   375 	void AddCoreNativesToPlugin(CPlugin *pPlugin);
   376 
   377 	/**
   378 	 * Runs an extension pass on a plugin.
   379 	 */
   380 	bool LoadOrRequireExtensions(CPlugin *pPlugin, unsigned int pass, char *error, size_t maxlength);
   381 
   382 	void _SetPauseState(CPlugin *pPlugin, bool pause);
   383 protected:
   384 	/**
   385 	 * Caching internal objects
   386 	 */
   387 	void ReleaseIterator(CPluginIterator *iter);
   388 	inline IdentityToken_t *GetIdentity()
   389 	{
   390 		return m_MyIdent;
   391 	}
   392 public:
   393 	bool AddFakeNative(IPluginFunction *pFunction, const char *name, SPVM_FAKENATIVE_FUNC func);
   394 private:
   395 	void AddFakeNativesToPlugin(CPlugin *pPlugin);
   396 private:
   397 	List<IPluginsListener *> m_listeners;
   398 	List<CPlugin *> m_plugins;
   399 	List<sp_nativeinfo_t *> m_natives;
   400 	CStack<CPluginManager::CPluginIterator *> m_iters;
   401 	CPluginInfoDatabase m_PluginInfo;
   402 	Trie *m_LoadLookup;
   403 	bool m_AllPluginsLoaded;
   404 	IdentityToken_t *m_MyIdent;
   405 
   406 	/* Dynamic native stuff */
   407 	List<FakeNative *> m_Natives;
   408 	Trie *m_pNativeLookup;
   409 
   410 };
   411 
   412 extern CPluginManager g_PluginSys;
   413 
   414 #endif //_INCLUDE_SOURCEMOD_PLUGINSYSTEM_H_