dvander@370: /** dvander@2782: * vim: set ts=4 sw=4 tw=99 noet : ds@1330: * ============================================================================= dvander@1231: * SourceMod ds@3099: * Copyright (C) 2004-2010 AlliedModders LLC. All rights reserved. ds@1330: * ============================================================================= dvander@370: * ds@1330: * This program is free software; you can redistribute it and/or modify it under ds@1330: * the terms of the GNU General Public License, version 3.0, as published by the ds@1330: * Free Software Foundation. dvander@1231: * ds@1330: * This program is distributed in the hope that it will be useful, but WITHOUT ds@1330: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS ds@1330: * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ds@1330: * details. dvander@1231: * ds@1330: * You should have received a copy of the GNU General Public License along with ds@1330: * this program. If not, see . dvander@1231: * ds@1330: * As a special exception, AlliedModders LLC gives you permission to link the ds@1330: * code of this program (as well as its derivative works) to "Half-Life 2," the ds@1330: * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software ds@1330: * by the Valve Corporation. You must obey the GNU General Public License in ds@1330: * all respects for all other code used. Additionally, AlliedModders LLC grants ds@1330: * this exception to all derivative works. AlliedModders LLC defines further ds@1330: * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), ds@1330: * or . dvander@370: * dvander@370: * Version: $Id$ dvander@370: */ dvander@370: dvander@397: #include dvander@298: #include "ExtensionSys.h" dvander@298: #include "LibrarySys.h" dvander@298: #include "ShareSys.h" dvander@592: #include "Logger.h" dvander@304: #include "sourcemm_api.h" dvander@310: #include "sm_srvcmds.h" dvander@397: #include "sm_stringutil.h" voidedweasel@3511: #include "PlayerManager.h" dvander@298: dvander@298: CExtensionManager g_Extensions; dvander@298: IdentityType_t g_ExtType; dvander@298: dvander@1648: void CExtension::Initialize(const char *filename, const char *path) dvander@298: { dvander@298: m_pAPI = NULL; dvander@298: m_pIdentToken = NULL; dvander@1648: unload_code = 0; dvander@1648: m_bFullyLoaded = false; dvander@1648: m_File.assign(filename); dvander@1648: m_Path.assign(path); dvander@2782: char real_name[PLATFORM_MAX_PATH]; dvander@2782: g_LibSys.GetFileFromPath(real_name, sizeof(real_name), m_Path.c_str()); dvander@2782: m_RealFile.assign(real_name); dvander@1648: } dvander@1648: dvander@1648: CRemoteExtension::CRemoteExtension(IExtensionInterface *pAPI, const char *filename, const char *path) dvander@1648: { dvander@1668: Initialize(filename, path); dvander@1648: m_pAPI = pAPI; dvander@1648: } dvander@1648: dvander@2782: #if defined METAMOD_PLAPI_VERSION dvander@2782: #if SOURCE_ENGINE == SE_LEFT4DEAD dvander@2782: #define GAMEFIX "2.l4d" fyrenmoo@2847: #elif SOURCE_ENGINE == SE_LEFT4DEAD2 fyrenmoo@2847: #define GAMEFIX "2.l4d2" ds@3099: #elif SOURCE_ENGINE == SE_ALIENSWARM ds@3099: #define GAMEFIX "2.swarm" dvander@2782: #elif SOURCE_ENGINE == SE_ORANGEBOX dvander@2782: #define GAMEFIX "2.ep2" skamonkey@3180: #elif SOURCE_ENGINE == SE_BLOODYGOODTIME skamonkey@3180: #define GAMEFIX "2.bgt" skamonkey@3373: #elif SOURCE_ENGINE == SE_EYE skamonkey@3373: #define GAMEFIX "2.eye" dvander@2782: #elif SOURCE_ENGINE == SE_ORANGEBOXVALVE dvander@2782: #define GAMEFIX "2.ep2v" dvander@2782: #elif SOURCE_ENGINE == SE_DARKMESSIAH dvander@2782: #define GAMEFIX "2.darkm" dvander@2782: #else dvander@2782: #define GAMEFIX "2.ep1" fyrenmoo@2847: #endif //(SOURCE_ENGINE == SE_LEFT4DEAD) || (SOURCE_ENGINE == SE_LEFT4DEAD2) dvander@2782: #else //METAMOD_PLAPI_VERSION dvander@2782: #define GAMEFIX "1.ep1" dvander@2782: #endif //METAMOD_PLAPI_VERSION dvander@2782: dvander@1648: CLocalExtension::CLocalExtension(const char *filename) dvander@1648: { dvander@304: m_PlId = 0; dvander@1648: m_pLib = NULL; dvander@298: ds@652: char path[PLATFORM_MAX_PATH]; dvander@1568: ds@3104: /* Special case for new bintools binary */ ds@3104: if (strcmp(filename, "bintools.ext") == 0) ds@3104: { ds@3104: goto normal; ds@3104: } ds@3104: dvander@2782: /* Zeroth, see if there is an engine specific build in the new place. */ dvander@2782: g_SourceMod.BuildPath(Path_SM, dvander@2782: path, dvander@2782: PLATFORM_MAX_PATH, dvander@2782: "extensions/%s." GAMEFIX "." PLATFORM_LIB_EXT, dvander@2782: filename); dvander@2782: dvander@2782: if (g_LibSys.IsPathFile(path)) dvander@2782: { dvander@2782: goto found; dvander@2782: } dvander@2782: dvander@1568: /* First see if there is an engine specific build! */ dvander@1568: g_SourceMod.BuildPath(Path_SM, dvander@1568: path, dvander@2782: PLATFORM_MAX_PATH, dvander@2782: "extensions/auto." GAMEFIX "/%s." PLATFORM_LIB_EXT, dvander@1568: filename); dvander@1568: ds@3104: normal: dvander@1568: /* Try the "normal" version */ dvander@1568: if (!g_LibSys.IsPathFile(path)) dvander@1568: { dvander@2782: g_SourceMod.BuildPath(Path_SM, dvander@2782: path, dvander@2782: PLATFORM_MAX_PATH, dvander@2782: "extensions/%s." PLATFORM_LIB_EXT, dvander@2782: filename); dvander@1568: } dvander@1568: dvander@2782: found: dvander@1648: Initialize(filename, path); dvander@941: } dvander@941: dvander@1648: bool CRemoteExtension::Load(char *error, size_t maxlength) dvander@1648: { dvander@1648: if (!PerformAPICheck(error, maxlength)) dvander@1648: { dvander@1648: m_pAPI = NULL; dvander@1648: return false; dvander@1648: } dvander@1648: dvander@1648: if (!CExtension::Load(error, maxlength)) dvander@1648: { dvander@1648: m_pAPI = NULL; dvander@1648: return false; dvander@1648: } dvander@1648: dvander@1648: return true; dvander@1648: } dvander@1648: dvander@1648: bool CLocalExtension::Load(char *error, size_t maxlength) dvander@941: { dvander@941: m_pLib = g_LibSys.OpenLibrary(m_Path.c_str(), error, maxlength); dvander@298: dvander@298: if (m_pLib == NULL) dvander@298: { dvander@941: return false; dvander@298: } dvander@298: dvander@298: typedef IExtensionInterface *(*GETAPI)(); dvander@298: GETAPI pfnGetAPI = NULL; dvander@298: dvander@298: if ((pfnGetAPI=(GETAPI)m_pLib->GetSymbolAddress("GetSMExtAPI")) == NULL) dvander@298: { dvander@298: m_pLib->CloseLibrary(); dvander@298: m_pLib = NULL; ds@755: snprintf(error, maxlength, "Unable to find extension entry point"); dvander@941: return false; dvander@298: } dvander@298: dvander@298: m_pAPI = pfnGetAPI(); dvander@1648: dvander@1648: /* Check pointer and version */ dvander@1648: if (!PerformAPICheck(error, maxlength)) dvander@298: { dvander@298: m_pLib->CloseLibrary(); dvander@298: m_pLib = NULL; dvander@1648: m_pAPI = NULL; dvander@941: return false; dvander@298: } dvander@298: dvander@1648: /* Load as MM:S */ dvander@298: if (m_pAPI->IsMetamodExtension()) dvander@298: { dvander@304: bool already; dvander@941: m_PlId = g_pMMPlugins->Load(m_Path.c_str(), g_PLID, already, error, maxlength); asherkin@3370: asherkin@3370: // Check the plugin didn't refuse load asherkin@3370: Pl_Status status; asherkin@3370: asherkin@3370: #ifndef METAMOD_PLAPI_VERSION asherkin@3370: const char *file; asherkin@3370: PluginId source; asherkin@3370: #endif asherkin@3370: asherkin@3370: if (!m_PlId || ( asherkin@3370: #ifndef METAMOD_PLAPI_VERSION asherkin@3370: g_pMMPlugins->Query(m_PlId, file, status, source) asherkin@3370: #else asherkin@3370: g_pMMPlugins->Query(m_PlId, NULL, &status, NULL) asherkin@3370: #endif asherkin@3370: && status < Pl_Paused)) asherkin@3370: { asherkin@3370: m_pLib->CloseLibrary(); asherkin@3370: m_pLib = NULL; asherkin@3370: m_pAPI = NULL; asherkin@3370: return false; asherkin@3370: } dvander@298: } dvander@298: dvander@1648: if (!CExtension::Load(error, maxlength)) dvander@298: { dvander@298: if (m_pAPI->IsMetamodExtension()) dvander@298: { dvander@304: if (m_PlId) dvander@304: { dvander@304: char dummy[255]; dvander@304: g_pMMPlugins->Unload(m_PlId, true, dummy, sizeof(dummy)); dvander@304: m_PlId = 0; dvander@304: } dvander@298: } dvander@1648: m_pLib->CloseLibrary(); dvander@1648: m_pLib = NULL; dvander@298: m_pAPI = NULL; dvander@1648: return false; dvander@1648: } dvander@1648: dvander@1648: return true; dvander@1648: } dvander@1648: dvander@1648: void CRemoteExtension::Unload() dvander@1648: { dvander@1648: } dvander@1648: dvander@1648: void CLocalExtension::Unload() dvander@1648: { dvander@1648: if (m_pAPI != NULL && m_PlId) dvander@1648: { dvander@1648: char temp_buffer[255]; dvander@1648: g_pMMPlugins->Unload(m_PlId, true, temp_buffer, sizeof(temp_buffer)); dvander@1648: m_PlId = 0; dvander@1648: } dvander@1648: dvander@1648: if (m_pLib != NULL) dvander@1648: { dvander@298: m_pLib->CloseLibrary(); dvander@298: m_pLib = NULL; dvander@1648: } dvander@1648: } dvander@1648: kornholijo@2778: bool CRemoteExtension::Reload(char *error, size_t maxlength) kornholijo@2778: { kornholijo@2778: snprintf(error, maxlength, "Remote extensions do not support reloading"); kornholijo@2778: return false; kornholijo@2778: } kornholijo@2778: kornholijo@2778: bool CLocalExtension::Reload(char *error, size_t maxlength) kornholijo@2778: { kornholijo@2778: if (m_pLib == NULL) // FIXME: just load it instead? kornholijo@2778: return false; kornholijo@2778: kornholijo@2778: m_pAPI->OnExtensionUnload(); kornholijo@2778: Unload(); kornholijo@2778: kornholijo@2778: return Load(error, maxlength); kornholijo@2778: } kornholijo@2778: dvander@1648: bool CRemoteExtension::IsExternal() dvander@1648: { dvander@1648: return true; dvander@1648: } dvander@1648: dvander@1648: bool CLocalExtension::IsExternal() dvander@1648: { dvander@1648: return false; dvander@1648: } dvander@1648: dvander@1648: CExtension::~CExtension() dvander@1648: { dvander@1648: DestroyIdentity(); dvander@1648: } dvander@1648: dvander@1648: bool CExtension::PerformAPICheck(char *error, size_t maxlength) dvander@1648: { dvander@1668: if (!m_pAPI) dvander@1668: { dvander@1668: snprintf(error, maxlength, "No IExtensionInterface instance provided"); dvander@1668: return false; dvander@1668: } dvander@1668: dvander@1668: if (m_pAPI->GetExtensionVersion() > SMINTERFACE_EXTENSIONAPI_VERSION) dvander@1648: { dvander@1648: snprintf(error, maxlength, "Extension version is too new to load (%d, max is %d)", m_pAPI->GetExtensionVersion(), SMINTERFACE_EXTENSIONAPI_VERSION); dvander@941: return false; dvander@1648: } dvander@1648: dvander@1648: return true; dvander@1648: } dvander@1648: dvander@1648: bool CExtension::Load(char *error, size_t maxlength) dvander@1648: { dvander@1648: CreateIdentity(); dvander@1648: if (!m_pAPI->OnExtensionLoad(this, &g_ShareSys, error, maxlength, !g_SourceMod.IsMapLoading())) dvander@1648: { dvander@1648: DestroyIdentity(); dvander@1648: return false; dvander@1648: } dvander@1648: else dvander@1648: { dvander@315: /* Check if we're past load time */ dvander@315: if (!g_SourceMod.IsMapLoading()) dvander@315: { dvander@315: m_pAPI->OnExtensionsAllLoaded(); dvander@315: } dvander@298: } dvander@941: dvander@941: return true; dvander@298: } dvander@298: dvander@1648: void CExtension::CreateIdentity() dvander@298: { dvander@1648: if (m_pIdentToken != NULL) dvander@298: { dvander@1648: return; dvander@298: } dvander@298: dvander@1648: m_pIdentToken = g_ShareSys.CreateIdentity(g_ExtType, this); dvander@1648: } dvander@1648: dvander@1648: void CExtension::DestroyIdentity() dvander@1648: { dvander@1648: if (m_pIdentToken == NULL) dvander@298: { dvander@1648: return; dvander@298: } dvander@298: dvander@1648: g_ShareSys.DestroyIdentity(m_pIdentToken); dvander@1648: m_pIdentToken = NULL; dvander@298: } dvander@298: dvander@315: void CExtension::MarkAllLoaded() dvander@315: { dvander@1648: assert(m_bFullyLoaded == false); dvander@1648: if (!m_bFullyLoaded) dvander@315: { dvander@1648: m_bFullyLoaded = true; dvander@315: m_pAPI->OnExtensionsAllLoaded(); dvander@315: } dvander@315: } dvander@315: dvander@2086: void CExtension::AddPlugin(CPlugin *pPlugin) dvander@305: { dvander@868: /* Unfortunately we have to do this :( */ dvander@2086: if (m_Dependents.find(pPlugin) != m_Dependents.end()) dvander@1121: { dvander@2086: m_Dependents.push_back(pPlugin); dvander@1121: } dvander@305: } dvander@305: dvander@298: void CExtension::SetError(const char *error) dvander@298: { dvander@298: m_Error.assign(error); dvander@298: } dvander@298: dvander@298: IExtensionInterface *CExtension::GetAPI() dvander@298: { dvander@298: return m_pAPI; dvander@298: } dvander@298: dvander@298: const char *CExtension::GetFilename() dvander@298: { dvander@2782: return m_RealFile.c_str(); dvander@298: } dvander@298: skamonkey@3402: const char *CExtension::GetPath() const skamonkey@3402: { skamonkey@3402: return m_Path.c_str(); skamonkey@3402: } skamonkey@3402: dvander@298: IdentityToken_t *CExtension::GetIdentity() dvander@298: { dvander@298: return m_pIdentToken; dvander@298: } dvander@298: dvander@1648: bool CRemoteExtension::IsLoaded() dvander@1648: { dvander@1648: return (m_pAPI != NULL); dvander@1648: } dvander@1648: dvander@1648: bool CLocalExtension::IsLoaded() dvander@298: { dvander@298: return (m_pLib != NULL); dvander@298: } dvander@298: dvander@2134: void CExtension::AddDependency(const IfaceInfo *pInfo) dvander@304: { dvander@1456: if (m_Deps.find(*pInfo) == m_Deps.end()) dvander@1456: { dvander@1456: m_Deps.push_back(*pInfo); dvander@1456: } dvander@304: } dvander@304: dvander@1150: bool operator ==(const IfaceInfo &i1, const IfaceInfo &i2) dvander@1150: { dvander@1150: return (i1.iface == i2.iface) && (i1.owner == i2.owner); dvander@1150: } dvander@1150: dvander@1150: void CExtension::AddChildDependent(CExtension *pOther, SMInterface *iface) dvander@1150: { dvander@1150: IfaceInfo info; dvander@1150: info.iface = iface; dvander@1150: info.owner = pOther; dvander@1150: dvander@1150: List::iterator iter; dvander@1150: for (iter = m_ChildDeps.begin(); dvander@1150: iter != m_ChildDeps.end(); dvander@1150: iter++) dvander@1150: { dvander@1150: IfaceInfo &other = (*iter); dvander@1150: if (other == info) dvander@1150: { dvander@1150: return; dvander@1150: } dvander@1150: } dvander@1150: dvander@1150: m_ChildDeps.push_back(info); dvander@1150: } dvander@1150: dvander@304: ITERATOR *CExtension::FindFirstDependency(IExtension **pOwner, SMInterface **pInterface) dvander@304: { dvander@304: List::iterator iter = m_Deps.begin(); dvander@304: dvander@304: if (iter == m_Deps.end()) dvander@304: { dvander@304: return NULL; dvander@304: } dvander@304: dvander@304: if (pOwner) dvander@304: { dvander@304: *pOwner = (*iter).owner; dvander@304: } dvander@304: if (pInterface) dvander@304: { dvander@304: *pInterface = (*iter).iface; dvander@304: } dvander@304: dvander@304: List::iterator *pIter = new List::iterator(iter); dvander@304: dvander@304: return (ITERATOR *)pIter; dvander@304: } dvander@304: dvander@304: bool CExtension::FindNextDependency(ITERATOR *iter, IExtension **pOwner, SMInterface **pInterface) dvander@304: { dvander@304: List::iterator *pIter = (List::iterator *)iter; dvander@304: List::iterator _iter; dvander@304: dvander@304: if (_iter == m_Deps.end()) dvander@304: { dvander@304: return false; dvander@304: } dvander@304: dvander@304: _iter++; dvander@304: dvander@304: if (pOwner) dvander@304: { dvander@304: *pOwner = (*_iter).owner; dvander@304: } dvander@304: if (pInterface) dvander@304: { dvander@304: *pInterface = (*_iter).iface; dvander@304: } dvander@304: dvander@304: *pIter = _iter; dvander@304: dvander@304: if (_iter == m_Deps.end()) dvander@304: { dvander@304: return false; dvander@304: } dvander@304: dvander@304: return true; dvander@304: } dvander@304: dvander@304: void CExtension::FreeDependencyIterator(ITERATOR *iter) dvander@304: { dvander@304: List::iterator *pIter = (List::iterator *)iter; dvander@304: dvander@304: delete pIter; dvander@304: } dvander@304: dvander@304: void CExtension::AddInterface(SMInterface *pInterface) dvander@304: { dvander@304: m_Interfaces.push_back(pInterface); dvander@304: } dvander@304: dvander@315: bool CExtension::IsRunning(char *error, size_t maxlength) dvander@315: { dvander@315: if (!IsLoaded()) dvander@315: { dvander@315: if (error) dvander@315: { dvander@315: snprintf(error, maxlength, "%s", m_Error.c_str()); dvander@315: } dvander@315: return false; dvander@315: } dvander@315: dvander@315: return m_pAPI->QueryRunning(error, maxlength); dvander@315: } dvander@315: dvander@1572: void CExtension::AddLibrary(const char *library) dvander@1572: { dvander@1572: m_Libraries.push_back(library); dvander@1572: } dvander@1572: dvander@304: /********************* dvander@304: * EXTENSION MANAGER * dvander@304: *********************/ dvander@304: dvander@298: void CExtensionManager::OnSourceModAllInitialized() dvander@298: { dvander@298: g_ExtType = g_ShareSys.CreateIdentType("EXTENSION"); dvander@305: g_PluginSys.AddPluginsListener(this); dvander@310: g_RootMenu.AddRootConsoleCommand("exts", "Manage extensions", this); dvander@336: g_ShareSys.AddInterface(NULL, this); dvander@298: } dvander@298: dvander@298: void CExtensionManager::OnSourceModShutdown() dvander@298: { dvander@310: g_RootMenu.RemoveRootConsoleCommand("exts", this); dvander@298: g_ShareSys.DestroyIdentType(g_ExtType); dvander@298: } dvander@298: dvander@1018: void CExtensionManager::Shutdown() dvander@1018: { dvander@1018: List::iterator iter; dvander@1018: dvander@1018: while ((iter = m_Libs.begin()) != m_Libs.end()) dvander@1018: { dvander@1018: UnloadExtension((*iter)); dvander@1018: } dvander@1018: } dvander@1018: dvander@397: void CExtensionManager::TryAutoload() dvander@397: { dvander@397: char path[PLATFORM_MAX_PATH]; dvander@397: dvander@397: g_SourceMod.BuildPath(Path_SM, path, sizeof(path), "extensions"); dvander@397: dvander@397: IDirectory *pDir = g_LibSys.OpenDirectory(path); dvander@397: if (!pDir) dvander@397: { dvander@397: return; dvander@397: } dvander@397: dvander@397: const char *lfile; dvander@397: size_t len; dvander@397: while (pDir->MoreFiles()) dvander@397: { dvander@397: if (pDir->IsEntryDirectory()) dvander@397: { dvander@397: pDir->NextEntry(); dvander@397: continue; dvander@397: } dvander@397: dvander@397: lfile = pDir->GetEntryName(); dvander@397: len = strlen(lfile); dvander@397: if (len <= 9) /* size of ".autoload" */ dvander@397: { dvander@397: pDir->NextEntry(); dvander@397: continue; dvander@397: } dvander@397: dvander@397: if (strcmp(&lfile[len - 9], ".autoload") != 0) dvander@397: { dvander@397: pDir->NextEntry(); dvander@397: continue; dvander@397: } dvander@397: dvander@397: char file[PLATFORM_MAX_PATH]; dvander@397: len = UTIL_Format(file, sizeof(file), "%s", lfile); dvander@2782: strcpy(&file[len - 9], ".ext"); dvander@397: dvander@397: LoadAutoExtension(file); dvander@397: dvander@397: pDir->NextEntry(); dvander@397: } dvander@397: } dvander@397: skamonkey@3402: IExtension *CExtensionManager::LoadAutoExtension(const char *path, bool bErrorOnMissing) dvander@298: { dvander@2782: /* Remove platform extension if it's there. Compat hack. */ dvander@2782: const char *ext = g_LibSys.GetFileExtension(path); dvander@2782: if (strcmp(ext, PLATFORM_LIB_EXT) == 0) dvander@315: { dvander@2782: char path2[PLATFORM_MAX_PATH]; dvander@2782: UTIL_Format(path2, sizeof(path2), "%s", path); dvander@2782: path2[strlen(path) - strlen(PLATFORM_LIB_EXT) - 1] = '\0'; skamonkey@3402: return LoadAutoExtension(path2, bErrorOnMissing); dvander@315: } dvander@315: dvander@304: IExtension *pAlready; dvander@304: if ((pAlready=FindExtensionByFile(path)) != NULL) dvander@304: { dvander@304: return pAlready; dvander@304: } dvander@304: dvander@298: char error[256]; dvander@1648: CExtension *p = new CLocalExtension(path); dvander@298: dvander@941: /* We put us in the list beforehand so extensions that check for each other dvander@941: * won't recursively load each other. dvander@941: */ dvander@941: m_Libs.push_back(p); dvander@941: dvander@941: if (!p->Load(error, sizeof(error)) || !p->IsLoaded()) dvander@298: { skamonkey@3402: if (bErrorOnMissing || g_LibSys.IsPathFile(p->GetPath())) skamonkey@3402: { skamonkey@3402: g_Logger.LogError("[SM] Unable to load extension \"%s\": %s", path, error); skamonkey@3402: } skamonkey@3402: dvander@298: p->SetError(error); dvander@298: } dvander@298: dvander@298: return p; dvander@298: } dvander@298: dvander@298: IExtension *CExtensionManager::FindExtensionByFile(const char *file) dvander@298: { dvander@298: List::iterator iter; dvander@298: CExtension *pExt; dvander@298: dvander@1508: /* Chomp off the path */ dvander@1508: char lookup[PLATFORM_MAX_PATH]; dvander@1508: g_LibSys.GetFileFromPath(lookup, sizeof(lookup), file); dvander@298: dvander@298: for (iter=m_Libs.begin(); iter!=m_Libs.end(); iter++) dvander@298: { dvander@298: pExt = (*iter); dvander@2782: if (pExt->IsSameFile(lookup)) dvander@298: { dvander@298: return pExt; dvander@298: } dvander@298: } dvander@298: dvander@298: return NULL; dvander@298: } dvander@298: dvander@298: IExtension *CExtensionManager::FindExtensionByName(const char *ext) dvander@298: { dvander@298: List::iterator iter; dvander@298: CExtension *pExt; dvander@298: IExtensionInterface *pAPI; dvander@298: const char *name; dvander@298: dvander@298: for (iter=m_Libs.begin(); iter!=m_Libs.end(); iter++) dvander@298: { dvander@298: pExt = (*iter); dvander@298: if (!pExt->IsLoaded()) dvander@298: { dvander@298: continue; dvander@298: } dvander@298: if ((pAPI = pExt->GetAPI()) == NULL) dvander@298: { dvander@298: continue; dvander@298: } dvander@298: name = pAPI->GetExtensionName(); dvander@298: if (!name) dvander@298: { dvander@298: continue; dvander@298: } dvander@298: if (strcmp(name, ext) == 0) dvander@298: { dvander@298: return pExt; dvander@298: } dvander@298: } dvander@298: dvander@298: return NULL; dvander@298: } dvander@298: dvander@1648: IExtension *CExtensionManager::LoadExtension(const char *file, char *error, size_t maxlength) dvander@298: { dvander@2782: /* Remove platform extension if it's there. Compat hack. */ dvander@2782: const char *ext = g_LibSys.GetFileExtension(file); dvander@2782: if (strcmp(ext, PLATFORM_LIB_EXT) == 0) dvander@2782: { dvander@2782: char path2[PLATFORM_MAX_PATH]; dvander@2782: UTIL_Format(path2, sizeof(path2), "%s", file); dvander@2782: path2[strlen(file) - strlen(PLATFORM_LIB_EXT) - 1] = '\0'; dvander@2782: return LoadExtension(path2, error, maxlength); dvander@2782: } dvander@2782: dvander@304: IExtension *pAlready; dvander@304: if ((pAlready=FindExtensionByFile(file)) != NULL) dvander@304: { dvander@304: return pAlready; dvander@304: } dvander@304: dvander@1648: CExtension *pExt = new CLocalExtension(file); dvander@298: dvander@941: if (!pExt->Load(error, maxlength) || !pExt->IsLoaded()) dvander@298: { dvander@1648: pExt->Unload(); dvander@298: delete pExt; dvander@298: return NULL; dvander@298: } dvander@298: dvander@315: /* :TODO: do QueryRunning check if the map is loaded */ dvander@315: dvander@298: m_Libs.push_back(pExt); dvander@298: dvander@298: return pExt; dvander@298: } dvander@298: dvander@1150: void CExtensionManager::BindDependency(IExtension *pRequester, IfaceInfo *pInfo) dvander@298: { dvander@1150: CExtension *pExt = (CExtension *)pRequester; dvander@1150: CExtension *pOwner = (CExtension *)pInfo->owner; dvander@304: dvander@304: pExt->AddDependency(pInfo); dvander@1150: dvander@1150: IExtensionInterface *pAPI = pExt->GetAPI(); dvander@1150: if (pAPI && !pAPI->QueryInterfaceDrop(pInfo->iface)) dvander@1150: { dvander@1150: pOwner->AddChildDependent(pExt, pInfo->iface); dvander@1150: } dvander@304: } dvander@304: dvander@2134: void CExtensionManager::AddRawDependency(IExtension *ext, IdentityToken_t *other, void *iface) dvander@2134: { dvander@2134: CExtension *pExt = (CExtension *)ext; dvander@2134: CExtension *pOwner = GetExtensionFromIdent(other); dvander@2134: dvander@2134: IfaceInfo info; dvander@2134: dvander@2134: info.iface = (SMInterface *)iface; dvander@2134: info.owner = pOwner; dvander@2134: dvander@2134: pExt->AddDependency(&info); dvander@2134: pOwner->AddChildDependent(pExt, (SMInterface *)iface); dvander@2134: } dvander@2134: dvander@304: void CExtensionManager::AddInterface(IExtension *pOwner, SMInterface *pInterface) dvander@304: { dvander@304: CExtension *pExt = (CExtension *)pOwner; dvander@304: dvander@304: pExt->AddInterface(pInterface); dvander@298: } dvander@298: dvander@2086: void CExtensionManager::BindChildPlugin( IExtension *pParent, CPlugin *pPlugin ) dvander@305: { dvander@305: CExtension *pExt = (CExtension *)pParent; dvander@305: dvander@305: pExt->AddPlugin(pPlugin); dvander@305: } dvander@305: dvander@305: void CExtensionManager::OnPluginDestroyed(IPlugin *plugin) dvander@305: { dvander@305: List::iterator iter; dvander@305: dvander@305: for (iter=m_Libs.begin(); iter!=m_Libs.end(); iter++) dvander@305: { dvander@2086: (*iter)->DropRefsTo((CPlugin *)plugin); dvander@305: } dvander@305: } dvander@305: dvander@311: CExtension *CExtensionManager::FindByOrder(unsigned int num) dvander@311: { dvander@311: if (num < 1 || num > m_Libs.size()) dvander@311: { dvander@311: return NULL; dvander@311: } dvander@311: dvander@311: List::iterator iter = m_Libs.begin(); dvander@311: dvander@311: while (iter != m_Libs.end()) dvander@311: { dvander@311: if (--num == 0) dvander@311: { dvander@311: return (*iter); dvander@311: } dvander@311: iter++; dvander@311: } dvander@311: dvander@311: return NULL; dvander@311: } dvander@311: dvander@304: bool CExtensionManager::UnloadExtension(IExtension *_pExt) dvander@298: { dvander@304: if (!_pExt) dvander@304: { dvander@304: return false; dvander@304: } dvander@304: dvander@304: CExtension *pExt = (CExtension *)_pExt; dvander@304: dvander@304: if (m_Libs.find(pExt) == m_Libs.end()) dvander@304: { dvander@304: return false; dvander@304: } dvander@304: dvander@304: /* First remove us from internal lists */ dvander@304: g_ShareSys.RemoveInterfaces(_pExt); dvander@304: m_Libs.remove(pExt); dvander@304: dvander@304: List UnloadQueue; dvander@298: dvander@304: /* Handle dependencies */ dvander@304: if (pExt->IsLoaded()) dvander@304: { dvander@305: /* Unload any dependent plugins */ dvander@2086: List::iterator p_iter = pExt->m_Dependents.begin(); dvander@2086: while (p_iter != pExt->m_Dependents.end()) dvander@305: { dvander@311: /* We have to manually unlink ourselves here, since we're no longer being managed */ dvander@305: g_PluginSys.UnloadPlugin((*p_iter)); dvander@2086: p_iter = pExt->m_Dependents.erase(p_iter); dvander@305: } dvander@305: dvander@1572: List::iterator s_iter; dvander@1572: for (s_iter = pExt->m_Libraries.begin(); dvander@1572: s_iter != pExt->m_Libraries.end(); dvander@1572: s_iter++) dvander@1572: { dvander@1572: g_PluginSys.OnLibraryAction((*s_iter).c_str(), false, true); dvander@1572: } dvander@1572: dvander@304: /* Notify and/or unload all dependencies */ dvander@304: List::iterator c_iter; dvander@304: CExtension *pDep; dvander@304: IExtensionInterface *pAPI; dvander@304: for (c_iter = m_Libs.begin(); c_iter != m_Libs.end(); c_iter++) dvander@304: { dvander@304: pDep = (*c_iter); dvander@304: if ((pAPI=pDep->GetAPI()) == NULL) dvander@304: { dvander@304: continue; dvander@304: } dvander@311: if (pDep == pExt) dvander@311: { dvander@311: continue; dvander@311: } dvander@304: /* Now, get its dependency list */ dvander@304: bool dropped = false; dvander@304: List::iterator i_iter = pDep->m_Deps.begin(); dvander@304: while (i_iter != pDep->m_Deps.end()) dvander@304: { dvander@304: if ((*i_iter).owner == _pExt) dvander@304: { dvander@1150: if (!pAPI->QueryInterfaceDrop((*i_iter).iface)) dvander@304: { dvander@1150: if (!dropped) dvander@1150: { dvander@1150: dropped = true; dvander@1150: UnloadQueue.push_back(pDep); dvander@1150: } dvander@304: } dvander@304: pAPI->NotifyInterfaceDrop((*i_iter).iface); dvander@304: i_iter = pDep->m_Deps.erase(i_iter); dvander@1572: } dvander@1572: else dvander@1572: { dvander@304: i_iter++; dvander@304: } dvander@304: } dvander@1150: /* Flush out any back references to this plugin */ dvander@1150: i_iter = pDep->m_ChildDeps.begin(); dvander@1150: while (i_iter != pDep->m_ChildDeps.end()) dvander@1150: { dvander@1150: if ((*i_iter).owner == pExt) dvander@1150: { dvander@1150: i_iter = pDep->m_ChildDeps.erase(i_iter); dvander@1572: } dvander@1572: else dvander@1572: { dvander@1150: i_iter++; dvander@1150: } dvander@1150: } dvander@304: } dvander@1202: dvander@1391: /* Unbind our natives from Core */ dvander@2086: pExt->DropEverything(); dvander@304: } dvander@304: dvander@1157: IdentityToken_t *pIdentity; dvander@1157: if ((pIdentity = pExt->GetIdentity()) != NULL) dvander@1157: { dvander@1157: SMGlobalClass *glob = SMGlobalClass::head; dvander@1157: while (glob) dvander@1157: { dvander@1157: glob->OnSourceModIdentityDropped(pIdentity); dvander@1157: glob = glob->m_pGlobalClassNext; dvander@1157: } dvander@1157: } dvander@1157: pred@2204: /* Tell it to unload */ pred@2204: if (pExt->IsLoaded()) pred@2204: { pred@2204: IExtensionInterface *pAPI = pExt->GetAPI(); pred@2204: pAPI->OnExtensionUnload(); pred@2204: } pred@2204: dvander@1648: pExt->Unload(); dvander@1018: delete pExt; dvander@1018: dvander@304: List::iterator iter; dvander@304: for (iter=UnloadQueue.begin(); iter!=UnloadQueue.end(); iter++) dvander@304: { dvander@304: /* NOTE: This is safe because the unload function backs out of anything not present */ dvander@304: UnloadExtension((*iter)); dvander@304: } dvander@304: dvander@298: return true; dvander@298: } dvander@310: dvander@315: void CExtensionManager::MarkAllLoaded() dvander@315: { dvander@315: List::iterator iter; dvander@315: CExtension *pExt; dvander@315: dvander@315: for (iter=m_Libs.begin(); iter!=m_Libs.end(); iter++) dvander@315: { dvander@315: pExt = (*iter); dvander@315: if (!pExt->IsLoaded()) dvander@315: { dvander@315: continue; dvander@315: } dvander@1648: if (pExt->m_bFullyLoaded) dvander@315: { dvander@315: continue; dvander@315: } dvander@315: pExt->MarkAllLoaded(); dvander@315: } dvander@315: } dvander@315: dvander@324: void CExtensionManager::AddDependency(IExtension *pSource, const char *file, bool required, bool autoload) dvander@324: { dvander@941: /* This function doesn't really need to do anything now. We make sure the dvander@941: * other extension is loaded, but handling of dependencies is really done dvander@941: * by the interface fetcher. dvander@941: */ dvander@941: if (required || autoload) dvander@941: { dvander@941: LoadAutoExtension(file); dvander@941: } dvander@324: } dvander@324: faluco@1541: void CExtensionManager::OnRootConsoleCommand(const char *cmdname, const CCommand &command) dvander@310: { faluco@1541: int argcount = command.ArgC(); dvander@310: if (argcount >= 3) dvander@310: { faluco@1541: const char *cmd = command.Arg(2); dvander@310: if (strcmp(cmd, "list") == 0) dvander@310: { dvander@310: List::iterator iter; dvander@310: CExtension *pExt; dvander@310: unsigned int num = 1; dvander@310: switch (m_Libs.size()) dvander@310: { dvander@310: case 1: dvander@310: { dvander@310: g_RootMenu.ConsolePrint("[SM] Displaying 1 extension:"); dvander@310: break; dvander@310: } dvander@310: case 0: dvander@310: { dvander@310: g_RootMenu.ConsolePrint("[SM] No extensions are loaded."); dvander@310: break; dvander@310: } dvander@310: default: dvander@310: { dvander@310: g_RootMenu.ConsolePrint("[SM] Displaying %d extensions:", m_Libs.size()); dvander@310: break; dvander@310: } dvander@310: } dvander@310: for (iter=m_Libs.begin(); iter!=m_Libs.end(); iter++,num++) dvander@310: { dvander@310: pExt = (*iter); dvander@310: if (pExt->IsLoaded()) dvander@310: { dvander@315: char error[255]; dvander@315: if (!pExt->IsRunning(error, sizeof(error))) dvander@315: { dvander@315: g_RootMenu.ConsolePrint("[%02d] file \"%s\": %s", num, pExt->GetFilename(), error); dvander@1648: } dvander@1648: else dvander@1648: { dvander@315: IExtensionInterface *pAPI = pExt->GetAPI(); dvander@315: const char *name = pAPI->GetExtensionName(); dvander@315: const char *version = pAPI->GetExtensionVerString(); dvander@315: const char *descr = pAPI->GetExtensionDescription(); dvander@315: g_RootMenu.ConsolePrint("[%02d] %s (%s): %s", num, name, version, descr); dvander@315: } dvander@310: } else { dvander@310: g_RootMenu.ConsolePrint("[%02d] file \"%s\": %s", num, pExt->GetFilename(), pExt->m_Error.c_str()); dvander@310: } dvander@310: } dvander@310: return; dvander@1648: } kornholijo@2778: else if (strcmp(cmd, "load") == 0) kornholijo@2778: { kornholijo@2778: if (argcount < 4) kornholijo@2778: { kornholijo@2778: g_RootMenu.ConsolePrint("[SM] Usage: sm exts load "); kornholijo@2778: return; kornholijo@2778: } kornholijo@2778: kornholijo@2778: const char *filename = command.Arg(3); kornholijo@2778: char path[PLATFORM_MAX_PATH]; kornholijo@2778: char error[256]; kornholijo@2778: kornholijo@2778: UTIL_Format(path, sizeof(path), "%s%s%s", filename, !strstr(filename, ".ext") ? ".ext" : "", kornholijo@2778: !strstr(filename, "." PLATFORM_LIB_EXT) ? "." PLATFORM_LIB_EXT : ""); kornholijo@2778: kornholijo@2778: if (FindExtensionByFile(path) != NULL) kornholijo@2778: { kornholijo@2778: g_RootMenu.ConsolePrint("[SM] Extension %s is already loaded.", path); kornholijo@2778: return; kornholijo@2778: } kornholijo@2778: kornholijo@2778: if (LoadExtension(path, error, sizeof(error))) kornholijo@2778: { kornholijo@2778: g_RootMenu.ConsolePrint("[SM] Loaded extension %s successfully.", path); kornholijo@2778: } else kornholijo@2778: { kornholijo@2778: g_RootMenu.ConsolePrint("[SM] Extension %s failed to load: %s", path, error); kornholijo@2778: } kornholijo@2778: kornholijo@2778: return; kornholijo@2778: } dvander@1648: else if (strcmp(cmd, "info") == 0) dvander@1648: { dvander@310: if (argcount < 4) dvander@310: { erik@3108: g_RootMenu.ConsolePrint("[SM] Usage: sm exts info <#>"); dvander@310: return; dvander@310: } dvander@310: faluco@1541: const char *sId = command.Arg(3); dvander@310: unsigned int id = atoi(sId); dvander@310: if (id <= 0) dvander@310: { erik@3108: g_RootMenu.ConsolePrint("[SM] Usage: sm exts info <#>"); dvander@310: return; dvander@310: } dvander@310: dvander@310: if (m_Libs.size() == 0) dvander@310: { dvander@310: g_RootMenu.ConsolePrint("[SM] No extensions are loaded."); dvander@310: return; dvander@310: } dvander@310: dvander@310: if (id > m_Libs.size()) dvander@310: { dvander@310: g_RootMenu.ConsolePrint("[SM] No extension was found with id %d.", id); dvander@310: return; dvander@310: } dvander@310: dvander@310: List::iterator iter = m_Libs.begin(); dvander@310: CExtension *pExt = NULL; dvander@310: while (iter != m_Libs.end()) dvander@310: { dvander@310: if (--id == 0) dvander@310: { dvander@310: pExt = (*iter); dvander@310: break; dvander@310: } dvander@310: iter++; dvander@310: } dvander@310: /* This should never happen */ dvander@310: if (!pExt) dvander@310: { dvander@310: g_RootMenu.ConsolePrint("[SM] No extension was found with id %d.", id); dvander@310: return; dvander@310: } dvander@310: dvander@310: if (!pExt->IsLoaded()) dvander@310: { dvander@310: g_RootMenu.ConsolePrint(" File: %s", pExt->GetFilename()); dvander@310: g_RootMenu.ConsolePrint(" Loaded: No (%s)", pExt->m_Error.c_str()); dvander@1648: } dvander@1648: else dvander@1648: { dvander@315: char error[255]; dvander@315: if (!pExt->IsRunning(error, sizeof(error))) dvander@315: { dvander@315: g_RootMenu.ConsolePrint(" File: %s", pExt->GetFilename()); dvander@315: g_RootMenu.ConsolePrint(" Loaded: Yes"); dvander@315: g_RootMenu.ConsolePrint(" Running: No (%s)", error); dvander@1648: } dvander@1648: else dvander@1648: { dvander@315: IExtensionInterface *pAPI = pExt->GetAPI(); dvander@315: g_RootMenu.ConsolePrint(" File: %s", pExt->GetFilename()); dvander@315: g_RootMenu.ConsolePrint(" Loaded: Yes (version %s)", pAPI->GetExtensionVerString()); dvander@315: g_RootMenu.ConsolePrint(" Name: %s (%s)", pAPI->GetExtensionName(), pAPI->GetExtensionDescription()); dvander@315: g_RootMenu.ConsolePrint(" Author: %s (%s)", pAPI->GetExtensionAuthor(), pAPI->GetExtensionURL()); dvander@315: g_RootMenu.ConsolePrint(" Binary info: API version %d (compiled %s)", pAPI->GetExtensionVersion(), pAPI->GetExtensionDateString()); dvander@1648: dvander@1648: if (pExt->IsExternal()) dvander@1648: { dvander@1648: g_RootMenu.ConsolePrint(" Method: Loaded by Metamod:Source, attached to SourceMod"); dvander@1648: } dvander@1648: else if (pAPI->IsMetamodExtension()) dvander@1648: { dvander@1648: g_RootMenu.ConsolePrint(" Method: Loaded by SourceMod, attached to Metamod:Source"); dvander@1648: } dvander@1648: else dvander@1648: { dvander@1648: g_RootMenu.ConsolePrint(" Method: Loaded by SourceMod"); dvander@1648: } dvander@315: } dvander@310: } dvander@310: return; dvander@1648: } dvander@1648: else if (strcmp(cmd, "unload") == 0) dvander@1648: { dvander@311: if (argcount < 4) dvander@311: { erik@3108: g_RootMenu.ConsolePrint("[SM] Usage: sm exts unload <#> [code]"); dvander@311: return; dvander@311: } dvander@311: faluco@1541: const char *arg = command.Arg(3); dvander@311: unsigned int num = atoi(arg); dvander@311: CExtension *pExt = FindByOrder(num); dvander@311: dvander@311: if (!pExt) dvander@311: { dvander@311: g_RootMenu.ConsolePrint("[SM] Extension number %d was not found.", num); dvander@311: return; dvander@311: } dvander@311: dvander@311: if (argcount > 4 && pExt->unload_code) dvander@311: { faluco@1541: const char *unload = command.Arg(4); dvander@351: if (pExt->unload_code == (unsigned)atoi(unload)) dvander@311: { ds@652: char filename[PLATFORM_MAX_PATH]; dvander@311: snprintf(filename, PLATFORM_MAX_PATH, "%s", pExt->GetFilename()); dvander@311: UnloadExtension(pExt); dvander@311: g_RootMenu.ConsolePrint("[SM] Extension %s is now unloaded.", filename); dvander@1456: } dvander@1456: else dvander@1456: { dvander@311: g_RootMenu.ConsolePrint("[SM] Please try again, the correct unload code is \"%d\"", pExt->unload_code); dvander@311: } dvander@311: return; dvander@311: } dvander@311: dvander@311: if (!pExt->IsLoaded() dvander@2086: || (!pExt->m_ChildDeps.size() && !pExt->m_Dependents.size())) dvander@311: { ds@652: char filename[PLATFORM_MAX_PATH]; dvander@311: snprintf(filename, PLATFORM_MAX_PATH, "%s", pExt->GetFilename()); dvander@311: UnloadExtension(pExt); dvander@311: g_RootMenu.ConsolePrint("[SM] Extension %s is now unloaded.", filename); dvander@311: return; dvander@1456: } dvander@1456: else dvander@1456: { dvander@2086: List plugins; dvander@1150: if (pExt->m_ChildDeps.size()) dvander@311: { dvander@311: g_RootMenu.ConsolePrint("[SM] Unloading %s will unload the following extensions: ", pExt->GetFilename()); dvander@311: List::iterator iter; dvander@311: CExtension *pOther; dvander@311: /* Get list of all extensions */ dvander@311: for (iter=m_Libs.begin(); iter!=m_Libs.end(); iter++) dvander@311: { dvander@311: List::iterator i_iter; dvander@311: pOther = (*iter); dvander@311: if (!pOther->IsLoaded() || pOther == pExt) dvander@311: { dvander@311: continue; dvander@311: } dvander@311: /* Get their dependencies */ dvander@311: for (i_iter=pOther->m_Deps.begin(); dvander@311: i_iter!=pOther->m_Deps.end(); dvander@311: i_iter++) dvander@311: { dvander@311: /* Is this dependency to us? */ dvander@311: if ((*i_iter).owner != pExt) dvander@311: { dvander@311: continue; dvander@311: } dvander@311: /* Will our dependent care? */ dvander@311: if (!pExt->GetAPI()->QueryInterfaceDrop((*i_iter).iface)) dvander@311: { dvander@311: g_RootMenu.ConsolePrint(" -> %s", pExt->GetFilename()); dvander@311: /* Add to plugin unload list */ dvander@2086: List::iterator p_iter; dvander@2086: for (p_iter=pOther->m_Dependents.begin(); dvander@2086: p_iter!=pOther->m_Dependents.end(); dvander@311: p_iter++) dvander@311: { dvander@311: if (plugins.find((*p_iter)) == plugins.end()) dvander@311: { dvander@311: plugins.push_back((*p_iter)); dvander@311: } dvander@311: } dvander@311: } dvander@311: } dvander@311: } dvander@311: } dvander@2086: if (pExt->m_Dependents.size()) dvander@311: { dvander@311: g_RootMenu.ConsolePrint("[SM] Unloading %s will unload the following plugins: ", pExt->GetFilename()); dvander@2086: List::iterator iter; dvander@2086: CPlugin *pPlugin; dvander@2086: for (iter = pExt->m_Dependents.begin(); iter != pExt->m_Dependents.end(); iter++) dvander@311: { dvander@311: pPlugin = (*iter); dvander@311: if (plugins.find(pPlugin) == plugins.end()) dvander@311: { dvander@311: plugins.push_back(pPlugin); dvander@311: } dvander@311: } dvander@311: for (iter = plugins.begin(); iter != plugins.end(); iter++) dvander@311: { dvander@311: pPlugin = (*iter); dvander@311: g_RootMenu.ConsolePrint(" -> %s", pPlugin->GetFilename()); dvander@311: } dvander@311: } dvander@311: srand(static_cast(time(NULL))); dvander@311: pExt->unload_code = (rand() % 877) + 123; //123 to 999 dvander@311: g_RootMenu.ConsolePrint("[SM] To verify unloading %s, please use the following: ", pExt->GetFilename()); dvander@311: g_RootMenu.ConsolePrint("[SM] sm exts unload %d %d", num, pExt->unload_code); dvander@311: dvander@311: return; dvander@311: } dvander@310: } kornholijo@2778: else if (strcmp(cmd, "reload") == 0) kornholijo@2778: { kornholijo@2778: if (argcount < 4) kornholijo@2778: { kornholijo@2778: g_RootMenu.ConsolePrint("[SM] Usage: sm exts reload <#>"); kornholijo@2778: return; kornholijo@2778: } kornholijo@2778: kornholijo@2778: const char *arg = command.Arg(3); kornholijo@2778: unsigned int num = atoi(arg); kornholijo@2778: CExtension *pExt = FindByOrder(num); kornholijo@2778: kornholijo@2778: if (!pExt) kornholijo@2778: { kornholijo@2778: g_RootMenu.ConsolePrint("[SM] Extension number %d was not found.", num); kornholijo@2778: return; kornholijo@2778: } kornholijo@2778: kornholijo@2778: if (pExt->IsLoaded()) kornholijo@2778: { kornholijo@2778: char filename[PLATFORM_MAX_PATH]; kornholijo@2778: char error[255]; kornholijo@2778: kornholijo@2778: snprintf(filename, PLATFORM_MAX_PATH, "%s", pExt->GetFilename()); kornholijo@2778: kornholijo@2778: if (pExt->Reload(error, sizeof(error))) kornholijo@2778: { kornholijo@2778: g_RootMenu.ConsolePrint("[SM] Extension %s is now reloaded.", filename); kornholijo@2778: } kornholijo@2778: else kornholijo@2778: { kornholijo@2778: g_RootMenu.ConsolePrint("[SM] Extension %s failed to reload: %s", filename, error); kornholijo@2778: } kornholijo@2778: kornholijo@2778: return; dvander@2782: } kornholijo@2778: else kornholijo@2778: { kornholijo@2778: g_RootMenu.ConsolePrint("[SM] Extension %s is not loaded.", pExt->GetFilename()); kornholijo@2778: kornholijo@2778: return; kornholijo@2778: } kornholijo@2778: kornholijo@2778: } dvander@310: } dvander@310: dvander@310: g_RootMenu.ConsolePrint("SourceMod Extensions Menu:"); dvander@310: g_RootMenu.DrawGenericOption("info", "Extra extension information"); dvander@310: g_RootMenu.DrawGenericOption("list", "List extensions"); kornholijo@2778: g_RootMenu.DrawGenericOption("load", "Load an extension"); kornholijo@2778: g_RootMenu.DrawGenericOption("reload", "Reload an extension"); dvander@311: g_RootMenu.DrawGenericOption("unload", "Unload an extension"); dvander@310: } dvander@868: voidedweasel@3511: void CExtensionManager::ListExtensionsToClient(CPlayer *player, const CCommand &args) voidedweasel@3511: { voidedweasel@3511: char buffer[256]; voidedweasel@3511: int numExtensions = m_Libs.size(); voidedweasel@3511: edict_t *edict = player->GetEdict(); voidedweasel@3511: unsigned int id = 0; voidedweasel@3511: unsigned int start = 0; voidedweasel@3511: voidedweasel@3511: if (!numExtensions) voidedweasel@3511: { voidedweasel@3511: ClientConsolePrint(edict, "[SM] No extensions found."); voidedweasel@3511: return; voidedweasel@3511: } voidedweasel@3511: voidedweasel@3511: if (args.ArgC() > 2) voidedweasel@3511: { voidedweasel@3511: start = atoi(args.Arg(2)); voidedweasel@3511: } voidedweasel@3511: voidedweasel@3511: CExtension *ext; voidedweasel@3511: SourceHook::List::iterator iter; voidedweasel@3511: voidedweasel@3511: for (iter = m_Libs.begin(); voidedweasel@3511: iter != m_Libs.end(); voidedweasel@3511: iter++) voidedweasel@3511: { voidedweasel@3511: ext = (*iter); voidedweasel@3511: voidedweasel@3511: char error[255]; voidedweasel@3511: if (!ext->IsRunning(error, sizeof(error))) voidedweasel@3511: { voidedweasel@3511: continue; voidedweasel@3511: } voidedweasel@3511: voidedweasel@3511: id++; voidedweasel@3511: if (id < start) voidedweasel@3511: { voidedweasel@3511: continue; voidedweasel@3511: } voidedweasel@3511: voidedweasel@3511: if (id - start > 10) voidedweasel@3511: { voidedweasel@3511: break; voidedweasel@3511: } voidedweasel@3511: voidedweasel@3511: IExtensionInterface *api = ext->GetAPI(); voidedweasel@3511: voidedweasel@3511: const char *name = api->GetExtensionName(); voidedweasel@3511: const char *version = api->GetExtensionVerString(); voidedweasel@3511: const char *author = api->GetExtensionAuthor(); voidedweasel@3511: const char *description = api->GetExtensionDescription(); voidedweasel@3511: voidedweasel@3511: size_t len = UTIL_Format(buffer, sizeof(buffer), " \"%s\"", name); voidedweasel@3511: voidedweasel@3511: if (version != NULL && IS_STR_FILLED(version)) voidedweasel@3511: { voidedweasel@3511: len += UTIL_Format(&buffer[len], sizeof(buffer)-len, " (%s)", version); voidedweasel@3511: } voidedweasel@3511: voidedweasel@3511: if (author != NULL && IS_STR_FILLED(author)) voidedweasel@3511: { voidedweasel@3511: len += UTIL_Format(&buffer[len], sizeof(buffer)-len, " by %s", author); voidedweasel@3511: } voidedweasel@3511: voidedweasel@3511: if (description != NULL && IS_STR_FILLED(description)) voidedweasel@3511: { voidedweasel@3511: len += UTIL_Format(&buffer[len], sizeof(buffer)-len, ": %s", description); voidedweasel@3511: } voidedweasel@3511: voidedweasel@3511: voidedweasel@3511: ClientConsolePrint(edict, "%s", buffer); voidedweasel@3511: } voidedweasel@3511: voidedweasel@3511: while (iter != m_Libs.end()) voidedweasel@3511: { voidedweasel@3511: char error[255]; voidedweasel@3511: if ((*iter)->IsRunning(error, sizeof(error))) voidedweasel@3511: { voidedweasel@3511: break; voidedweasel@3511: } voidedweasel@3511: } voidedweasel@3511: voidedweasel@3511: if (iter != m_Libs.end()) voidedweasel@3511: { voidedweasel@3511: ClientConsolePrint(edict, "To see more, type \"sm exts %d\"", id); voidedweasel@3511: } voidedweasel@3511: } voidedweasel@3511: dvander@868: CExtension *CExtensionManager::GetExtensionFromIdent(IdentityToken_t *ptr) dvander@868: { dvander@868: if (ptr->type == g_ExtType) dvander@868: { dvander@868: return (CExtension *)(ptr->ptr); dvander@868: } dvander@868: dvander@868: return NULL; dvander@868: } dvander@1391: dvander@1391: CExtensionManager::CExtensionManager() dvander@1391: { dvander@1391: } dvander@1391: dvander@1391: CExtensionManager::~CExtensionManager() dvander@1391: { dvander@1391: } dvander@1572: dvander@1572: void CExtensionManager::AddLibrary(IExtension *pSource, const char *library) dvander@1572: { dvander@1572: CExtension *pExt = (CExtension *)pSource; dvander@1572: pExt->AddLibrary(library); dvander@1572: g_PluginSys.OnLibraryAction(library, false, false); dvander@1572: } dvander@1573: dvander@1573: bool CExtensionManager::LibraryExists(const char *library) dvander@1573: { dvander@1573: CExtension *pExt; dvander@1573: dvander@1573: for (List::iterator iter = m_Libs.begin(); dvander@1573: iter != m_Libs.end(); dvander@1573: iter++) dvander@1573: { dvander@1573: pExt = (*iter); dvander@1573: for (List::iterator s_iter = pExt->m_Libraries.begin(); dvander@1573: s_iter != pExt->m_Libraries.end(); dvander@1573: s_iter++) dvander@1573: { dvander@1573: if ((*s_iter).compare(library) == 0) dvander@1573: { dvander@1573: return true; dvander@1573: } dvander@1573: } dvander@1573: } dvander@1573: dvander@1573: return false; dvander@1573: } dvander@1648: dvander@1648: IExtension *CExtensionManager::LoadExternal(IExtensionInterface *pInterface, dvander@1648: const char *filepath, dvander@1648: const char *filename, dvander@1648: char *error, dvander@1648: size_t maxlength) dvander@1648: { dvander@1648: IExtension *pAlready; dvander@1648: if ((pAlready=FindExtensionByFile(filename)) != NULL) dvander@1648: { dvander@1648: return pAlready; dvander@1648: } dvander@1648: dvander@1648: CExtension *pExt = new CRemoteExtension(pInterface, filename, filepath); dvander@1648: dvander@1648: if (!pExt->Load(error, maxlength) || !pExt->IsLoaded()) dvander@1648: { dvander@1648: pExt->Unload(); dvander@1648: delete pExt; dvander@1648: return NULL; dvander@1648: } dvander@1648: dvander@1648: m_Libs.push_back(pExt); dvander@1648: dvander@1648: return pExt; dvander@1648: } dvander@1668: faluco@1993: void CExtensionManager::CallOnCoreMapStart(edict_t *pEdictList, int edictCount, int clientMax) faluco@1993: { dvander@1998: IExtensionInterface *pAPI; faluco@1993: List::iterator iter; faluco@1993: faluco@1993: for (iter=m_Libs.begin(); iter!=m_Libs.end(); iter++) faluco@1993: { dvander@1998: if ((pAPI = (*iter)->GetAPI()) == NULL) faluco@1993: { dvander@1998: continue; dvander@1998: } dvander@1998: if (pAPI->GetExtensionVersion() > 3) dvander@1998: { dvander@1998: pAPI->OnCoreMapStart(pEdictList, edictCount, clientMax); faluco@1993: } faluco@1993: } faluco@1993: } dvander@2782: dvander@2782: bool CLocalExtension::IsSameFile(const char *file) dvander@2782: { dvander@2782: /* Only care about the shortened name. */ dvander@2782: return strcmp(file, m_File.c_str()) == 0; dvander@2782: } dvander@2782: dvander@2782: bool CRemoteExtension::IsSameFile(const char *file) dvander@2782: { dvander@2782: /* :TODO: this could be better, but no one uses this API anyway. */ dvander@2782: return strcmp(file, m_Path.c_str()) == 0; dvander@2782: } dvander@2782: