dvander@370: /** ds@653: * vim: set ts=4 : ds@1330: * ============================================================================= dvander@1231: * SourceMod dvander@1231: * Copyright (C) 2004-2007 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" dvander@298: dvander@298: CExtensionManager g_Extensions; dvander@298: IdentityType_t g_ExtType; dvander@298: ds@755: CExtension::CExtension(const char *filename, char *error, size_t maxlength) dvander@298: { dvander@298: m_File.assign(filename); dvander@298: m_pAPI = NULL; dvander@298: m_pIdentToken = NULL; dvander@304: m_PlId = 0; dvander@311: unload_code = 0; dvander@315: m_FullyLoaded = false; dvander@298: ds@652: char path[PLATFORM_MAX_PATH]; dvander@1568: dvander@1568: /* First see if there is an engine specific build! */ dvander@1568: g_SourceMod.BuildPath(Path_SM, dvander@1568: path, dvander@1568: PLATFORM_MAX_PATH, dvander@1568: #if defined METAMOD_PLAPI_VERSION dvander@1568: #if defined ORANGEBOX_BUILD dvander@1568: "extensions/auto.2.ep2/%s", dvander@1568: #else dvander@1568: "extensions/auto.2.ep1/%s", dvander@1568: #endif //ORANGEBOX_BUILD dvander@1568: #else //METAMOD_PLAPI_VERSION dvander@1568: "extensions/auto.1.ep1/%s", dvander@1568: #endif //METAMOD_PLAPI_VERSION dvander@1568: filename); dvander@1568: dvander@1568: /* Try the "normal" version */ dvander@1568: if (!g_LibSys.IsPathFile(path)) dvander@1568: { dvander@1568: g_SourceMod.BuildPath(Path_SM, path, PLATFORM_MAX_PATH, "extensions/%s", filename); dvander@1568: } dvander@1568: dvander@941: m_Path.assign(path); dvander@298: dvander@941: m_pLib = NULL; dvander@941: } dvander@941: dvander@941: bool CExtension::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@298: if (!m_pAPI || m_pAPI->GetExtensionVersion() > SMINTERFACE_EXTENSIONAPI_VERSION) dvander@298: { dvander@298: m_pLib->CloseLibrary(); dvander@298: m_pLib = NULL; ds@755: 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@298: } dvander@298: 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); dvander@298: } dvander@298: dvander@868: m_pIdentToken = g_ShareSys.CreateIdentity(g_ExtType, this); dvander@298: ds@755: if (!m_pAPI->OnExtensionLoad(this, &g_ShareSys, error, maxlength, !g_SourceMod.IsMapLoading())) 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@298: m_pAPI = NULL; dvander@298: m_pLib->CloseLibrary(); dvander@298: m_pLib = NULL; dvander@298: g_ShareSys.DestroyIdentity(m_pIdentToken); dvander@298: m_pIdentToken = NULL; dvander@941: return false; dvander@315: } else { 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@298: CExtension::~CExtension() dvander@298: { dvander@1508: char temp_buffer[255]; dvander@1508: dvander@298: if (m_pAPI) dvander@298: { dvander@304: if (m_PlId) dvander@304: { dvander@1508: g_pMMPlugins->Unload(m_PlId, true, temp_buffer, sizeof(temp_buffer)); dvander@304: } dvander@298: } dvander@298: dvander@298: if (m_pIdentToken) dvander@298: { dvander@298: g_ShareSys.DestroyIdentity(m_pIdentToken); dvander@298: } dvander@298: dvander@298: if (m_pLib) dvander@298: { dvander@298: m_pLib->CloseLibrary(); dvander@298: } dvander@298: } dvander@298: dvander@315: void CExtension::MarkAllLoaded() dvander@315: { dvander@315: assert(m_FullyLoaded == false); dvander@315: if (!m_FullyLoaded) dvander@315: { dvander@315: m_FullyLoaded = true; dvander@315: m_pAPI->OnExtensionsAllLoaded(); dvander@315: } dvander@315: } dvander@315: dvander@305: void CExtension::AddPlugin(IPlugin *pPlugin) dvander@305: { dvander@868: /* Unfortunately we have to do this :( */ dvander@868: if (m_Plugins.find(pPlugin) != m_Plugins.end()) dvander@868: { dvander@868: m_Plugins.push_back(pPlugin); dvander@868: } dvander@305: } dvander@305: dvander@305: void CExtension::RemovePlugin(IPlugin *pPlugin) dvander@305: { dvander@305: m_Plugins.remove(pPlugin); dvander@1121: dvander@1121: List::iterator iter = m_WeakNatives.begin(); dvander@1121: while (iter != m_WeakNatives.end()) dvander@1121: { dvander@1121: if ((*iter).pl == pPlugin) dvander@1121: { dvander@1121: iter = m_WeakNatives.erase(iter); dvander@1121: } else { dvander@1121: iter++; dvander@1121: } 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@298: return m_File.c_str(); dvander@298: } dvander@298: dvander@298: IdentityToken_t *CExtension::GetIdentity() dvander@298: { dvander@298: return m_pIdentToken; dvander@298: } dvander@298: dvander@298: bool CExtension::IsLoaded() dvander@298: { dvander@298: return (m_pLib != NULL); dvander@298: } dvander@298: dvander@304: void CExtension::AddDependency(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@397: strcpy(&file[len - 9], ".ext." PLATFORM_LIB_EXT); dvander@397: dvander@397: LoadAutoExtension(file); dvander@397: dvander@397: pDir->NextEntry(); dvander@397: } dvander@397: } dvander@397: dvander@298: IExtension *CExtensionManager::LoadAutoExtension(const char *path) dvander@298: { dvander@315: if (!strstr(path, "." PLATFORM_LIB_EXT)) dvander@315: { ds@652: char newpath[PLATFORM_MAX_PATH]; dvander@1456: g_LibSys.PathFormat(newpath, sizeof(newpath), "%s.%s", path, PLATFORM_LIB_EXT); dvander@315: return LoadAutoExtension(newpath); 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@298: CExtension *p = new CExtension(path, error, sizeof(error)); 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: { dvander@305: g_Logger.LogError("[SM] Unable to load extension \"%s\": %s", path, error); 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@315: if (!strstr(file, "." PLATFORM_LIB_EXT)) dvander@315: { dvander@315: char path[PLATFORM_MAX_PATH]; dvander@1456: UTIL_Format(path, sizeof(path), "%s.%s", file, PLATFORM_LIB_EXT); dvander@315: return FindExtensionByFile(path); dvander@315: } dvander@315: 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@1508: char short_file[PLATFORM_MAX_PATH]; dvander@1508: g_LibSys.GetFileFromPath(short_file, sizeof(short_file), pExt->GetFilename()); dvander@1508: if (strcmp(lookup, short_file) == 0) 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: ds@755: IExtension *CExtensionManager::LoadExtension(const char *file, ExtensionLifetime lifetime, char *error, size_t maxlength) dvander@298: { dvander@304: IExtension *pAlready; dvander@304: if ((pAlready=FindExtensionByFile(file)) != NULL) dvander@304: { dvander@304: return pAlready; dvander@304: } dvander@304: ds@755: CExtension *pExt = new CExtension(file, error, maxlength); dvander@298: dvander@298: /* :NOTE: lifetime is currently ignored */ dvander@298: dvander@941: if (!pExt->Load(error, maxlength) || !pExt->IsLoaded()) dvander@298: { 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@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@305: void CExtensionManager::BindChildPlugin(IExtension *pParent, IPlugin *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@305: (*iter)->RemovePlugin(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@315: void CExtensionManager::BindAllNativesToPlugin(IPlugin *pPlugin) dvander@315: { dvander@315: IPluginContext *pContext = pPlugin->GetBaseContext(); dvander@1391: dvander@1391: List exts; dvander@1391: dvander@1391: uint32_t natives = pContext->GetNativesNum(); dvander@1391: sp_native_t *native; dvander@1391: sm_extnative_t *x_native; dvander@1391: for (uint32_t i=0; iGetNativeByIndex(i, &native) != SP_ERROR_NONE) dvander@315: { dvander@315: continue; dvander@315: } dvander@1391: /* Make sure the native is not already bound */ dvander@1391: if (native->status == SP_NATIVE_BOUND) dvander@1391: { dvander@1391: continue; dvander@1391: } dvander@1391: /* See if we've got this native in our cache */ dvander@1391: if ((x_native = m_ExtNatives.retrieve(native->name)) == NULL) dvander@315: { dvander@315: continue; dvander@315: } dvander@1391: /* Try and bind it */ dvander@1391: if (pContext->BindNativeToIndex(i, x_native->info->func) != SP_ERROR_NONE) dvander@1391: { dvander@1391: continue; dvander@1391: } dvander@1391: /* See if it's optional */ dvander@1391: if (native->flags & SP_NTVFLAG_OPTIONAL) dvander@315: { dvander@1391: WeakNative wkn = WeakNative((CPlugin *)pPlugin, i); dvander@1391: x_native->owner->m_WeakNatives.push_back(wkn); dvander@1572: } dvander@1572: else if (exts.find(x_native->owner) == exts.end()) dvander@1572: { dvander@1391: exts.push_back(x_native->owner); dvander@315: } dvander@1391: } dvander@1391: dvander@1391: List::iterator iter; dvander@1391: for (iter = exts.begin(); iter != exts.end(); iter++) dvander@1391: { dvander@1391: CExtension *pExt = (*iter); dvander@1391: if (pExt->m_Plugins.find(pPlugin) != pExt->m_Plugins.end()) dvander@315: { dvander@1391: continue; dvander@315: } dvander@1391: pExt->m_Plugins.push_back(pPlugin); dvander@315: } dvander@315: } dvander@315: 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@305: List::iterator p_iter = pExt->m_Plugins.begin(); dvander@305: while (p_iter != pExt->m_Plugins.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@311: p_iter = pExt->m_Plugins.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: faluco@1112: /* Unbound weak natives */ dvander@1121: List::iterator wkn_iter; faluco@1112: for (wkn_iter=pExt->m_WeakNatives.begin(); wkn_iter!=pExt->m_WeakNatives.end(); wkn_iter++) faluco@1112: { dvander@1121: WeakNative & wkn = (*wkn_iter); dvander@1121: sp_context_t *ctx = wkn.pl->GetContext(); dvander@1121: ctx->natives[wkn.idx].status = SP_NATIVE_UNBOUND; faluco@1112: } faluco@1112: 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@1391: List::iterator native_iter; dvander@1391: for (native_iter = pExt->m_Natives.begin(); dvander@1391: native_iter != pExt->m_Natives.end(); dvander@1391: native_iter++) dvander@1391: { dvander@1391: const sp_nativeinfo_t *natives = (*native_iter); dvander@1391: sm_extnative_t *x_native; dvander@1391: for (unsigned int n = 0; dvander@1391: natives[n].name != NULL && natives[n].func != NULL; dvander@1391: n++) dvander@1391: { dvander@1391: x_native = m_ExtNatives.retrieve(natives[n].name); dvander@1391: if (x_native && x_native->owner == pExt) dvander@1391: { dvander@1391: m_ExtNatives.remove(natives[n].name); dvander@1391: } dvander@1391: } dvander@1391: } dvander@1391: dvander@1202: /* Tell it to unload */ dvander@1202: pAPI = pExt->GetAPI(); dvander@1202: pAPI->OnExtensionUnload(); 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: 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::AddNatives(IExtension *pOwner, const sp_nativeinfo_t *natives) dvander@315: { dvander@315: CExtension *pExt = (CExtension *)pOwner; dvander@315: dvander@315: pExt->m_Natives.push_back(natives); dvander@1391: dvander@1391: for (unsigned int i = 0; natives[i].func != NULL && natives[i].name != NULL; i++) dvander@1391: { dvander@1391: if (m_ExtNatives.retrieve(natives[i].name) != NULL) dvander@1391: { dvander@1391: continue; dvander@1391: } dvander@1391: sm_extnative_t x_native; dvander@1391: x_native.info = &natives[i]; dvander@1391: x_native.owner = pExt; dvander@1391: m_ExtNatives.insert(natives[i].name, x_native); dvander@1391: } dvander@315: } dvander@315: 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@315: if (pExt->m_FullyLoaded) 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@315: } else { 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@310: } else if (strcmp(cmd, "info") == 0) { dvander@310: if (argcount < 4) dvander@310: { dvander@310: g_RootMenu.ConsolePrint("[SM] Usage: sm 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: { dvander@310: g_RootMenu.ConsolePrint("[SM] Usage: sm 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@310: } else { 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@315: } else { 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@315: g_RootMenu.ConsolePrint(" Metamod enabled: %s", pAPI->IsMetamodExtension() ? "yes" : "no"); dvander@315: } dvander@310: } dvander@310: return; dvander@311: } else if (strcmp(cmd, "unload") == 0) { dvander@311: if (argcount < 4) dvander@311: { dvander@311: g_RootMenu.ConsolePrint("[SM] Usage: sm 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@1150: || (!pExt->m_ChildDeps.size() && !pExt->m_Plugins.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@311: 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@311: List::iterator p_iter; dvander@311: for (p_iter=pOther->m_Plugins.begin(); dvander@311: p_iter!=pOther->m_Plugins.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@311: if (pExt->m_Plugins.size()) dvander@311: { dvander@311: g_RootMenu.ConsolePrint("[SM] Unloading %s will unload the following plugins: ", pExt->GetFilename()); dvander@311: List::iterator iter; dvander@311: IPlugin *pPlugin; dvander@311: for (iter = pExt->m_Plugins.begin(); iter != pExt->m_Plugins.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: } 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"); dvander@311: g_RootMenu.DrawGenericOption("unload", "Unload an extension"); dvander@310: } dvander@868: 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: }