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