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