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