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