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