/** * 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 . * * 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 . * * Version: $Id$ */ #include #include "ExtensionSys.h" #include #include #include "common_logic.h" #include "PluginSys.h" CExtensionManager g_Extensions; IdentityType_t g_ExtType; void CExtension::Initialize(const char *filename, const char *path) { m_pAPI = NULL; m_pIdentToken = NULL; unload_code = 0; m_bFullyLoaded = false; m_File.assign(filename); m_Path.assign(path); char real_name[PLATFORM_MAX_PATH]; libsys->GetFileFromPath(real_name, sizeof(real_name), m_Path.c_str()); m_RealFile.assign(real_name); } CRemoteExtension::CRemoteExtension(IExtensionInterface *pAPI, const char *filename, const char *path) { Initialize(filename, path); m_pAPI = pAPI; } CLocalExtension::CLocalExtension(const char *filename) { m_PlId = 0; m_pLib = NULL; char path[PLATFORM_MAX_PATH]; /* Special case for new bintools binary */ if (strcmp(filename, "bintools.ext") == 0) { goto normal; } /* Zeroth, see if there is an engine specific build in the new place. */ g_pSM->BuildPath(Path_SM, path, PLATFORM_MAX_PATH, "extensions/%s.%s." PLATFORM_LIB_EXT, filename, smcore.gamesuffix); if (libsys->IsPathFile(path)) { goto found; } /* COMPAT HACK: One-halfth, if ep2v, see if there is an engine specific build in the new place with old naming */ if (strcmp(smcore.gamesuffix, "2.tf2") == 0 || strcmp(smcore.gamesuffix, "2.dods") == 0 || strcmp(smcore.gamesuffix, "2.hl2dm") == 0 ) { g_pSM->BuildPath(Path_SM, path, PLATFORM_MAX_PATH, "extensions/%s.2.ep2v." PLATFORM_LIB_EXT, filename); if (libsys->IsPathFile(path)) { goto found; } } else if (strcmp(smcore.gamesuffix, "2.nd") == 0) { g_pSM->BuildPath(Path_SM, path, PLATFORM_MAX_PATH, "extensions/%s.2.l4d2." PLATFORM_LIB_EXT, filename); if (libsys->IsPathFile(path)) { goto found; } } /* First see if there is an engine specific build! */ g_pSM->BuildPath(Path_SM, path, PLATFORM_MAX_PATH, "extensions/auto.%s/%s." PLATFORM_LIB_EXT, filename, smcore.gamesuffix); /* Try the "normal" version */ if (!libsys->IsPathFile(path)) { normal: g_pSM->BuildPath(Path_SM, path, PLATFORM_MAX_PATH, "extensions/%s." PLATFORM_LIB_EXT, filename); } found: Initialize(filename, path); } bool CRemoteExtension::Load(char *error, size_t maxlength) { if (!PerformAPICheck(error, maxlength)) { m_pAPI = NULL; return false; } if (!CExtension::Load(error, maxlength)) { m_pAPI = NULL; return false; } return true; } bool CLocalExtension::Load(char *error, size_t maxlength) { m_pLib = libsys->OpenLibrary(m_Path.c_str(), error, maxlength); if (m_pLib == NULL) { return false; } typedef IExtensionInterface *(*GETAPI)(); GETAPI pfnGetAPI = NULL; if ((pfnGetAPI=(GETAPI)m_pLib->GetSymbolAddress("GetSMExtAPI")) == NULL) { m_pLib->CloseLibrary(); m_pLib = NULL; snprintf(error, maxlength, "Unable to find extension entry point"); return false; } m_pAPI = pfnGetAPI(); /* Check pointer and version */ if (!PerformAPICheck(error, maxlength)) { m_pLib->CloseLibrary(); m_pLib = NULL; m_pAPI = NULL; return false; } /* Load as MM:S */ if (m_pAPI->IsMetamodExtension()) { bool ok; m_PlId = smcore.LoadMMSPlugin(m_Path.c_str(), &ok, error, maxlength); if (!m_PlId || !ok) { m_pLib->CloseLibrary(); m_pLib = NULL; m_pAPI = NULL; return false; } } if (!CExtension::Load(error, maxlength)) { if (m_pAPI->IsMetamodExtension()) { if (m_PlId) { smcore.UnloadMMSPlugin(m_PlId); m_PlId = 0; } } m_pLib->CloseLibrary(); m_pLib = NULL; m_pAPI = NULL; return false; } return true; } void CRemoteExtension::Unload() { } void CLocalExtension::Unload() { if (m_pAPI != NULL && m_PlId) { smcore.UnloadMMSPlugin(m_PlId); m_PlId = 0; } if (m_pLib != NULL) { m_pLib->CloseLibrary(); m_pLib = NULL; } } bool CRemoteExtension::Reload(char *error, size_t maxlength) { snprintf(error, maxlength, "Remote extensions do not support reloading"); return false; } bool CLocalExtension::Reload(char *error, size_t maxlength) { if (m_pLib == NULL) // FIXME: just load it instead? return false; m_pAPI->OnExtensionUnload(); Unload(); return Load(error, maxlength); } bool CRemoteExtension::IsExternal() { return true; } bool CLocalExtension::IsExternal() { return false; } CExtension::~CExtension() { DestroyIdentity(); } bool CExtension::PerformAPICheck(char *error, size_t maxlength) { if (!m_pAPI) { snprintf(error, maxlength, "No IExtensionInterface instance provided"); return false; } if (m_pAPI->GetExtensionVersion() > SMINTERFACE_EXTENSIONAPI_VERSION) { snprintf(error, maxlength, "Extension version is too new to load (%d, max is %d)", m_pAPI->GetExtensionVersion(), SMINTERFACE_EXTENSIONAPI_VERSION); return false; } return true; } bool CExtension::Load(char *error, size_t maxlength) { CreateIdentity(); if (!m_pAPI->OnExtensionLoad(this, &g_ShareSys, error, maxlength, !smcore.IsMapLoading())) { DestroyIdentity(); return false; } else { /* Check if we're past load time */ if (!smcore.IsMapLoading()) { m_pAPI->OnExtensionsAllLoaded(); } } return true; } void CExtension::CreateIdentity() { if (m_pIdentToken != NULL) { return; } m_pIdentToken = g_ShareSys.CreateIdentity(g_ExtType, this); } void CExtension::DestroyIdentity() { if (m_pIdentToken == NULL) { return; } g_ShareSys.DestroyIdentity(m_pIdentToken); m_pIdentToken = NULL; } void CExtension::MarkAllLoaded() { assert(m_bFullyLoaded == false); if (!m_bFullyLoaded) { m_bFullyLoaded = true; m_pAPI->OnExtensionsAllLoaded(); } } void CExtension::AddPlugin(CPlugin *pPlugin) { /* Unfortunately we have to do this :( */ if (m_Dependents.find(pPlugin) != m_Dependents.end()) { m_Dependents.push_back(pPlugin); } } void CExtension::SetError(const char *error) { m_Error.assign(error); } IExtensionInterface *CExtension::GetAPI() { return m_pAPI; } const char *CExtension::GetFilename() { return m_RealFile.c_str(); } const char *CExtension::GetPath() const { return m_Path.c_str(); } IdentityToken_t *CExtension::GetIdentity() { return m_pIdentToken; } bool CRemoteExtension::IsLoaded() { return (m_pAPI != NULL); } bool CLocalExtension::IsLoaded() { return (m_pLib != NULL); } void CExtension::AddDependency(const IfaceInfo *pInfo) { if (m_Deps.find(*pInfo) == m_Deps.end()) { m_Deps.push_back(*pInfo); } } bool operator ==(const IfaceInfo &i1, const IfaceInfo &i2) { return (i1.iface == i2.iface) && (i1.owner == i2.owner); } void CExtension::AddChildDependent(CExtension *pOther, SMInterface *iface) { IfaceInfo info; info.iface = iface; info.owner = pOther; List::iterator iter; for (iter = m_ChildDeps.begin(); iter != m_ChildDeps.end(); iter++) { IfaceInfo &other = (*iter); if (other == info) { return; } } m_ChildDeps.push_back(info); } ITERATOR *CExtension::FindFirstDependency(IExtension **pOwner, SMInterface **pInterface) { List::iterator iter = m_Deps.begin(); if (iter == m_Deps.end()) { return NULL; } if (pOwner) { *pOwner = (*iter).owner; } if (pInterface) { *pInterface = (*iter).iface; } List::iterator *pIter = new List::iterator(iter); return (ITERATOR *)pIter; } bool CExtension::FindNextDependency(ITERATOR *iter, IExtension **pOwner, SMInterface **pInterface) { List::iterator *pIter = (List::iterator *)iter; List::iterator _iter; if (_iter == m_Deps.end()) { return false; } _iter++; if (pOwner) { *pOwner = (*_iter).owner; } if (pInterface) { *pInterface = (*_iter).iface; } *pIter = _iter; if (_iter == m_Deps.end()) { return false; } return true; } void CExtension::FreeDependencyIterator(ITERATOR *iter) { List::iterator *pIter = (List::iterator *)iter; delete pIter; } void CExtension::AddInterface(SMInterface *pInterface) { m_Interfaces.push_back(pInterface); } bool CExtension::IsRunning(char *error, size_t maxlength) { if (!IsLoaded()) { if (error) { snprintf(error, maxlength, "%s", m_Error.c_str()); } return false; } return m_pAPI->QueryRunning(error, maxlength); } void CExtension::AddLibrary(const char *library) { m_Libraries.push_back(library); } /********************* * EXTENSION MANAGER * *********************/ void CExtensionManager::OnSourceModAllInitialized() { g_ExtType = g_ShareSys.CreateIdentType("EXTENSION"); pluginsys->AddPluginsListener(this); rootmenu->AddRootConsoleCommand("exts", "Manage extensions", this); g_ShareSys.AddInterface(NULL, this); } void CExtensionManager::OnSourceModShutdown() { rootmenu->RemoveRootConsoleCommand("exts", this); pluginsys->RemovePluginsListener(this); g_ShareSys.DestroyIdentType(g_ExtType); } void CExtensionManager::Shutdown() { List::iterator iter; while ((iter = m_Libs.begin()) != m_Libs.end()) { UnloadExtension((*iter)); } } void CExtensionManager::TryAutoload() { char path[PLATFORM_MAX_PATH]; g_pSM->BuildPath(Path_SM, path, sizeof(path), "extensions"); IDirectory *pDir = libsys->OpenDirectory(path); if (!pDir) { return; } const char *lfile; size_t len; while (pDir->MoreFiles()) { if (pDir->IsEntryDirectory()) { pDir->NextEntry(); continue; } lfile = pDir->GetEntryName(); len = strlen(lfile); if (len <= 9) /* size of ".autoload" */ { pDir->NextEntry(); continue; } if (strcmp(&lfile[len - 9], ".autoload") != 0) { pDir->NextEntry(); continue; } char file[PLATFORM_MAX_PATH]; len = smcore.Format(file, sizeof(file), "%s", lfile); strcpy(&file[len - 9], ".ext"); LoadAutoExtension(file); pDir->NextEntry(); } } IExtension *CExtensionManager::LoadAutoExtension(const char *path, bool bErrorOnMissing) { /* Remove platform extension if it's there. Compat hack. */ const char *ext = libsys->GetFileExtension(path); if (strcmp(ext, PLATFORM_LIB_EXT) == 0) { char path2[PLATFORM_MAX_PATH]; smcore.Format(path2, sizeof(path2), "%s", path); path2[strlen(path) - strlen(PLATFORM_LIB_EXT) - 1] = '\0'; return LoadAutoExtension(path2, bErrorOnMissing); } IExtension *pAlready; if ((pAlready=FindExtensionByFile(path)) != NULL) { return pAlready; } char error[256]; CExtension *p = new CLocalExtension(path); /* We put us in the list beforehand so extensions that check for each other * won't recursively load each other. */ m_Libs.push_back(p); if (!p->Load(error, sizeof(error)) || !p->IsLoaded()) { if (bErrorOnMissing || libsys->IsPathFile(p->GetPath())) { smcore.LogError("[SM] Unable to load extension \"%s\": %s", path, error); } p->SetError(error); } return p; } IExtension *CExtensionManager::FindExtensionByFile(const char *file) { List::iterator iter; CExtension *pExt; /* Chomp off the path */ char lookup[PLATFORM_MAX_PATH]; libsys->GetFileFromPath(lookup, sizeof(lookup), file); for (iter=m_Libs.begin(); iter!=m_Libs.end(); iter++) { pExt = (*iter); if (pExt->IsSameFile(lookup)) { return pExt; } } return NULL; } IExtension *CExtensionManager::FindExtensionByName(const char *ext) { List::iterator iter; CExtension *pExt; IExtensionInterface *pAPI; const char *name; for (iter=m_Libs.begin(); iter!=m_Libs.end(); iter++) { pExt = (*iter); if (!pExt->IsLoaded()) { continue; } if ((pAPI = pExt->GetAPI()) == NULL) { continue; } name = pAPI->GetExtensionName(); if (!name) { continue; } if (strcmp(name, ext) == 0) { return pExt; } } return NULL; } IExtension *CExtensionManager::LoadExtension(const char *file, char *error, size_t maxlength) { /* Remove platform extension if it's there. Compat hack. */ const char *ext = libsys->GetFileExtension(file); if (strcmp(ext, PLATFORM_LIB_EXT) == 0) { char path2[PLATFORM_MAX_PATH]; smcore.Format(path2, sizeof(path2), "%s", file); path2[strlen(file) - strlen(PLATFORM_LIB_EXT) - 1] = '\0'; return LoadExtension(path2, error, maxlength); } IExtension *pAlready; if ((pAlready=FindExtensionByFile(file)) != NULL) { return pAlready; } CExtension *pExt = new CLocalExtension(file); if (!pExt->Load(error, maxlength) || !pExt->IsLoaded()) { pExt->Unload(); delete pExt; return NULL; } /* :TODO: do QueryRunning check if the map is loaded */ m_Libs.push_back(pExt); return pExt; } void CExtensionManager::BindDependency(IExtension *pRequester, IfaceInfo *pInfo) { CExtension *pExt = (CExtension *)pRequester; CExtension *pOwner = (CExtension *)pInfo->owner; pExt->AddDependency(pInfo); IExtensionInterface *pAPI = pExt->GetAPI(); if (pAPI && !pAPI->QueryInterfaceDrop(pInfo->iface)) { pOwner->AddChildDependent(pExt, pInfo->iface); } } void CExtensionManager::AddRawDependency(IExtension *ext, IdentityToken_t *other, void *iface) { CExtension *pExt = (CExtension *)ext; CExtension *pOwner = GetExtensionFromIdent(other); IfaceInfo info; info.iface = (SMInterface *)iface; info.owner = pOwner; pExt->AddDependency(&info); pOwner->AddChildDependent(pExt, (SMInterface *)iface); } void CExtensionManager::AddInterface(IExtension *pOwner, SMInterface *pInterface) { CExtension *pExt = (CExtension *)pOwner; pExt->AddInterface(pInterface); } void CExtensionManager::BindChildPlugin(IExtension *pParent, SMPlugin *pPlugin) { CExtension *pExt = (CExtension *)pParent; pExt->AddPlugin(static_cast(pPlugin)); } void CExtensionManager::OnPluginDestroyed(IPlugin *plugin) { List::iterator iter; for (iter=m_Libs.begin(); iter!=m_Libs.end(); iter++) { (*iter)->DropRefsTo(static_cast(plugin)); } } CExtension *CExtensionManager::FindByOrder(unsigned int num) { if (num < 1 || num > m_Libs.size()) { return NULL; } List::iterator iter = m_Libs.begin(); while (iter != m_Libs.end()) { if (--num == 0) { return (*iter); } iter++; } return NULL; } bool CExtensionManager::UnloadExtension(IExtension *_pExt) { if (!_pExt) return false; CExtension *pExt = (CExtension *)_pExt; if (m_Libs.find(pExt) == m_Libs.end()) return false; /* Tell it to unload */ if (pExt->IsLoaded()) pExt->GetAPI()->OnExtensionUnload(); // Remove us from internal lists. Note that because we do this, it's // possible that our extension could be added back if another plugin // tries to load during this process. If we ever find this to happen, // we can just block plugin loading. g_ShareSys.RemoveInterfaces(_pExt); m_Libs.remove(pExt); List UnloadQueue; /* Handle dependencies */ if (pExt->IsLoaded()) { /* Unload any dependent plugins */ List::iterator p_iter = pExt->m_Dependents.begin(); while (p_iter != pExt->m_Dependents.end()) { /* We have to manually unlink ourselves here, since we're no longer being managed */ scripts->UnloadPlugin((*p_iter)); p_iter = pExt->m_Dependents.erase(p_iter); } List::iterator s_iter; for (s_iter = pExt->m_Libraries.begin(); s_iter != pExt->m_Libraries.end(); s_iter++) { scripts->OnLibraryAction((*s_iter).c_str(), LibraryAction_Removed); } /* Notify and/or unload all dependencies */ List::iterator c_iter; CExtension *pDep; IExtensionInterface *pAPI; for (c_iter = m_Libs.begin(); c_iter != m_Libs.end(); c_iter++) { pDep = (*c_iter); if ((pAPI=pDep->GetAPI()) == NULL) continue; if (pDep == pExt) continue; /* Now, get its dependency list */ bool dropped = false; List::iterator i_iter = pDep->m_Deps.begin(); while (i_iter != pDep->m_Deps.end()) { if ((*i_iter).owner == _pExt) { if (!pAPI->QueryInterfaceDrop((*i_iter).iface)) { if (!dropped) { dropped = true; UnloadQueue.push_back(pDep); } } pAPI->NotifyInterfaceDrop((*i_iter).iface); i_iter = pDep->m_Deps.erase(i_iter); } else { i_iter++; } } /* Flush out any back references to this plugin */ i_iter = pDep->m_ChildDeps.begin(); while (i_iter != pDep->m_ChildDeps.end()) { if ((*i_iter).owner == pExt) i_iter = pDep->m_ChildDeps.erase(i_iter); else i_iter++; } } /* Unbind our natives from Core */ pExt->DropEverything(); } IdentityToken_t *pIdentity; if ((pIdentity = pExt->GetIdentity()) != NULL) { SMGlobalClass *glob = SMGlobalClass::head; while (glob) { glob->OnSourceModIdentityDropped(pIdentity); glob = glob->m_pGlobalClassNext; } } // Everything has been informed that we're unloading, so give the // extension one last notification. if (pExt->IsLoaded() && pExt->GetAPI()->GetExtensionVersion() >= 7) pExt->GetAPI()->OnDependenciesDropped(); pExt->Unload(); delete pExt; List::iterator iter; for (iter=UnloadQueue.begin(); iter!=UnloadQueue.end(); iter++) { /* NOTE: This is safe because the unload function backs out of anything not present */ UnloadExtension((*iter)); } return true; } void CExtensionManager::MarkAllLoaded() { List::iterator iter; CExtension *pExt; for (iter=m_Libs.begin(); iter!=m_Libs.end(); iter++) { pExt = (*iter); if (!pExt->IsLoaded()) { continue; } if (pExt->m_bFullyLoaded) { continue; } pExt->MarkAllLoaded(); } } void CExtensionManager::AddDependency(IExtension *pSource, const char *file, bool required, bool autoload) { /* This function doesn't really need to do anything now. We make sure the * other extension is loaded, but handling of dependencies is really done * by the interface fetcher. */ if (required || autoload) { LoadAutoExtension(file); } } void CExtensionManager::OnRootConsoleCommand(const char *cmdname, const CCommand &command) { int argcount = smcore.Argc(command); if (argcount >= 3) { const char *cmd = smcore.Arg(command, 2); if (strcmp(cmd, "list") == 0) { List::iterator iter; CExtension *pExt; unsigned int num = 1; switch (m_Libs.size()) { case 1: { rootmenu->ConsolePrint("[SM] Displaying 1 extension:"); break; } case 0: { rootmenu->ConsolePrint("[SM] No extensions are loaded."); break; } default: { rootmenu->ConsolePrint("[SM] Displaying %d extensions:", m_Libs.size()); break; } } for (iter=m_Libs.begin(); iter!=m_Libs.end(); iter++,num++) { pExt = (*iter); if (pExt->IsLoaded()) { char error[255]; if (!pExt->IsRunning(error, sizeof(error))) { rootmenu->ConsolePrint("[%02d] file \"%s\": %s", num, pExt->GetFilename(), error); } else { IExtensionInterface *pAPI = pExt->GetAPI(); const char *name = pAPI->GetExtensionName(); const char *version = pAPI->GetExtensionVerString(); const char *descr = pAPI->GetExtensionDescription(); rootmenu->ConsolePrint("[%02d] %s (%s): %s", num, name, version, descr); } } else { rootmenu->ConsolePrint("[%02d] file \"%s\": %s", num, pExt->GetFilename(), pExt->m_Error.c_str()); } } return; } else if (strcmp(cmd, "load") == 0) { if (argcount < 4) { rootmenu->ConsolePrint("[SM] Usage: sm exts load "); return; } const char *filename = smcore.Arg(command, 3); char path[PLATFORM_MAX_PATH]; char error[256]; smcore.Format(path, sizeof(path), "%s%s%s", filename, !strstr(filename, ".ext") ? ".ext" : "", !strstr(filename, "." PLATFORM_LIB_EXT) ? "." PLATFORM_LIB_EXT : ""); if (FindExtensionByFile(path) != NULL) { rootmenu->ConsolePrint("[SM] Extension %s is already loaded.", path); return; } if (LoadExtension(path, error, sizeof(error))) { rootmenu->ConsolePrint("[SM] Loaded extension %s successfully.", path); } else { rootmenu->ConsolePrint("[SM] Extension %s failed to load: %s", path, error); } return; } else if (strcmp(cmd, "info") == 0) { if (argcount < 4) { rootmenu->ConsolePrint("[SM] Usage: sm exts info <#>"); return; } const char *sId = smcore.Arg(command, 3); unsigned int id = atoi(sId); if (id <= 0) { rootmenu->ConsolePrint("[SM] Usage: sm exts info <#>"); return; } if (m_Libs.size() == 0) { rootmenu->ConsolePrint("[SM] No extensions are loaded."); return; } if (id > m_Libs.size()) { rootmenu->ConsolePrint("[SM] No extension was found with id %d.", id); return; } List::iterator iter = m_Libs.begin(); CExtension *pExt = NULL; while (iter != m_Libs.end()) { if (--id == 0) { pExt = (*iter); break; } iter++; } /* This should never happen */ if (!pExt) { rootmenu->ConsolePrint("[SM] No extension was found with id %d.", id); return; } if (!pExt->IsLoaded()) { rootmenu->ConsolePrint(" File: %s", pExt->GetFilename()); rootmenu->ConsolePrint(" Loaded: No (%s)", pExt->m_Error.c_str()); } else { char error[255]; if (!pExt->IsRunning(error, sizeof(error))) { rootmenu->ConsolePrint(" File: %s", pExt->GetFilename()); rootmenu->ConsolePrint(" Loaded: Yes"); rootmenu->ConsolePrint(" Running: No (%s)", error); } else { IExtensionInterface *pAPI = pExt->GetAPI(); rootmenu->ConsolePrint(" File: %s", pExt->GetFilename()); rootmenu->ConsolePrint(" Loaded: Yes (version %s)", pAPI->GetExtensionVerString()); rootmenu->ConsolePrint(" Name: %s (%s)", pAPI->GetExtensionName(), pAPI->GetExtensionDescription()); rootmenu->ConsolePrint(" Author: %s (%s)", pAPI->GetExtensionAuthor(), pAPI->GetExtensionURL()); rootmenu->ConsolePrint(" Binary info: API version %d (compiled %s)", pAPI->GetExtensionVersion(), pAPI->GetExtensionDateString()); if (pExt->IsExternal()) { rootmenu->ConsolePrint(" Method: Loaded by Metamod:Source, attached to SourceMod"); } else if (pAPI->IsMetamodExtension()) { rootmenu->ConsolePrint(" Method: Loaded by SourceMod, attached to Metamod:Source"); } else { rootmenu->ConsolePrint(" Method: Loaded by SourceMod"); } } } return; } else if (strcmp(cmd, "unload") == 0) { if (argcount < 4) { rootmenu->ConsolePrint("[SM] Usage: sm exts unload <#> [code]"); return; } const char *arg = smcore.Arg(command, 3); unsigned int num = atoi(arg); CExtension *pExt = FindByOrder(num); if (!pExt) { rootmenu->ConsolePrint("[SM] Extension number %d was not found.", num); return; } if (argcount > 4 && pExt->unload_code) { const char *unload = smcore.Arg(command, 4); if (pExt->unload_code == (unsigned)atoi(unload)) { char filename[PLATFORM_MAX_PATH]; snprintf(filename, PLATFORM_MAX_PATH, "%s", pExt->GetFilename()); UnloadExtension(pExt); rootmenu->ConsolePrint("[SM] Extension %s is now unloaded.", filename); } else { rootmenu->ConsolePrint("[SM] Please try again, the correct unload code is \"%d\"", pExt->unload_code); } return; } if (!pExt->IsLoaded() || (!pExt->m_ChildDeps.size() && !pExt->m_Dependents.size())) { char filename[PLATFORM_MAX_PATH]; snprintf(filename, PLATFORM_MAX_PATH, "%s", pExt->GetFilename()); UnloadExtension(pExt); rootmenu->ConsolePrint("[SM] Extension %s is now unloaded.", filename); return; } else { List plugins; if (pExt->m_ChildDeps.size()) { rootmenu->ConsolePrint("[SM] Unloading %s will unload the following extensions: ", pExt->GetFilename()); List::iterator iter; CExtension *pOther; /* Get list of all extensions */ for (iter=m_Libs.begin(); iter!=m_Libs.end(); iter++) { List::iterator i_iter; pOther = (*iter); if (!pOther->IsLoaded() || pOther == pExt) { continue; } /* Get their dependencies */ for (i_iter=pOther->m_Deps.begin(); i_iter!=pOther->m_Deps.end(); i_iter++) { /* Is this dependency to us? */ if ((*i_iter).owner != pExt) { continue; } /* Will our dependent care? */ if (!pExt->GetAPI()->QueryInterfaceDrop((*i_iter).iface)) { rootmenu->ConsolePrint(" -> %s", pExt->GetFilename()); /* Add to plugin unload list */ List::iterator p_iter; for (p_iter=pOther->m_Dependents.begin(); p_iter!=pOther->m_Dependents.end(); p_iter++) { if (plugins.find((*p_iter)) == plugins.end()) { plugins.push_back((*p_iter)); } } } } } } if (pExt->m_Dependents.size()) { rootmenu->ConsolePrint("[SM] Unloading %s will unload the following plugins: ", pExt->GetFilename()); List::iterator iter; CPlugin *pPlugin; for (iter = pExt->m_Dependents.begin(); iter != pExt->m_Dependents.end(); iter++) { pPlugin = (*iter); if (plugins.find(pPlugin) == plugins.end()) { plugins.push_back(pPlugin); } } for (iter = plugins.begin(); iter != plugins.end(); iter++) { pPlugin = (*iter); rootmenu->ConsolePrint(" -> %s", pPlugin->GetFilename()); } } srand(static_cast(time(NULL))); pExt->unload_code = (rand() % 877) + 123; //123 to 999 rootmenu->ConsolePrint("[SM] To verify unloading %s, please use the following: ", pExt->GetFilename()); rootmenu->ConsolePrint("[SM] sm exts unload %d %d", num, pExt->unload_code); return; } } else if (strcmp(cmd, "reload") == 0) { if (argcount < 4) { rootmenu->ConsolePrint("[SM] Usage: sm exts reload <#>"); return; } const char *arg = smcore.Arg(command, 3); unsigned int num = atoi(arg); CExtension *pExt = FindByOrder(num); if (!pExt) { rootmenu->ConsolePrint("[SM] Extension number %d was not found.", num); return; } if (pExt->IsLoaded()) { char filename[PLATFORM_MAX_PATH]; char error[255]; snprintf(filename, PLATFORM_MAX_PATH, "%s", pExt->GetFilename()); if (pExt->Reload(error, sizeof(error))) { rootmenu->ConsolePrint("[SM] Extension %s is now reloaded.", filename); } else { rootmenu->ConsolePrint("[SM] Extension %s failed to reload: %s", filename, error); } return; } else { rootmenu->ConsolePrint("[SM] Extension %s is not loaded.", pExt->GetFilename()); return; } } } rootmenu->ConsolePrint("SourceMod Extensions Menu:"); rootmenu->DrawGenericOption("info", "Extra extension information"); rootmenu->DrawGenericOption("list", "List extensions"); rootmenu->DrawGenericOption("load", "Load an extension"); rootmenu->DrawGenericOption("reload", "Reload an extension"); rootmenu->DrawGenericOption("unload", "Unload an extension"); } CExtension *CExtensionManager::GetExtensionFromIdent(IdentityToken_t *ptr) { if (ptr->type == g_ExtType) { return (CExtension *)(ptr->ptr); } return NULL; } CExtensionManager::CExtensionManager() { } CExtensionManager::~CExtensionManager() { } void CExtensionManager::AddLibrary(IExtension *pSource, const char *library) { CExtension *pExt = (CExtension *)pSource; pExt->AddLibrary(library); scripts->OnLibraryAction(library, LibraryAction_Added); } bool CExtensionManager::LibraryExists(const char *library) { CExtension *pExt; for (List::iterator iter = m_Libs.begin(); iter != m_Libs.end(); iter++) { pExt = (*iter); for (List::iterator s_iter = pExt->m_Libraries.begin(); s_iter != pExt->m_Libraries.end(); s_iter++) { if ((*s_iter).compare(library) == 0) { return true; } } } return false; } IExtension *CExtensionManager::LoadExternal(IExtensionInterface *pInterface, const char *filepath, const char *filename, char *error, size_t maxlength) { IExtension *pAlready; if ((pAlready=FindExtensionByFile(filename)) != NULL) { return pAlready; } CExtension *pExt = new CRemoteExtension(pInterface, filename, filepath); if (!pExt->Load(error, maxlength) || !pExt->IsLoaded()) { pExt->Unload(); delete pExt; return NULL; } m_Libs.push_back(pExt); return pExt; } void CExtensionManager::CallOnCoreMapStart(edict_t *pEdictList, int edictCount, int clientMax) { IExtensionInterface *pAPI; List::iterator iter; for (iter=m_Libs.begin(); iter!=m_Libs.end(); iter++) { if ((pAPI = (*iter)->GetAPI()) == NULL) { continue; } if (pAPI->GetExtensionVersion() > 3) { pAPI->OnCoreMapStart(pEdictList, edictCount, clientMax); } } } const CVector *CExtensionManager::ListExtensions() { CVector *list = new CVector(); for (List::iterator iter = m_Libs.begin(); iter != m_Libs.end(); iter++) list->push_back(*iter); return list; } void CExtensionManager::FreeExtensionList(const CVector *list) { delete const_cast *>(list); } bool CLocalExtension::IsSameFile(const char *file) { /* Only care about the shortened name. */ return strcmp(file, m_File.c_str()) == 0; } bool CRemoteExtension::IsSameFile(const char *file) { /* :TODO: this could be better, but no one uses this API anyway. */ return strcmp(file, m_Path.c_str()) == 0; }