1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/core/systems/PluginSys.h Sun Jul 06 04:49:55 2008 +0000
1.3 @@ -0,0 +1,484 @@
1.4 +/**
1.5 + * vim: set ts=4 :
1.6 + * =============================================================================
1.7 + * SourceMod
1.8 + * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
1.9 + * =============================================================================
1.10 + *
1.11 + * This program is free software; you can redistribute it and/or modify it under
1.12 + * the terms of the GNU General Public License, version 3.0, as published by the
1.13 + * Free Software Foundation.
1.14 + *
1.15 + * This program is distributed in the hope that it will be useful, but WITHOUT
1.16 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
1.17 + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
1.18 + * details.
1.19 + *
1.20 + * You should have received a copy of the GNU General Public License along with
1.21 + * this program. If not, see <http://www.gnu.org/licenses/>.
1.22 + *
1.23 + * As a special exception, AlliedModders LLC gives you permission to link the
1.24 + * code of this program (as well as its derivative works) to "Half-Life 2," the
1.25 + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
1.26 + * by the Valve Corporation. You must obey the GNU General Public License in
1.27 + * all respects for all other code used. Additionally, AlliedModders LLC grants
1.28 + * this exception to all derivative works. AlliedModders LLC defines further
1.29 + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
1.30 + * or <http://www.sourcemod.net/license.php>.
1.31 + *
1.32 + * Version: $Id$
1.33 + */
1.34 +
1.35 +#ifndef _INCLUDE_SOURCEMOD_PLUGINSYSTEM_H_
1.36 +#define _INCLUDE_SOURCEMOD_PLUGINSYSTEM_H_
1.37 +
1.38 +#include <time.h>
1.39 +#include <errno.h>
1.40 +#include <sys/stat.h>
1.41 +#include <IPluginSys.h>
1.42 +#include <IHandleSys.h>
1.43 +#include <IForwardSys.h>
1.44 +#include <sh_list.h>
1.45 +#include <sh_stack.h>
1.46 +#include <sh_vector.h>
1.47 +#include <sh_string.h>
1.48 +#include "sm_globals.h"
1.49 +#include "vm/sp_vm_basecontext.h"
1.50 +#include "PluginInfoDatabase.h"
1.51 +#include "sm_trie.h"
1.52 +#include "sourcemod.h"
1.53 +#include <IRootConsoleMenu.h>
1.54 +#if defined ORANGEBOX_BUILD
1.55 +#include "convar_sm_ob.h"
1.56 +#else
1.57 +#include "convar_sm.h"
1.58 +#endif
1.59 +#include "ITranslator.h"
1.60 +#include "NativeOwner.h"
1.61 +#include "ShareSys.h"
1.62 +
1.63 +using namespace SourceHook;
1.64 +
1.65 +/**
1.66 + * NOTES:
1.67 + *
1.68 + * UPDATE 2008-03-11: These comments are horribly out of date. They paint a good overall
1.69 + * picture of how PluginSys works, but things like dependencies and fake natives have
1.70 + * complicated things quite a bit.
1.71 + *
1.72 + * Currently this system needs a lot of work but it's good skeletally. Plugin creation
1.73 + * is done without actually compiling anything. This is done by Load functions in the
1.74 + * manager. This will need a rewrite when we add context switching.
1.75 + *
1.76 + * The plugin object itself has a few things to note. The most important is that it stores
1.77 + * a table of function objects. The manager marshals allocation and freeing of these objects.
1.78 + * The plugin object can be in erroneous states, they are:
1.79 + * Plugin_Error --> Some error occurred any time during or after compilation.
1.80 + * This error can be cleared since the plugin itself is valid.
1.81 + * However, the state itself being set prevents any runtime action.
1.82 + * Plugin_BadLoad --> The plugin failed to load entirely and nothing can be done to save it.
1.83 + *
1.84 + * If a plugin fails to load externally, it is never added to the internal tracker. However,
1.85 + * plugins that failed to load from the internal loading mechanism are always tracked. This
1.86 + * allows users to see which automatically loaded plugins failed, and makes the interface a bit
1.87 + * more flexible.
1.88 + *
1.89 + * Once a plugin is compiled, it sets its own state to Plugin_Created. This state is still invalid
1.90 + * for execution. SourceMod is a two pass system, and even though the second pass is not implemented
1.91 + * yet, it is structured so Plugin_Created must be switched to Plugin_Running in the second pass. When
1.92 + * implemented, a Created plugin will be switched to Error in the second pass if it not loadable.
1.93 + *
1.94 + * The two pass loading mechanism is described below. Modules/natives are not implemented yet.
1.95 + * PASS ONE: All loadable plugins are found and have the following steps performed:
1.96 + * 1. Loading and compilation is attempted.
1.97 + * 2. If successful, all natives from Core are added.
1.98 + * 3. OnPluginLoad() is called.
1.99 + * 4. If failed, any user natives are scrapped and the process halts here.
1.100 + * 5. If successful, the plugin is ready for Pass 2.
1.101 + * INTERMEDIATE:
1.102 + * 1. All forced modules are loaded.
1.103 + * PASS TWO: All loaded plugins are found and have these steps performed:
1.104 + * 1. Any modules referenced in the plugin that are not already loaded, are loaded.
1.105 + * 2. If any module fails to load and the plugin requires it, load fails and jump to step 6.
1.106 + * 3. If any natives are unresolved, check if they are found in the user-natives pool.
1.107 + * 4. If yes, load succeeds. If not, natives are passed through a native acceptance filter.
1.108 + * 5. If the filter fails, the plugin is marked as failed.
1.109 + * 6. If the plugin has failed to load at this point, any dynamic natives it has added are scrapped.
1.110 + * Furthermore, any plugin that referenced these natives must now have pass 2 re-ran.
1.111 + * PASS THREE (not a real pass):
1.112 + * 7. Once all plugins are deemed to be loaded, OnPluginStart() is called
1.113 + */
1.114 +
1.115 +#define SM_CONTEXTVAR_MYSELF 0
1.116 +
1.117 +struct ContextPair
1.118 +{
1.119 + ContextPair() : base(NULL), ctx(NULL), co(NULL)
1.120 + {
1.121 + };
1.122 + BaseContext *base;
1.123 + sp_context_t *ctx;
1.124 + ICompilation *co;
1.125 + IVirtualMachine *vm;
1.126 +};
1.127 +
1.128 +enum LoadRes
1.129 +{
1.130 + LoadRes_Successful,
1.131 + LoadRes_AlreadyLoaded,
1.132 + LoadRes_Failure,
1.133 + LoadRes_NeverLoad
1.134 +};
1.135 +
1.136 +struct AutoConfig
1.137 +{
1.138 + String autocfg;
1.139 + String folder;
1.140 + bool create;
1.141 +};
1.142 +
1.143 +class CPlugin;
1.144 +
1.145 +class CPlugin :
1.146 + public IPlugin,
1.147 + public CNativeOwner
1.148 +{
1.149 + friend class CPluginManager;
1.150 + friend class CFunction;
1.151 +public:
1.152 + CPlugin(const char *file);
1.153 + ~CPlugin();
1.154 +public:
1.155 + PluginType GetType();
1.156 + SourcePawn::IPluginContext *GetBaseContext();
1.157 + sp_context_t *GetContext();
1.158 + const sm_plugininfo_t *GetPublicInfo();
1.159 + const char *GetFilename();
1.160 + bool IsDebugging();
1.161 + PluginStatus GetStatus();
1.162 + bool SetPauseState(bool paused);
1.163 + unsigned int GetSerial();
1.164 + const sp_plugin_t *GetPluginStructure();
1.165 + IdentityToken_t *GetIdentity();
1.166 + unsigned int CalcMemUsage();
1.167 + bool SetProperty(const char *prop, void *ptr);
1.168 + bool GetProperty(const char *prop, void **ptr, bool remove=false);
1.169 + void DropEverything();
1.170 +public:
1.171 + /**
1.172 + * Creates a plugin object with default values.
1.173 + * If an error buffer is specified, and an error occurs, the error will be copied to the buffer
1.174 + * and NULL will be returned.
1.175 + * If an error buffer is not specified, the error will be copied to an internal buffer and
1.176 + * a valid (but error-stated) CPlugin will be returned.
1.177 + */
1.178 + static CPlugin *CreatePlugin(const char *file, char *error, size_t maxlength);
1.179 +public:
1.180 + /**
1.181 + * Starts the initial compilation of a plugin.
1.182 + * Returns false if another compilation exists or there is a current context set.
1.183 + */
1.184 + ICompilation *StartMyCompile(IVirtualMachine *vm);
1.185 + /**
1.186 + * Finalizes a compilation. If error buffer is NULL, the error is saved locally.
1.187 + */
1.188 + bool FinishMyCompile(char *error, size_t maxlength);
1.189 + void CancelMyCompile();
1.190 +
1.191 + /**
1.192 + * Sets an error state on the plugin
1.193 + */
1.194 + void SetErrorState(PluginStatus status, const char *error_fmt, ...);
1.195 +
1.196 + /**
1.197 + * Initializes the plugin's identity information
1.198 + */
1.199 + void InitIdentity();
1.200 +
1.201 + /**
1.202 + * Calls the OnPluginLoad function, and sets any failed states if necessary.
1.203 + * NOTE: Valid pre-states are: Plugin_Created
1.204 + * NOTE: If validated, plugin state is changed to Plugin_Loaded
1.205 + *
1.206 + * If the error buffer is NULL, the error message is cached locally.
1.207 + */
1.208 + bool Call_AskPluginLoad(char *error, size_t maxlength);
1.209 +
1.210 + /**
1.211 + * Calls the OnPluginStart function.
1.212 + * NOTE: Valid pre-states are: Plugin_Created
1.213 + * NOTE: Post-state will be Plugin_Running
1.214 + */
1.215 + void Call_OnPluginStart();
1.216 +
1.217 + /**
1.218 + * Calls the OnPluginEnd function.
1.219 + */
1.220 + void Call_OnPluginEnd();
1.221 +
1.222 + /**
1.223 + * Calls the OnAllPluginsLoaded function.
1.224 + */
1.225 + void Call_OnAllPluginsLoaded();
1.226 +
1.227 + /**
1.228 + * Toggles debug mode in the plugin
1.229 + */
1.230 + bool ToggleDebugMode(bool debug, char *error, size_t maxlength);
1.231 +
1.232 + /**
1.233 + * Returns true if a plugin is usable.
1.234 + */
1.235 + bool IsRunnable();
1.236 +
1.237 + /**
1.238 + * Get languages info.
1.239 + */
1.240 + IPhraseCollection *GetPhrases();
1.241 +public:
1.242 + /**
1.243 + * Returns the modification time during last plugin load.
1.244 + */
1.245 + time_t GetTimeStamp();
1.246 +
1.247 + /**
1.248 + * Returns the current modification time of the plugin file.
1.249 + */
1.250 + time_t GetFileTimeStamp();
1.251 +
1.252 + /**
1.253 + * Returns true if the plugin was running, but is now invalid.
1.254 + */
1.255 + bool WasRunning();
1.256 +
1.257 + Handle_t GetMyHandle();
1.258 +
1.259 + bool AddFakeNative(IPluginFunction *pFunc, const char *name, SPVM_FAKENATIVE_FUNC func);
1.260 + void AddConfig(bool autoCreate, const char *cfg, const char *folder);
1.261 + unsigned int GetConfigCount();
1.262 + AutoConfig *GetConfig(unsigned int i);
1.263 + inline void AddLibrary(const char *name)
1.264 + {
1.265 + m_Libraries.push_back(name);
1.266 + }
1.267 + void LibraryActions(bool dropping);
1.268 +protected:
1.269 + void UpdateInfo();
1.270 + void SetTimeStamp(time_t t);
1.271 + void DependencyDropped(CPlugin *pOwner);
1.272 +private:
1.273 + ContextPair m_ctx;
1.274 + PluginType m_type;
1.275 + char m_filename[PLATFORM_MAX_PATH];
1.276 + PluginStatus m_status;
1.277 + unsigned int m_serial;
1.278 + sm_plugininfo_t m_info;
1.279 + sp_plugin_t *m_plugin;
1.280 + char m_errormsg[256];
1.281 + time_t m_LastAccess;
1.282 + IdentityToken_t *m_ident;
1.283 + Handle_t m_handle;
1.284 + bool m_WasRunning;
1.285 + IPhraseCollection *m_pPhrases;
1.286 + List<String> m_RequiredLibs;
1.287 + List<String> m_Libraries;
1.288 + Trie *m_pProps;
1.289 + bool m_FakeNativesMissing;
1.290 + bool m_LibraryMissing;
1.291 + CVector<AutoConfig *> m_configs;
1.292 + bool m_bGotAllLoaded;
1.293 + int m_FileVersion;
1.294 + char m_DateTime[256];
1.295 +};
1.296 +
1.297 +class CPluginManager :
1.298 + public IPluginManager,
1.299 + public SMGlobalClass,
1.300 + public IHandleTypeDispatch,
1.301 + public IRootConsoleCommand
1.302 +{
1.303 + friend class CPlugin;
1.304 +public:
1.305 + CPluginManager();
1.306 + ~CPluginManager();
1.307 +public:
1.308 + /* Implements iterator class */
1.309 + class CPluginIterator : public IPluginIterator
1.310 + {
1.311 + public:
1.312 + CPluginIterator(List<CPlugin *> *mylist);
1.313 + virtual ~CPluginIterator();
1.314 + virtual bool MorePlugins();
1.315 + virtual IPlugin *GetPlugin();
1.316 + virtual void NextPlugin();
1.317 + void Release();
1.318 + public:
1.319 + void Reset();
1.320 + private:
1.321 + List<CPlugin *> *mylist;
1.322 + List<CPlugin *>::iterator current;
1.323 + };
1.324 + friend class CPluginManager::CPluginIterator;
1.325 +public: //IPluginManager
1.326 + IPlugin *LoadPlugin(const char *path,
1.327 + bool debug,
1.328 + PluginType type,
1.329 + char error[],
1.330 + size_t maxlength,
1.331 + bool *wasloaded);
1.332 + bool UnloadPlugin(IPlugin *plugin);
1.333 + IPlugin *FindPluginByContext(const sp_context_t *ctx);
1.334 + unsigned int GetPluginCount();
1.335 + IPluginIterator *GetPluginIterator();
1.336 + void AddPluginsListener(IPluginsListener *listener);
1.337 + void RemovePluginsListener(IPluginsListener *listener);
1.338 +public: //SMGlobalClass
1.339 + void OnSourceModAllInitialized();
1.340 + void OnSourceModShutdown();
1.341 +public: //IHandleTypeDispatch
1.342 + void OnHandleDestroy(HandleType_t type, void *object);
1.343 + bool GetHandleApproxSize(HandleType_t type, void *object, unsigned int *pSize);
1.344 +public: //IRootConsoleCommand
1.345 + void OnRootConsoleCommand(const char *cmdname, const CCommand &command);
1.346 +public:
1.347 + /**
1.348 + * Loads all plugins not yet loaded
1.349 + */
1.350 + void LoadAll_FirstPass(const char *config, const char *basedir);
1.351 +
1.352 + /**
1.353 + * Runs the second loading pass for all plugins
1.354 + */
1.355 + void LoadAll_SecondPass();
1.356 +
1.357 + /**
1.358 + * Tests a plugin file mask against a local folder.
1.359 + * The alias is searched backwards from localdir - i.e., given this input:
1.360 + * csdm/ban csdm/ban
1.361 + * ban csdm/ban
1.362 + * csdm/ban optional/csdm/ban
1.363 + * All of these will return true for an alias match.
1.364 + * Wildcards are allowed in the filename.
1.365 + */
1.366 + bool TestAliasMatch(const char *alias, const char *localdir);
1.367 +
1.368 + /**
1.369 + * Returns whether anything loaded will be a late load.
1.370 + */
1.371 + bool IsLateLoadTime() const;
1.372 +
1.373 + /**
1.374 + * Converts a Handle to an IPlugin if possible.
1.375 + */
1.376 + IPlugin *PluginFromHandle(Handle_t handle, HandleError *err);
1.377 +
1.378 + /**
1.379 + * Finds a plugin based on its index. (starts on index 1)
1.380 + */
1.381 + CPlugin *GetPluginByOrder(int num);
1.382 +
1.383 + int GetOrderOfPlugin(IPlugin *pl);
1.384 +
1.385 + /**
1.386 + * Internal version of FindPluginByContext()
1.387 + */
1.388 + inline CPlugin *GetPluginByCtx(const sp_context_t *ctx)
1.389 + {
1.390 + return reinterpret_cast<CPlugin *>(ctx->user[SM_CONTEXTVAR_MYSELF]);
1.391 + }
1.392 +
1.393 + /**
1.394 + * Gets status text for a status code
1.395 + */
1.396 + const char *GetStatusText(PluginStatus status);
1.397 +
1.398 + /**
1.399 + * Reload or update plugins on level shutdown.
1.400 + */
1.401 + void ReloadOrUnloadPlugins();
1.402 +
1.403 + /**
1.404 + * Add public functions from all running or paused
1.405 + * plugins to the specified forward if the names match.
1.406 + */
1.407 + void AddFunctionsToForward(const char *name, IChangeableForward *pForward);
1.408 +
1.409 + /**
1.410 + * Iterates through plugins to call OnAllPluginsLoaded.
1.411 + */
1.412 + void AllPluginsLoaded();
1.413 +
1.414 + CPlugin *GetPluginFromIdentity(IdentityToken_t *pToken);
1.415 +
1.416 + void Shutdown();
1.417 +
1.418 + void OnLibraryAction(const char *lib, bool is_a_plugin, bool drop);
1.419 +
1.420 + bool LibraryExists(const char *lib);
1.421 +
1.422 + bool ReloadPlugin(CPlugin *pl);
1.423 +
1.424 + void UnloadAll();
1.425 +
1.426 + CPlugin *FindPluginByConsoleArg(const char *arg);
1.427 +private:
1.428 + LoadRes _LoadPlugin(CPlugin **pPlugin, const char *path, bool debug, PluginType type, char error[], size_t maxlength);
1.429 +
1.430 + void LoadAutoPlugin(const char *plugin);
1.431 +
1.432 + /**
1.433 + * Recursively loads all plugins in the given directory.
1.434 + */
1.435 + void LoadPluginsFromDir(const char *basedir, const char *localdir);
1.436 +
1.437 + /**
1.438 + * Adds a plugin object. This is wrapped by LoadPlugin functions.
1.439 + */
1.440 + void AddPlugin(CPlugin *pPlugin);
1.441 +
1.442 + /**
1.443 + * Runs the second loading pass on a plugin.
1.444 + */
1.445 + bool RunSecondPass(CPlugin *pPlugin, char *error, size_t maxlength);
1.446 +
1.447 + /**
1.448 + * Runs an extension pass on a plugin.
1.449 + */
1.450 + bool LoadOrRequireExtensions(CPlugin *pPlugin, unsigned int pass, char *error, size_t maxlength);
1.451 +
1.452 + /**
1.453 + * Manages required natives.
1.454 + */
1.455 + bool FindOrRequirePluginDeps(CPlugin *pPlugin, char *error, size_t maxlength);
1.456 +
1.457 + void _SetPauseState(CPlugin *pPlugin, bool pause);
1.458 +protected:
1.459 + /**
1.460 + * Caching internal objects
1.461 + */
1.462 + void ReleaseIterator(CPluginIterator *iter);
1.463 +public:
1.464 + inline IdentityToken_t *GetIdentity()
1.465 + {
1.466 + return m_MyIdent;
1.467 + }
1.468 +private:
1.469 + void TryRefreshDependencies(CPlugin *pOther);
1.470 +private:
1.471 + List<IPluginsListener *> m_listeners;
1.472 + List<CPlugin *> m_plugins;
1.473 + CStack<CPluginManager::CPluginIterator *> m_iters;
1.474 + CPluginInfoDatabase m_PluginInfo;
1.475 + Trie *m_LoadLookup;
1.476 + bool m_AllPluginsLoaded;
1.477 + IdentityToken_t *m_MyIdent;
1.478 +
1.479 + /* Dynamic native stuff */
1.480 + List<FakeNative *> m_Natives;
1.481 +
1.482 + bool m_LoadingLocked;
1.483 +};
1.484 +
1.485 +extern CPluginManager g_PluginSys;
1.486 +
1.487 +#endif //_INCLUDE_SOURCEMOD_PLUGINSYSTEM_H_