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