1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/core/logic/PluginSys.h Fri Mar 29 11:37:29 2013 -0700
1.3 @@ -0,0 +1,482 @@
1.4 +/**
1.5 + * vim: set ts=4 :
1.6 + * =============================================================================
1.7 + * SourceMod
1.8 + * Copyright (C) 2004-2010 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 "common_logic.h"
1.49 +#include <sm_trie_tpl.h>
1.50 +#include <IRootConsoleMenu.h>
1.51 +#include "ITranslator.h"
1.52 +#include "IGameConfigs.h"
1.53 +#include "NativeOwner.h"
1.54 +#include "ShareSys.h"
1.55 +
1.56 +class CPlayer;
1.57 +
1.58 +using namespace SourceHook;
1.59 +
1.60 +/**
1.61 + * NOTES:
1.62 + *
1.63 + * UPDATE 2008-03-11: These comments are horribly out of date. They paint a good overall
1.64 + * picture of how PluginSys works, but things like dependencies and fake natives have
1.65 + * complicated things quite a bit.
1.66 + *
1.67 + * Currently this system needs a lot of work but it's good skeletally. Plugin creation
1.68 + * is done without actually compiling anything. This is done by Load functions in the
1.69 + * manager. This will need a rewrite when we add context switching.
1.70 + *
1.71 + * The plugin object itself has a few things to note. The most important is that it stores
1.72 + * a table of function objects. The manager marshals allocation and freeing of these objects.
1.73 + * The plugin object can be in erroneous states, they are:
1.74 + * Plugin_Error --> Some error occurred any time during or after compilation.
1.75 + * This error can be cleared since the plugin itself is valid.
1.76 + * However, the state itself being set prevents any runtime action.
1.77 + * Plugin_BadLoad --> The plugin failed to load entirely and nothing can be done to save it.
1.78 + *
1.79 + * If a plugin fails to load externally, it is never added to the internal tracker. However,
1.80 + * plugins that failed to load from the internal loading mechanism are always tracked. This
1.81 + * allows users to see which automatically loaded plugins failed, and makes the interface a bit
1.82 + * more flexible.
1.83 + *
1.84 + * Once a plugin is compiled, it sets its own state to Plugin_Created. This state is still invalid
1.85 + * for execution. SourceMod is a two pass system, and even though the second pass is not implemented
1.86 + * yet, it is structured so Plugin_Created must be switched to Plugin_Running in the second pass. When
1.87 + * implemented, a Created plugin will be switched to Error in the second pass if it not loadable.
1.88 + *
1.89 + * The two pass loading mechanism is described below. Modules/natives are not implemented yet.
1.90 + * PASS ONE: All loadable plugins are found and have the following steps performed:
1.91 + * 1. Loading and compilation is attempted.
1.92 + * 2. If successful, all natives from Core are added.
1.93 + * 3. OnPluginLoad() is called.
1.94 + * 4. If failed, any user natives are scrapped and the process halts here.
1.95 + * 5. If successful, the plugin is ready for Pass 2.
1.96 + * INTERMEDIATE:
1.97 + * 1. All forced modules are loaded.
1.98 + * PASS TWO: All loaded plugins are found and have these steps performed:
1.99 + * 1. Any modules referenced in the plugin that are not already loaded, are loaded.
1.100 + * 2. If any module fails to load and the plugin requires it, load fails and jump to step 6.
1.101 + * 3. If any natives are unresolved, check if they are found in the user-natives pool.
1.102 + * 4. If yes, load succeeds. If not, natives are passed through a native acceptance filter.
1.103 + * 5. If the filter fails, the plugin is marked as failed.
1.104 + * 6. If the plugin has failed to load at this point, any dynamic natives it has added are scrapped.
1.105 + * Furthermore, any plugin that referenced these natives must now have pass 2 re-ran.
1.106 + * PASS THREE (not a real pass):
1.107 + * 7. Once all plugins are deemed to be loaded, OnPluginStart() is called
1.108 + */
1.109 +
1.110 +enum LoadRes
1.111 +{
1.112 + LoadRes_Successful,
1.113 + LoadRes_AlreadyLoaded,
1.114 + LoadRes_Failure,
1.115 + LoadRes_SilentFailure,
1.116 + LoadRes_NeverLoad
1.117 +};
1.118 +
1.119 +enum APLRes
1.120 +{
1.121 + APLRes_Success,
1.122 + APLRes_Failure,
1.123 + APLRes_SilentFailure
1.124 +};
1.125 +
1.126 +class CPlugin :
1.127 + public SMPlugin,
1.128 + public CNativeOwner
1.129 +{
1.130 + friend class CPluginManager;
1.131 + friend class CFunction;
1.132 +public:
1.133 + CPlugin(const char *file);
1.134 + ~CPlugin();
1.135 +public:
1.136 + PluginType GetType();
1.137 + SourcePawn::IPluginContext *GetBaseContext();
1.138 + sp_context_t *GetContext();
1.139 + void *GetPluginStructure();
1.140 + const char *GetFilename();
1.141 + bool IsDebugging();
1.142 + PluginStatus GetStatus();
1.143 + bool IsSilentlyFailed();
1.144 + void SetSilentlyFailed(bool sf);
1.145 + const sm_plugininfo_t *GetPublicInfo();
1.146 + bool SetPauseState(bool paused);
1.147 + unsigned int GetSerial();
1.148 + IdentityToken_t *GetIdentity();
1.149 + unsigned int CalcMemUsage();
1.150 + bool SetProperty(const char *prop, void *ptr);
1.151 + bool GetProperty(const char *prop, void **ptr, bool remove=false);
1.152 + void DropEverything();
1.153 + SourcePawn::IPluginRuntime *GetRuntime();
1.154 + CNativeOwner *ToNativeOwner() {
1.155 + return this;
1.156 + }
1.157 +public:
1.158 + /**
1.159 + * Creates a plugin object with default values.
1.160 + * If an error buffer is specified, and an error occurs, the error will be copied to the buffer
1.161 + * and NULL will be returned.
1.162 + * If an error buffer is not specified, the error will be copied to an internal buffer and
1.163 + * a valid (but error-stated) CPlugin will be returned.
1.164 + */
1.165 + static CPlugin *CreatePlugin(const char *file, char *error, size_t maxlength);
1.166 +public:
1.167 +
1.168 + /**
1.169 + * Sets an error state on the plugin
1.170 + */
1.171 + void SetErrorState(PluginStatus status, const char *error_fmt, ...);
1.172 +
1.173 + /**
1.174 + * Initializes the plugin's identity information
1.175 + */
1.176 + void InitIdentity();
1.177 +
1.178 + /**
1.179 + * Calls the OnPluginLoad function, and sets any failed states if necessary.
1.180 + * NOTE: Valid pre-states are: Plugin_Created
1.181 + * NOTE: If validated, plugin state is changed to Plugin_Loaded
1.182 + *
1.183 + * If the error buffer is NULL, the error message is cached locally.
1.184 + */
1.185 + APLRes Call_AskPluginLoad(char *error, size_t maxlength);
1.186 +
1.187 + /**
1.188 + * Calls the OnPluginStart function.
1.189 + * NOTE: Valid pre-states are: Plugin_Created
1.190 + * NOTE: Post-state will be Plugin_Running
1.191 + */
1.192 + void Call_OnPluginStart();
1.193 +
1.194 + /**
1.195 + * Calls the OnPluginEnd function.
1.196 + */
1.197 + void Call_OnPluginEnd();
1.198 +
1.199 + /**
1.200 + * Calls the OnAllPluginsLoaded function.
1.201 + */
1.202 + void Call_OnAllPluginsLoaded();
1.203 +
1.204 + /**
1.205 + * Returns true if a plugin is usable.
1.206 + */
1.207 + bool IsRunnable();
1.208 +
1.209 + /**
1.210 + * Get languages info.
1.211 + */
1.212 + IPhraseCollection *GetPhrases();
1.213 +public:
1.214 + /**
1.215 + * Returns the modification time during last plugin load.
1.216 + */
1.217 + time_t GetTimeStamp();
1.218 +
1.219 + /**
1.220 + * Returns the current modification time of the plugin file.
1.221 + */
1.222 + time_t GetFileTimeStamp();
1.223 +
1.224 + /**
1.225 + * Returns true if the plugin was running, but is now invalid.
1.226 + */
1.227 + bool WasRunning();
1.228 +
1.229 + Handle_t GetMyHandle();
1.230 +
1.231 + bool AddFakeNative(IPluginFunction *pFunc, const char *name, SPVM_FAKENATIVE_FUNC func);
1.232 + void AddConfig(bool autoCreate, const char *cfg, const char *folder);
1.233 + unsigned int GetConfigCount();
1.234 + AutoConfig *GetConfig(unsigned int i);
1.235 + inline void AddLibrary(const char *name)
1.236 + {
1.237 + m_Libraries.push_back(name);
1.238 + }
1.239 + void LibraryActions(LibraryAction action);
1.240 + void SyncMaxClients(int max_clients);
1.241 +protected:
1.242 + bool UpdateInfo();
1.243 + void SetTimeStamp(time_t t);
1.244 + void DependencyDropped(CPlugin *pOwner);
1.245 +private:
1.246 + PluginType m_type;
1.247 + char m_filename[PLATFORM_MAX_PATH];
1.248 + PluginStatus m_status;
1.249 + bool m_bSilentlyFailed;
1.250 + unsigned int m_serial;
1.251 + sm_plugininfo_t m_info;
1.252 + char m_errormsg[256];
1.253 + time_t m_LastAccess;
1.254 + IdentityToken_t *m_ident;
1.255 + Handle_t m_handle;
1.256 + bool m_WasRunning;
1.257 + IPhraseCollection *m_pPhrases;
1.258 + List<String> m_RequiredLibs;
1.259 + List<String> m_Libraries;
1.260 + KTrie<void *> m_pProps;
1.261 + bool m_FakeNativesMissing;
1.262 + bool m_LibraryMissing;
1.263 + CVector<AutoConfig *> m_configs;
1.264 + bool m_bGotAllLoaded;
1.265 + int m_FileVersion;
1.266 + char m_DateTime[256];
1.267 + IPluginRuntime *m_pRuntime;
1.268 + IPluginContext *m_pContext;
1.269 + sp_pubvar_t *m_MaxClientsVar;
1.270 +};
1.271 +
1.272 +class CPluginManager :
1.273 + public IScriptManager,
1.274 + public SMGlobalClass,
1.275 + public IHandleTypeDispatch,
1.276 + public IRootConsoleCommand
1.277 +{
1.278 + friend class CPlugin;
1.279 +public:
1.280 + CPluginManager();
1.281 + ~CPluginManager();
1.282 +public:
1.283 + /* Implements iterator class */
1.284 + class CPluginIterator : public IPluginIterator
1.285 + {
1.286 + public:
1.287 + CPluginIterator(List<CPlugin *> *mylist);
1.288 + virtual ~CPluginIterator();
1.289 + virtual bool MorePlugins();
1.290 + virtual IPlugin *GetPlugin();
1.291 + virtual void NextPlugin();
1.292 + void Release();
1.293 + public:
1.294 + void Reset();
1.295 + private:
1.296 + List<CPlugin *> *mylist;
1.297 + List<CPlugin *>::iterator current;
1.298 + };
1.299 + friend class CPluginManager::CPluginIterator;
1.300 +public: //IScriptManager
1.301 + IPlugin *LoadPlugin(const char *path,
1.302 + bool debug,
1.303 + PluginType type,
1.304 + char error[],
1.305 + size_t maxlength,
1.306 + bool *wasloaded);
1.307 + bool UnloadPlugin(IPlugin *plugin);
1.308 + IPlugin *FindPluginByContext(const sp_context_t *ctx);
1.309 + unsigned int GetPluginCount();
1.310 + IPluginIterator *GetPluginIterator();
1.311 + void AddPluginsListener(IPluginsListener *listener);
1.312 + void RemovePluginsListener(IPluginsListener *listener);
1.313 + void LoadAll(const char *config_path, const char *plugins_path);
1.314 + void RefreshAll();
1.315 + SMPlugin *FindPluginByOrder(unsigned num) {
1.316 + return GetPluginByOrder(num);
1.317 + }
1.318 + SMPlugin *FindPluginByIdentity(IdentityToken_t *ident) {
1.319 + return GetPluginFromIdentity(ident);
1.320 + }
1.321 + SMPlugin *FindPluginByContext(IPluginContext *ctx) {
1.322 + return GetPluginByCtx(ctx->GetContext());
1.323 + }
1.324 + SMPlugin *FindPluginByContext(sp_context_t *ctx) {
1.325 + return GetPluginByCtx(ctx);
1.326 + }
1.327 + SMPlugin *FindPluginByConsoleArg(const char *text);
1.328 + SMPlugin *FindPluginByHandle(Handle_t hndl, HandleError *errp) {
1.329 + return static_cast<SMPlugin *>(PluginFromHandle(hndl, errp));
1.330 + }
1.331 + void ListPlugins(CVector<SMPlugin *> *plugins);
1.332 +public: //SMGlobalClass
1.333 + void OnSourceModAllInitialized();
1.334 + void OnSourceModShutdown();
1.335 + ConfigResult OnSourceModConfigChanged(const char *key, const char *value, ConfigSource source, char *error, size_t maxlength);
1.336 + void OnSourceModMaxPlayersChanged(int newvalue);
1.337 +public: //IHandleTypeDispatch
1.338 + void OnHandleDestroy(HandleType_t type, void *object);
1.339 + bool GetHandleApproxSize(HandleType_t type, void *object, unsigned int *pSize);
1.340 +public: //IRootConsoleCommand
1.341 + void OnRootConsoleCommand(const char *cmdname, const CCommand &command);
1.342 +public:
1.343 + /**
1.344 + * Loads all plugins not yet loaded
1.345 + */
1.346 + void LoadAll_FirstPass(const char *config, const char *basedir);
1.347 +
1.348 + /**
1.349 + * Runs the second loading pass for all plugins
1.350 + */
1.351 + void LoadAll_SecondPass();
1.352 +
1.353 + /**
1.354 + * Tests a plugin file mask against a local folder.
1.355 + * The alias is searched backwards from localdir - i.e., given this input:
1.356 + * csdm/ban csdm/ban
1.357 + * ban csdm/ban
1.358 + * csdm/ban optional/csdm/ban
1.359 + * All of these will return true for an alias match.
1.360 + * Wildcards are allowed in the filename.
1.361 + */
1.362 + bool TestAliasMatch(const char *alias, const char *localdir);
1.363 +
1.364 + /**
1.365 + * Returns whether anything loaded will be a late load.
1.366 + */
1.367 + bool IsLateLoadTime() const;
1.368 +
1.369 + /**
1.370 + * Converts a Handle to an IPlugin if possible.
1.371 + */
1.372 + IPlugin *PluginFromHandle(Handle_t handle, HandleError *err);
1.373 +
1.374 + /**
1.375 + * Finds a plugin based on its index. (starts on index 1)
1.376 + */
1.377 + CPlugin *GetPluginByOrder(int num);
1.378 +
1.379 + int GetOrderOfPlugin(IPlugin *pl);
1.380 +
1.381 + /**
1.382 + * Internal version of FindPluginByContext()
1.383 + */
1.384 + CPlugin *GetPluginByCtx(const sp_context_t *ctx);
1.385 +
1.386 + /**
1.387 + * Gets status text for a status code
1.388 + */
1.389 + const char *GetStatusText(PluginStatus status);
1.390 +
1.391 + /**
1.392 + * Add public functions from all running or paused
1.393 + * plugins to the specified forward if the names match.
1.394 + */
1.395 + void AddFunctionsToForward(const char *name, IChangeableForward *pForward);
1.396 +
1.397 + /**
1.398 + * Iterates through plugins to call OnAllPluginsLoaded.
1.399 + */
1.400 + void AllPluginsLoaded();
1.401 +
1.402 + CPlugin *GetPluginFromIdentity(IdentityToken_t *pToken);
1.403 +
1.404 + void Shutdown();
1.405 +
1.406 + void OnLibraryAction(const char *lib, LibraryAction action);
1.407 +
1.408 + bool LibraryExists(const char *lib);
1.409 +
1.410 + bool ReloadPlugin(CPlugin *pl);
1.411 +
1.412 + void UnloadAll();
1.413 +
1.414 + void SyncMaxClients(int max_clients);
1.415 +
1.416 + void ListPluginsToClient(CPlayer *player, const CCommand &args);
1.417 +private:
1.418 + LoadRes _LoadPlugin(CPlugin **pPlugin, const char *path, bool debug, PluginType type, char error[], size_t maxlength);
1.419 +
1.420 + void LoadAutoPlugin(const char *plugin);
1.421 +
1.422 + /**
1.423 + * Recursively loads all plugins in the given directory.
1.424 + */
1.425 + void LoadPluginsFromDir(const char *basedir, const char *localdir);
1.426 +
1.427 + /**
1.428 + * Adds a plugin object. This is wrapped by LoadPlugin functions.
1.429 + */
1.430 + void AddPlugin(CPlugin *pPlugin);
1.431 +
1.432 + /**
1.433 + * Runs the second loading pass on a plugin.
1.434 + */
1.435 + bool RunSecondPass(CPlugin *pPlugin, char *error, size_t maxlength);
1.436 +
1.437 + /**
1.438 + * Runs an extension pass on a plugin.
1.439 + */
1.440 + bool LoadOrRequireExtensions(CPlugin *pPlugin, unsigned int pass, char *error, size_t maxlength);
1.441 +
1.442 + /**
1.443 + * Manages required natives.
1.444 + */
1.445 + bool FindOrRequirePluginDeps(CPlugin *pPlugin, char *error, size_t maxlength);
1.446 +
1.447 + void _SetPauseState(CPlugin *pPlugin, bool pause);
1.448 +protected:
1.449 + /**
1.450 + * Caching internal objects
1.451 + */
1.452 + void ReleaseIterator(CPluginIterator *iter);
1.453 +public:
1.454 + inline IdentityToken_t *GetIdentity()
1.455 + {
1.456 + return m_MyIdent;
1.457 + }
1.458 + IPluginManager *GetOldAPI();
1.459 +private:
1.460 + void TryRefreshDependencies(CPlugin *pOther);
1.461 +private:
1.462 + List<IPluginsListener *> m_listeners;
1.463 + List<CPlugin *> m_plugins;
1.464 + CStack<CPluginManager::CPluginIterator *> m_iters;
1.465 + KTrie<CPlugin *> m_LoadLookup;
1.466 + bool m_AllPluginsLoaded;
1.467 + IdentityToken_t *m_MyIdent;
1.468 +
1.469 + /* Dynamic native stuff */
1.470 + List<FakeNative *> m_Natives;
1.471 +
1.472 + bool m_LoadingLocked;
1.473 +
1.474 + // Config
1.475 + bool m_bBlockBadPlugins;
1.476 +
1.477 + // Forwards
1.478 + IForward *m_pOnLibraryAdded;
1.479 + IForward *m_pOnLibraryRemoved;
1.480 +};
1.481 +
1.482 +extern CPluginManager g_PluginSys;
1.483 +
1.484 +#endif //_INCLUDE_SOURCEMOD_PLUGINSYSTEM_H_
1.485 +