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