533 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			533 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /**
 | |
|  * vim: set ts=4 sw=4 tw=99 noet :
 | |
|  * =============================================================================
 | |
|  * SourceMod
 | |
|  * Copyright (C) 2004-2010 AlliedModders LLC.  All rights reserved.
 | |
|  * =============================================================================
 | |
|  *
 | |
|  * This program is free software; you can redistribute it and/or modify it under
 | |
|  * the terms of the GNU General Public License, version 3.0, as published by the
 | |
|  * Free Software Foundation.
 | |
|  * 
 | |
|  * This program is distributed in the hope that it will be useful, but WITHOUT
 | |
|  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 | |
|  * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
 | |
|  * details.
 | |
|  *
 | |
|  * You should have received a copy of the GNU General Public License along with
 | |
|  * this program.  If not, see <http://www.gnu.org/licenses/>.
 | |
|  *
 | |
|  * As a special exception, AlliedModders LLC gives you permission to link the
 | |
|  * code of this program (as well as its derivative works) to "Half-Life 2," the
 | |
|  * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
 | |
|  * by the Valve Corporation.  You must obey the GNU General Public License in
 | |
|  * all respects for all other code used.  Additionally, AlliedModders LLC grants
 | |
|  * this exception to all derivative works.  AlliedModders LLC defines further
 | |
|  * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
 | |
|  * or <http://www.sourcemod.net/license.php>.
 | |
|  *
 | |
|  * Version: $Id$
 | |
|  */
 | |
| 
 | |
| #ifndef _INCLUDE_SOURCEMOD_PLUGINSYSTEM_H_
 | |
| #define _INCLUDE_SOURCEMOD_PLUGINSYSTEM_H_
 | |
| 
 | |
| #include <errno.h>
 | |
| #include <sys/stat.h>
 | |
| #include <time.h>
 | |
| 
 | |
| #include <memory>
 | |
| 
 | |
| #include <IPluginSys.h>
 | |
| #include <IHandleSys.h>
 | |
| #include <IForwardSys.h>
 | |
| #include <sh_list.h>
 | |
| #include <sh_stack.h>
 | |
| #include <sh_vector.h>
 | |
| #include <sh_string.h>
 | |
| #include "common_logic.h"
 | |
| #include <IRootConsoleMenu.h>
 | |
| #include <sm_stringhashmap.h>
 | |
| #include <sm_namehashset.h>
 | |
| #include "ITranslator.h"
 | |
| #include "IGameConfigs.h"
 | |
| #include "NativeOwner.h"
 | |
| #include "ShareSys.h"
 | |
| #include "PhraseCollection.h"
 | |
| #include <am-string.h>
 | |
| #include <bridge/include/IScriptManager.h>
 | |
| #include <am-function.h>
 | |
| #include <ReentrantList.h>
 | |
| 
 | |
| class CPlayer;
 | |
| 
 | |
| using namespace SourceHook;
 | |
| 
 | |
| enum LoadRes
 | |
| {
 | |
| 	LoadRes_Successful,
 | |
| 	LoadRes_AlreadyLoaded,
 | |
| 	LoadRes_Failure,
 | |
| 	LoadRes_NeverLoad
 | |
| };
 | |
| 
 | |
| enum APLRes
 | |
| {
 | |
| 	APLRes_Success,
 | |
| 	APLRes_Failure,
 | |
| 	APLRes_SilentFailure
 | |
| };
 | |
| 
 | |
| // Plugin membership state.
 | |
| enum class PluginState
 | |
| {
 | |
| 	// The plugin has not yet been added to the global plugin list.
 | |
| 	Unregistered,
 | |
| 
 | |
| 	// The plugin is a member of the global plugin list.
 | |
| 	Registered,
 | |
| 
 | |
| 	// The plugin has been evicted.
 | |
| 	Evicted,
 | |
| 
 | |
| 	// The plugin is waiting to be unloaded.
 | |
| 	WaitingToUnload,
 | |
| 	WaitingToUnloadAndReload,
 | |
| };
 | |
| 
 | |
| class CPlugin : 
 | |
| 	public SMPlugin,
 | |
| 	public CNativeOwner
 | |
