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