- sourcemod now intelligently unloads plugins which are leaking insane amounts of handles. these unloads get logged to sourcemod_fatal.log
- unloading is now delayed if a plugin is in the middle of a callstack
3 * =============================================================================
5 * Copyright (C) 2004-2007 AlliedModders LLC. All rights reserved.
6 * =============================================================================
8 * This program is free software; you can redistribute it and/or modify it under
9 * the terms of the GNU General Public License, version 3.0, as published by the
10 * Free Software Foundation.
12 * This program is distributed in the hope that it will be useful, but WITHOUT
13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
17 * You should have received a copy of the GNU General Public License along with
18 * this program. If not, see <http://www.gnu.org/licenses/>.
20 * As a special exception, AlliedModders LLC gives you permission to link the
21 * code of this program (as well as its derivative works) to "Half-Life 2," the
22 * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
23 * by the Valve Corporation. You must obey the GNU General Public License in
24 * all respects for all other code used. Additionally, AlliedModders LLC grants
25 * this exception to all derivative works. AlliedModders LLC defines further
26 * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
27 * or <http://www.sourcemod.net/license.php>.
32 #ifndef _INCLUDE_SOURCEMOD_PLUGINSYSTEM_H_
33 #define _INCLUDE_SOURCEMOD_PLUGINSYSTEM_H_
38 #include <IPluginSys.h>
39 #include <IHandleSys.h>
40 #include <IForwardSys.h>
43 #include <sh_vector.h>
44 #include <sh_string.h>
45 #include "sm_globals.h"
46 #include "vm/sp_vm_basecontext.h"
47 #include "PluginInfoDatabase.h"
49 #include "sourcemod.h"
50 #include <IRootConsoleMenu.h>
51 #if defined ORANGEBOX_BUILD
52 #include "convar_sm_ob.h"
54 #include "convar_sm.h"
57 using namespace SourceHook;
62 * Currently this system needs a lot of work but it's good skeletally. Plugin creation
63 * is done without actually compiling anything. This is done by Load functions in the
64 * manager. This will need a rewrite when we add context switching.
66 * The plugin object itself has a few things to note. The most important is that it stores
67 * a table of function objects. The manager marshals allocation and freeing of these objects.
68 * The plugin object can be in erroneous states, they are:
69 * Plugin_Error --> Some error occurred any time during or after compilation.
70 * This error can be cleared since the plugin itself is valid.
71 * However, the state itself being set prevents any runtime action.
72 * Plugin_BadLoad --> The plugin failed to load entirely and nothing can be done to save it.
74 * If a plugin fails to load externally, it is never added to the internal tracker. However,
75 * plugins that failed to load from the internal loading mechanism are always tracked. This
76 * allows users to see which automatically loaded plugins failed, and makes the interface a bit
79 * Once a plugin is compiled, it sets its own state to Plugin_Created. This state is still invalid
80 * for execution. SourceMod is a two pass system, and even though the second pass is not implemented
81 * yet, it is structured so Plugin_Created must be switched to Plugin_Running in the second pass. When
82 * implemented, a Created plugin will be switched to Error in the second pass if it not loadable.
84 * The two pass loading mechanism is described below. Modules/natives are not implemented yet.
85 * PASS ONE: All loadable plugins are found and have the following steps performed:
86 * 1. Loading and compilation is attempted.
87 * 2. If successful, all natives from Core are added.
88 * 3. OnPluginLoad() is called.
89 * 4. If failed, any user natives are scrapped and the process halts here.
90 * 5. If successful, the plugin is ready for Pass 2.
92 * 1. All forced modules are loaded.
93 * PASS TWO: All loaded plugins are found and have these steps performed:
94 * 1. Any modules referenced in the plugin that are not already loaded, are loaded.
95 * 2. If any module fails to load and the plugin requires it, load fails and jump to step 6.
96 * 3. If any natives are unresolved, check if they are found in the user-natives pool.
97 * 4. If yes, load succeeds. If not, natives are passed through a native acceptance filter.
98 * 5. If the filter fails, the plugin is marked as failed.
99 * 6. If the plugin has failed to load at this point, any dynamic natives it has added are scrapped.
100 * Furthermore, any plugin that referenced these natives must now have pass 2 re-ran.
101 * PASS THREE (not a real pass):
102 * 7. Once all plugins are deemed to be loaded, OnPluginStart() is called
105 #define SM_CONTEXTVAR_MYSELF 0
109 ContextPair() : base(NULL), ctx(NULL), co(NULL)
121 IPluginFunction *call;
123 SPVM_NATIVE_FUNC func;
129 LoadRes_AlreadyLoaded,
144 WeakNative(CPlugin *plugin, uint32_t index)
153 class CPlugin : public IPlugin
155 friend class CPluginManager;
156 friend class CFunction;
158 CPlugin(const char *file);
161 PluginType GetType();
162 SourcePawn::IPluginContext *GetBaseContext();
163 sp_context_t *GetContext();
164 const sm_plugininfo_t *GetPublicInfo();
165 const char *GetFilename();
167 PluginStatus GetStatus();
168 bool SetPauseState(bool paused);
169 unsigned int GetSerial();
170 const sp_plugin_t *GetPluginStructure();
171 IdentityToken_t *GetIdentity();
172 bool SetProperty(const char *prop, void *ptr);
173 bool GetProperty(const char *prop, void **ptr, bool remove=false);
176 * Creates a plugin object with default values.
177 * If an error buffer is specified, and an error occurs, the error will be copied to the buffer
178 * and NULL will be returned.
179 * If an error buffer is not specified, the error will be copied to an internal buffer and
180 * a valid (but error-stated) CPlugin will be returned.
182 static CPlugin *CreatePlugin(const char *file, char *error, size_t maxlength);
185 * Starts the initial compilation of a plugin.
186 * Returns false if another compilation exists or there is a current context set.
188 ICompilation *StartMyCompile(IVirtualMachine *vm);
190 * Finalizes a compilation. If error buffer is NULL, the error is saved locally.
192 bool FinishMyCompile(char *error, size_t maxlength);
193 void CancelMyCompile();
196 * Sets an error state on the plugin
198 void SetErrorState(PluginStatus status, const char *error_fmt, ...);
201 * Initializes the plugin's identity information
206 * Calls the OnPluginLoad function, and sets any failed states if necessary.
207 * NOTE: Valid pre-states are: Plugin_Created
208 * NOTE: If validated, plugin state is changed to Plugin_Loaded
210 * If the error buffer is NULL, the error message is cached locally.
212 bool Call_AskPluginLoad(char *error, size_t maxlength);
215 * Calls the OnPluginStart function.
216 * NOTE: Valid pre-states are: Plugin_Created
217 * NOTE: Post-state will be Plugin_Running
219 void Call_OnPluginStart();
222 * Calls the OnPluginEnd function.
224 void Call_OnPluginEnd();
227 * Calls the OnAllPluginsLoaded function.
229 void Call_OnAllPluginsLoaded();
232 * Toggles debug mode in the plugin
234 bool ToggleDebugMode(bool debug, char *error, size_t maxlength);
237 * Returns true if a plugin is usable.
242 * Adds a language file index to the plugin's list.
244 void AddLangFile(unsigned int index);
247 * Get language file count for this plugin.
249 size_t GetLangFileCount();
252 * Get language file index based on the vector index.
254 unsigned int GetLangFileByIndex(unsigned int index);
257 * Returns the modification time during last plugin load.
259 time_t GetTimeStamp();
262 * Returns the current modification time of the plugin file.
264 time_t GetFileTimeStamp();
267 * Returns true if the plugin was running, but is now invalid.
271 Handle_t GetMyHandle();
273 void AddConfig(bool autoCreate, const char *cfg, const char *folder);
274 unsigned int GetConfigCount();
275 AutoConfig *GetConfig(unsigned int i);
276 inline void AddLibrary(const char *name)
278 m_Libraries.push_back(name);
280 void LibraryActions(bool dropping);
283 void SetTimeStamp(time_t t);
284 void DependencyDropped(CPlugin *pOwner);
288 char m_filename[PLATFORM_MAX_PATH];
289 PluginStatus m_status;
290 unsigned int m_serial;
291 sm_plugininfo_t m_info;
292 sp_plugin_t *m_plugin;
293 char m_errormsg[256];
295 IdentityToken_t *m_ident;
298 CVector<unsigned int> m_PhraseFiles;
299 List<CPlugin *> m_dependents;
300 List<CPlugin *> m_dependsOn;
301 List<FakeNative *> m_fakeNatives;
302 List<WeakNative> m_WeakNatives;
303 List<String> m_RequiredLibs;
304 List<String> m_Libraries;
306 bool m_FakeNativesMissing;
307 bool m_LibraryMissing;
308 CVector<AutoConfig *> m_configs;
309 bool m_bGotAllLoaded;
312 class CPluginManager :
313 public IPluginManager,
314 public SMGlobalClass,
315 public IHandleTypeDispatch,
316 public IRootConsoleCommand
318 friend class CPlugin;
323 /* Implements iterator class */
324 class CPluginIterator : public IPluginIterator
327 CPluginIterator(List<CPlugin *> *mylist);
328 virtual ~CPluginIterator();
329 virtual bool MorePlugins();
330 virtual IPlugin *GetPlugin();
331 virtual void NextPlugin();
336 List<CPlugin *> *mylist;
337 List<CPlugin *>::iterator current;
339 friend class CPluginManager::CPluginIterator;
340 public: //IPluginManager
341 IPlugin *LoadPlugin(const char *path,
347 bool UnloadPlugin(IPlugin *plugin);
348 IPlugin *FindPluginByContext(const sp_context_t *ctx);
349 unsigned int GetPluginCount();
350 IPluginIterator *GetPluginIterator();
351 void AddPluginsListener(IPluginsListener *listener);
352 void RemovePluginsListener(IPluginsListener *listener);
353 public: //SMGlobalClass
354 void OnSourceModAllInitialized();
355 void OnSourceModShutdown();
356 public: //IHandleTypeDispatch
357 void OnHandleDestroy(HandleType_t type, void *object);
358 public: //IRootConsoleCommand
359 void OnRootConsoleCommand(const char *cmdname, const CCommand &command);
362 * Loads all plugins not yet loaded
364 void LoadAll_FirstPass(const char *config, const char *basedir);
367 * Runs the second loading pass for all plugins
369 void LoadAll_SecondPass();
372 * Tests a plugin file mask against a local folder.
373 * The alias is searched backwards from localdir - i.e., given this input:
376 * csdm/ban optional/csdm/ban
377 * All of these will return true for an alias match.
378 * Wildcards are allowed in the filename.
380 bool TestAliasMatch(const char *alias, const char *localdir);
383 * Returns whether anything loaded will be a late load.
385 bool IsLateLoadTime() const;
388 * Adds natives from core into the native pool.
390 void RegisterNativesFromCore(sp_nativeinfo_t *natives);
393 * Converts a Handle to an IPlugin if possible.
395 IPlugin *PluginFromHandle(Handle_t handle, HandleError *err);
398 * Finds a plugin based on its index. (starts on index 1)
400 CPlugin *GetPluginByOrder(int num);
402 int GetOrderOfPlugin(IPlugin *pl);
405 * Internal version of FindPluginByContext()
407 inline CPlugin *GetPluginByCtx(const sp_context_t *ctx)
409 return reinterpret_cast<CPlugin *>(ctx->user[SM_CONTEXTVAR_MYSELF]);
413 * Gets status text for a status code
415 const char *GetStatusText(PluginStatus status);
418 * Reload or update plugins on level shutdown.
420 void ReloadOrUnloadPlugins();
423 * Add public functions from all running or paused
424 * plugins to the specified forward if the names match.
426 void AddFunctionsToForward(const char *name, IChangeableForward *pForward);
429 * Iterates through plugins to call OnAllPluginsLoaded.
431 void AllPluginsLoaded();
433 CPlugin *GetPluginFromIdentity(IdentityToken_t *pToken);
437 void OnLibraryAction(const char *lib, bool is_a_plugin, bool drop);
439 bool LibraryExists(const char *lib);
441 bool ReloadPlugin(CPlugin *pl);
445 LoadRes _LoadPlugin(CPlugin **pPlugin, const char *path, bool debug, PluginType type, char error[], size_t maxlength);
447 void LoadAutoPlugin(const char *plugin);
450 * Recursively loads all plugins in the given directory.
452 void LoadPluginsFromDir(const char *basedir, const char *localdir);
455 * Adds a plugin object. This is wrapped by LoadPlugin functions.
457 void AddPlugin(CPlugin *pPlugin);
460 * Runs the second loading pass on a plugin.
462 bool RunSecondPass(CPlugin *pPlugin, char *error, size_t maxlength);
465 * Adds any globally registered natives to a plugin
467 void AddCoreNativesToPlugin(CPlugin *pPlugin);
470 * Runs an extension pass on a plugin.
472 bool LoadOrRequireExtensions(CPlugin *pPlugin, unsigned int pass, char *error, size_t maxlength);
475 * Manages required natives.
477 bool FindOrRequirePluginDeps(CPlugin *pPlugin, char *error, size_t maxlength);
479 void _SetPauseState(CPlugin *pPlugin, bool pause);
482 * Caching internal objects
484 void ReleaseIterator(CPluginIterator *iter);
486 inline IdentityToken_t *GetIdentity()
491 bool AddFakeNative(IPluginFunction *pFunction, const char *name, SPVM_FAKENATIVE_FUNC func);
493 void AddFakeNativesToPlugin(CPlugin *pPlugin);
494 void TryRefreshDependencies(CPlugin *pOther);
496 List<IPluginsListener *> m_listeners;
497 List<CPlugin *> m_plugins;
498 CStack<CPluginManager::CPluginIterator *> m_iters;
499 CPluginInfoDatabase m_PluginInfo;
501 bool m_AllPluginsLoaded;
502 IdentityToken_t *m_MyIdent;
503 Trie *m_pCoreNatives;
505 /* Dynamic native stuff */
506 List<FakeNative *> m_Natives;
507 Trie *m_pNativeLookup;
509 bool m_LoadingLocked;
512 extern CPluginManager g_PluginSys;
514 #endif //_INCLUDE_SOURCEMOD_PLUGINSYSTEM_H_