| {
 | |
| public:
 | |
| 	CPlugin(const char *file);
 | |
| 	~CPlugin();
 | |
| public:
 | |
| 	PluginType GetType();
 | |
| 	SourcePawn::IPluginContext *GetBaseContext();
 | |
| 	sp_context_t *GetContext();
 | |
| 	void *GetPluginStructure();
 | |
| 	const char *GetFilename();
 | |
| 	bool IsDebugging();
 | |
| 	PluginStatus GetStatus();
 | |
| 	PluginStatus Status() const;
 | |
| 	bool IsSilentlyFailed();
 | |
| 	const sm_plugininfo_t *GetPublicInfo();
 | |
| 	bool SetPauseState(bool paused);
 | |
| 	unsigned int GetSerial();
 | |
| 	IdentityToken_t *GetIdentity();
 | |
| 	unsigned int CalcMemUsage();
 | |
| 	bool SetProperty(const char *prop, void *ptr);
 | |
| 	bool GetProperty(const char *prop, void **ptr, bool remove=false);
 | |
| 	void DropEverything();
 | |
| 	SourcePawn::IPluginRuntime *GetRuntime();
 | |
| 	CNativeOwner *ToNativeOwner() {
 | |
| 		return this;
 | |
| 	}
 | |
| 
 | |
| 	struct ExtVar {
 | |
| 		char *name;
 | |
| 		char *file;
 | |
| 		bool autoload;
 | |
| 		bool required;
 | |
| 	};
 | |
| 
 | |
| 	typedef ke::Function<bool(const sp_pubvar_t *, const ExtVar& ext)> ExtVarCallback;
 | |
| 	bool ForEachExtVar(const ExtVarCallback& callback);
 | |
| 
 | |
| 	void ForEachLibrary(ke::Function<void(const char *)> callback);
 | |
| public:
 | |
| 	/**
 | |
| 	 * Creates a plugin object with default values.
 | |
| 	 *   If an error buffer is specified, and an error occurs, the error will be copied to the buffer
 | |
| 	 * and NULL will be returned.
 | |
| 	 *   If an error buffer is not specified, the error will be copied to an internal buffer and 
 | |
| 	 * a valid (but error-stated) CPlugin will be returned.
 | |
| 	 */
 | |
| 	static CPlugin *Create(const char *file);
 | |
| 
 | |
| public:
 | |
| 	// Evicts the plugin from memory and sets an error state.
 | |
| 	void EvictWithError(PluginStatus status, const char *error_fmt, ...);
 | |
| 	void FinishEviction();
 | |
| 
 | |
| 	// Initializes the plugin's identity information
 | |
| 	void InitIdentity();
 | |
| 
 | |
| 	// Calls the OnPluginLoad function, and sets any failed states if necessary.
 | |
| 	// After invoking AskPluginLoad, its state is either Running or Failed.
 | |
| 	APLRes AskPluginLoad();
 | |
| 
 | |
| 	// Transition to the fully running state, if possible.
 | |
| 	bool OnPluginStart();
 | |
| 
 | |
| 	void Call_OnPluginEnd();
 | |
| 	void Call_OnAllPluginsLoaded();
 | |
| 	void Call_OnLibraryAdded(const char *lib);
 | |
| 
 | |
| 	// Returns true if a plugin is usable.
 | |
| 	bool IsRunnable();
 | |
| 
 | |
| 	// Get languages info.
 | |
| 	IPhraseCollection *GetPhrases();
 | |
| 
 | |
| 	PluginState State() const {
 | |
| 		return m_state;
 | |
| 	}
 | |
| 	void SetRegistered();
 | |
| 	void SetWaitingToUnload(bool andReload=false);
 | |
| 
 | |
| 	PluginStatus GetDisplayStatus() const {
 | |
| 		return m_status;
 | |
| 	}
 | |
| 	bool IsEvictionCandidate() const;
 | |
| 
 | |
| public:
 | |
| 	// Returns true if the plugin was running, but is now invalid.
 | |
| 	bool WasRunning();
 | |
| 
 | |
| 	Handle_t GetMyHandle();
 | |
| 
 | |
| 	bool AddFakeNative(IPluginFunction *pFunc, const char *name, SPVM_FAKENATIVE_FUNC func);
 | |
| 	void AddConfig(bool autoCreate, const char *cfg, const char *folder);
 | |
| 	size_t GetConfigCount();
 | |
| 	AutoConfig *GetConfig(size_t i);
 | |
| 	inline void AddLibrary(const char *name) {
 | |
| 		m_Libraries.push_back(name);
 | |
| 	}
 | |
| 	inline bool HasLibrary(const char *name) {
 | |
| 		return m_Libraries.find(name) != m_Libraries.end();
 | |
| 	}
 | |
| 	void LibraryActions(LibraryAction action);
 | |
| 	void SyncMaxClients(int max_clients);
 | |
| 
 | |
| 	// Returns true if the plugin's underlying file structure has a newer
 | |
| 	// modification time than either when it was first instantiated or
 | |
| 	// since the last call to HasUpdatedFile().
 | |
| 	bool HasUpdatedFile();
 | |
| 
 | |
| 	const char *GetDateTime() const {
 | |
| 		return m_DateTime;
 | |
| 	}
 | |
| 	int GetFileVersion() const {
 | |
| 		return m_FileVersion;
 | |
| 	}
 | |
| 	const char *GetErrorMsg() const {
 | |
| 		assert(m_status != Plugin_Running && m_status != Plugin_Loaded);
 | |
| 		return m_errormsg;
 | |
| 	}
 | |
| 
 | |
| 	void AddRequiredLib(const char *name);
 | |
| 	bool ForEachRequiredLib(ke::Function<bool(const char *)> callback);
 | |
| 
 | |
| 	bool HasMissingFakeNatives() const {
 | |
| 		return m_FakeNativesMissing;
 | |
| 	}
 | |
| 	bool HasMissingLibrary() const {
 | |
| 		return m_LibraryMissing;
 | |
| 	}
 | |
| 	bool HasFakeNatives() const {
 | |
| 		return m_fakes.size() > 0;
 | |
| 	}
 | |
| 
 | |
| 	// True if we got far enough into the second pass to call OnPluginLoaded
 | |
| 	// in the listener list.
 | |
| 	bool EnteredSecondPass() const {
 | |
| 		return m_EnteredSecondPass;
 | |
| 	}
 | |
| 
 | |
| 	bool IsInErrorState() const {
 | |
| 		if (m_status == Plugin_Running || m_status == Plugin_Loaded)
 | |
| 			return false;
 | |
| 		return true;
 | |
| 	}
 | |
| 
 | |
| 	bool TryCompile();
 | |
| 	void BindFakeNativesTo(CPlugin *other);
 | |
| 
 | |
| protected:
 | |
| 	void DependencyDropped(CPlugin *pOwner);
 | |
| 
 | |
| private:
 | |
| 	time_t GetFileTimeStamp();
 | |
| 	bool ReadInfo();
 | |
| 	void DestroyIdentity();
 | |
| 
 | |
| private:
 | |
| 	// This information is static for the lifetime of the plugin.
 | |
| 	char m_filename[PLATFORM_MAX_PATH];
 | |
| 	unsigned int m_serial;
 | |
| 
 | |
| 	PluginStatus m_status;
 | |
| 	PluginState m_state;
 | |
| 	bool m_AddedLibraries;
 | |
| 	bool m_EnteredSecondPass;
 | |
| 
 | |
| 	// Statuses that are set during failure.
 | |
| 	bool m_SilentFailure;
 | |
| 	bool m_FakeNativesMissing;
 | |
| 	bool m_LibraryMissing;
 | |
| 	char m_errormsg[256];
 | |
| 
 | |
| 	// Internal properties that must by reset if the runtime is evicted.
 | |
| 	std::unique_ptr<IPluginRuntime> m_pRuntime;
 | |
| 	std::unique_ptr<CPhraseCollection> m_pPhrases;
 | |
| 	IPluginContext *m_pContext;
 | |
| 	sp_pubvar_t *m_MaxClientsVar;
 | |
| 	StringHashMap<void *> m_Props;
 | |
| 	CVector<AutoConfig *> m_configs;
 | |
| 	List<String> m_Libraries;
 | |
| 	bool m_bGotAllLoaded;
 | |
| 	int m_FileVersion;
 | |
| 
 | |
| 	// Information that survives past eviction.
 | |
| 	List<String> m_RequiredLibs;
 | |
| 	IdentityToken_t *m_ident;
 | |
| 	time_t m_LastFileModTime;
 | |
| 	Handle_t m_handle;
 | |
| 	char m_DateTime[256];
 | |
| 
 | |
| 	// Cached.
 | |
| 	sm_plugininfo_t m_info;
 | |
| 	std::string info_name_;
 | |
| 	std::string info_author_;
 | |
| 	std::string info_description_;
 | |
| 	std::string info_version_;
 | |
| 	std::string info_url_;
 | |
| };
 | |
| 
 | |
| class CPluginManager : 
 | |
| 	public IScriptManager,
 | |
| 	public SMGlobalClass,
 | |
| 	public IHandleTypeDispatch,
 | |
| 	public IRootConsoleCommand
 | |
| {
 | |
| public:
 | |
| 	CPluginManager();
 | |
| 	~CPluginManager();
 | |
| public:
 | |
| 	/* Implements iterator class */
 | |
| 	class CPluginIterator
 | |
| 		: public IPluginIterator,
 | |
| 		  public IPluginsListener
 | |
| 	{
 | |
| 	public:
 | |
| 		CPluginIterator(ReentrantList<CPlugin *>& in);
 | |
| 		virtual ~CPluginIterator();
 | |
| 		virtual bool MorePlugins();
 | |
| 		virtual IPlugin *GetPlugin();
 | |
| 		virtual void NextPlugin();
 | |
| 		void Release();
 | |
| 		void OnPluginDestroyed(IPlugin *plugin) override;
 | |
| 	private:
 | |
| 		std::list<CPlugin *> mylist;
 | |
| 		std::list<CPlugin *>::iterator current;
 | |
| 	};
 | |
| 	friend class CPluginManager::CPluginIterator;
 | |
| public: //IScriptManager
 | |
| 	IPlugin *LoadPlugin(const char *path, 
 | |
| 								bool debug,
 | |
| 								PluginType type,
 | |
| 								char error[],
 | |
| 								size_t maxlength,
 | |
| 								bool *wasloaded);
 | |
| 	bool UnloadPlugin(IPlugin *plugin);
 | |
| 	IPlugin *FindPluginByContext(const sp_context_t *ctx);
 | |
| 	unsigned int GetPluginCount();
 | |
| 	IPluginIterator *GetPluginIterator();
 | |
| 	void AddPluginsListener(IPluginsListener *listener);
 | |
| 	void RemovePluginsListener(IPluginsListener *listener);
 | |
| 	void LoadAll(const char *config_path, const char *plugins_path);
 | |
| 	void RefreshAll();
 | |
| 	SMPlugin *FindPluginByOrder(unsigned num) {
 | |
| 		return GetPluginByOrder(num);
 | |
| 	}
 | |
| 	SMPlugin *FindPluginByIdentity(IdentityToken_t *ident) {
 | |
| 		return GetPluginFromIdentity(ident);
 | |
| 	}
 | |
| 	SMPlugin *FindPluginByContext(IPluginContext *ctx) {
 | |
| 		return GetPluginByCtx(ctx->GetContext());
 | |
| 	}
 | |
| 	SMPlugin *FindPluginByContext(sp_context_t *ctx) {
 | |
| 		return GetPluginByCtx(ctx);
 | |
| 	}
 | |
| 	SMPlugin *FindPluginByConsoleArg(const char *text);
 | |
| 	SMPlugin *FindPluginByHandle(Handle_t hndl, HandleError *errp) {
 | |
| 		return static_cast<SMPlugin *>(PluginFromHandle(hndl, errp));
 | |
| 	}
 | |
| 	const CVector<SMPlugin *> *ListPlugins();
 | |
| 	void FreePluginList(const CVector<SMPlugin *> *plugins);
 | |
| 
 | |
| public: //SMGlobalClass
 | |
| 	void OnSourceModAllInitialized();
 | |
| 	void OnSourceModShutdown();
 | |
| 	ConfigResult OnSourceModConfigChanged(const char *key, const char *value, ConfigSource source, char *error, size_t maxlength);
 | |
| 	void OnSourceModMaxPlayersChanged(int newvalue);
 | |
| public: //IHandleTypeDispatch
 | |
| 	void OnHandleDestroy(HandleType_t type, void *object);
 | |
| 	bool GetHandleApproxSize(HandleType_t type, void *object, unsigned int *pSize);
 | |
| public: //IRootConsoleCommand
 | |
| 	void OnRootConsoleCommand(const char *cmdname, const ICommandArgs *command) override;
 | |
| public:
 | |
| 	/**
 | |
| 	 * Loads all plugins not yet loaded
 | |
| 	 */
 | |
| 	void LoadAll_FirstPass(const char *config, const char *basedir);
 | |
| 
 | |
| 	/**
 | |
| 	 * Runs the second loading pass for all plugins
 | |
| 	 */
 | |
| 	void LoadAll_SecondPass();
 | |
| 
 | |
| 	/** 
 | |
| 	 * Returns whether anything loaded will be a late load.
 | |
| 	 */
 | |
| 	bool IsLateLoadTime() const;
 | |
| 
 | |
| 	/**
 | |
| 	 * Converts a Handle to an IPlugin if possible.
 | |
| 	 */
 | |
| 	IPlugin *PluginFromHandle(Handle_t handle, HandleError *err);
 | |
| 
 | |
| 	/**
 | |
| 	 * Finds a plugin based on its index. (starts on index 1)
 | |
| 	 */
 | |
| 	CPlugin *GetPluginByOrder(int num);
 | |
| 
 | |
| 	int GetOrderOfPlugin(IPlugin *pl);
 | |
| 
 | |
| 	/** 
 | |
| 	 * Internal version of FindPluginByContext()
 | |
| 	 */
 | |
| 	CPlugin *GetPluginByCtx(const sp_context_t *ctx);
 | |
| 
 | |
| 	/**
 | |
| 	 * Gets status text for a status code 
 | |
| 	 */
 | |
| 	const char *GetStatusText(PluginStatus status);
 | |
| 
 | |
| 	/**
 | |
| 	 * Add public functions from all running or paused
 | |
| 	 * plugins to the specified forward if the names match.
 | |
| 	 */
 | |
| 	void AddFunctionsToForward(const char *name, IChangeableForward *pForward);
 | |
| 
 | |
| 	/**
 | |
| 	 * Iterates through plugins to call OnAllPluginsLoaded.
 | |
| 	 */
 | |
| 	void AllPluginsLoaded();
 | |
| 
 | |
| 	CPlugin *GetPluginFromIdentity(IdentityToken_t *pToken);
 | |
| 
 | |
| 	void Shutdown();
 | |
| 
 | |
| 	void OnLibraryAction(const char *lib, LibraryAction action);
 | |
| 
 | |
| 	bool LibraryExists(const char *lib);
 | |
| 
 | |
| 	bool ReloadPlugin(CPlugin *pl, bool print=false);
 | |
| 
 | |
| 	void UnloadAll();
 | |
| 
 | |
| 	void SyncMaxClients(int max_clients);
 | |
| 
 | |
| 	void ListPluginsToClient(CPlayer *player, const CCommand &args);
 | |
| 
 | |
| 	void _SetPauseState(CPlugin *pPlugin, bool pause);
 | |
| 
 | |
| 	void ForEachPlugin(ke::Function<void(CPlugin *)> callback);
 | |
| private:
 | |
| 	LoadRes LoadPlugin(CPlugin **pPlugin, const char *path, bool debug, PluginType type);
 | |
| 
 | |
| 	void LoadAutoPlugin(const char *plugin);
 | |
| 
 | |
| 	/**
 | |
| 	 * Recursively loads all plugins in the given directory.
 | |
| 	 */
 | |
| 	void LoadPluginsFromDir(const char *basedir, const char *localdir);
 | |
| 
 | |
| 	/**
 | |
| 	 * Adds a plugin object.  This is wrapped by LoadPlugin functions.
 | |
| 	 */
 | |
| 	void AddPlugin(CPlugin *pPlugin);
 | |
| 
 | |
| 	// First pass for loading a plugin, and its helpers.
 | |
| 	CPlugin *CompileAndPrep(const char *path);
 | |
| 	bool MalwareCheckPass(CPlugin *pPlugin);
 | |
| 
 | |
| 	// Runs the second loading pass on a plugin.
 | |
| 	bool RunSecondPass(CPlugin *pPlugin);
 | |
| 	void LoadExtensions(CPlugin *pPlugin);
 | |
| 	bool RequireExtensions(CPlugin *pPlugin);
 | |
| 	bool FindOrRequirePluginDeps(CPlugin *pPlugin);
 | |
| 
 | |
| 	void UnloadPluginImpl(CPlugin *plugin);
 | |
| 	void ReloadPluginImpl(int id, const char filename[], PluginType ptype, bool print);
 | |
| 
 | |
| 	void Purge(CPlugin *plugin);
 | |
| public:
 | |
| 	inline IdentityToken_t *GetIdentity()
 | |
| 	{
 | |
| 		return m_MyIdent;
 | |
| 	}
 | |
| 	IPluginManager *GetOldAPI();
 | |
| private:
 | |
| 	void TryRefreshDependencies(CPlugin *pOther);
 | |
| private:
 | |
| 	ReentrantList<IPluginsListener *> m_listeners;
 | |
| 	ReentrantList<CPlugin *> m_plugins;
 | |
| 	std::list<CPluginIterator *> m_iterators;
 | |
| 
 | |
| 	typedef decltype(m_listeners)::iterator ListenerIter;
 | |
| 	typedef decltype(m_plugins)::iterator PluginIter;
 | |
| 
 | |
| 	struct CPluginPolicy
 | |
| 	{
 | |
| 		static inline uint32_t hash(const detail::CharsAndLength &key)
 | |
| 		{
 | |
| /* For windows & mac, we convert the path to lower-case in order to avoid duplicate plugin loading */
 | |
| #if defined PLATFORM_WINDOWS || defined PLATFORM_APPLE
 | |
| 			std::string lower = ke::Lowercase(key.c_str());
 | |
| 			return detail::CharsAndLength(lower.c_str()).hash();
 | |
| #else
 | |
| 			return key.hash();
 | |
| #endif
 | |
| 		}
 | |
| 		
 | |
| 		static inline bool matches(const char *file, const CPlugin *plugin)
 | |
| 		{
 | |
| 			const char *pluginFileChars = const_cast<CPlugin*>(plugin)->GetFilename();
 | |
| #if defined PLATFORM_WINDOWS || defined PLATFORM_APPLE
 | |
| 			std::string pluginFile = ke::Lowercase(pluginFileChars);
 | |
| 			std::string input = ke::Lowercase(file);
 | |
| 			
 | |
| 			return pluginFile == input;
 | |
| #else
 | |
| 			return strcmp(pluginFileChars, file) == 0;
 | |
| #endif
 | |
| 		}
 | |
| 	};
 | |
| 	NameHashSet<CPlugin *, CPluginPolicy> m_LoadLookup;
 | |
| 
 | |
| 	bool m_AllPluginsLoaded;
 | |
| 	IdentityToken_t *m_MyIdent;
 | |
| 
 | |
| 	/* Dynamic native stuff */
 | |
| 	List<FakeNative *> m_Natives;
 | |
| 
 | |
| 	bool m_LoadingLocked;
 | |
| 	
 | |
| 	// Config
 | |
| 	bool m_bBlockBadPlugins;
 | |
| 	
 | |
| 	// Forwards
 | |
| 	IForward *m_pOnLibraryAdded;
 | |
| 	IForward *m_pOnLibraryRemoved;
 | |
| 	IForward *m_pOnNotifyPluginUnloaded;
 | |
| };
 | |
| 
 | |
| extern CPluginManager g_PluginSys;
 | |
| 
 | |
| #endif //_INCLUDE_SOURCEMOD_PLUGINSYSTEM_H_
 | |
| 
 |