|
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_ |