dvander@298: #include "ExtensionSys.h" dvander@298: #include "LibrarySys.h" dvander@298: #include "ShareSys.h" dvander@298: #include "CLogger.h" dvander@304: #include "sourcemm_api.h" dvander@305: #include "PluginSys.h" dvander@310: #include "sm_srvcmds.h" dvander@311: #include dvander@298: dvander@298: CExtensionManager g_Extensions; dvander@298: IdentityType_t g_ExtType; dvander@298: dvander@298: CExtension::CExtension(const char *filename, char *error, size_t err_max) 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@298: dvander@298: char path[PLATFORM_MAX_PATH+1]; dvander@298: g_LibSys.PathFormat(path, PLATFORM_MAX_PATH, "%s/extensions/%s", g_SourceMod.GetSMBaseDir(), filename); dvander@298: dvander@298: m_pLib = g_LibSys.OpenLibrary(path, error, err_max); dvander@298: dvander@298: if (m_pLib == NULL) dvander@298: { dvander@298: return; 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; dvander@298: snprintf(error, err_max, "Unable to find extension entry point"); dvander@298: return; 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; dvander@298: snprintf(error, err_max, "Extension version is too new to load (%d, max is %d)", m_pAPI->GetExtensionVersion(), SMINTERFACE_EXTENSIONAPI_VERSION); dvander@298: return; dvander@298: } dvander@298: dvander@298: if (m_pAPI->IsMetamodExtension()) dvander@298: { dvander@304: bool already; dvander@304: m_PlId = g_pMMPlugins->Load(path, g_PLID, already, error, err_max); dvander@298: } dvander@298: dvander@298: m_pIdentToken = g_ShareSys.CreateIdentity(g_ExtType); dvander@298: dvander@298: if (!m_pAPI->OnExtensionLoad(this, &g_ShareSys, error, err_max, g_SourceMod.IsLateLoadInMap())) 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@298: return; dvander@298: } dvander@298: } dvander@298: dvander@298: CExtension::~CExtension() dvander@298: { dvander@298: if (m_pAPI) dvander@298: { dvander@298: m_pAPI->OnExtensionUnload(); dvander@304: if (m_PlId) dvander@304: { dvander@304: g_pMMPlugins->Unload(m_PlId, true, NULL, 0); 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@305: void CExtension::AddPlugin(IPlugin *pPlugin) dvander@305: { dvander@305: m_Plugins.push_back(pPlugin); dvander@305: } dvander@305: dvander@305: void CExtension::RemovePlugin(IPlugin *pPlugin) dvander@305: { dvander@305: m_Plugins.remove(pPlugin); 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@304: m_Deps.push_back(*pInfo); dvander@304: } dvander@304: 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@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@298: } dvander@298: dvander@298: void CExtensionManager::OnSourceModShutdown() dvander@298: { dvander@310: g_RootMenu.RemoveRootConsoleCommand("exts", this); dvander@305: g_PluginSys.RemovePluginsListener(this); dvander@298: g_ShareSys.DestroyIdentType(g_ExtType); dvander@298: } dvander@298: dvander@298: IExtension *CExtensionManager::LoadAutoExtension(const char *path) dvander@298: { 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@298: if (!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: m_Libs.push_back(p); 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@298: /* Make sure the file direction is right */ dvander@298: char path[PLATFORM_MAX_PATH+1]; dvander@298: g_LibSys.PathFormat(path, PLATFORM_MAX_PATH, "%s", file); dvander@298: dvander@298: for (iter=m_Libs.begin(); iter!=m_Libs.end(); iter++) dvander@298: { dvander@298: pExt = (*iter); dvander@298: if (strcmp(pExt->GetFilename(), path) == 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: dvander@298: IExtension *CExtensionManager::LoadExtension(const char *file, ExtensionLifetime lifetime, char *error, size_t err_max) dvander@298: { dvander@304: IExtension *pAlready; dvander@304: if ((pAlready=FindExtensionByFile(file)) != NULL) dvander@304: { dvander@304: return pAlready; dvander@304: } dvander@304: dvander@298: CExtension *pExt = new CExtension(file, error, err_max); dvander@298: dvander@298: /* :NOTE: lifetime is currently ignored */ dvander@298: dvander@298: if (!pExt->IsLoaded()) dvander@298: { dvander@298: delete pExt; dvander@298: return NULL; dvander@298: } dvander@298: dvander@298: m_Libs.push_back(pExt); dvander@298: dvander@298: return pExt; dvander@298: } dvander@298: dvander@304: void CExtensionManager::BindDependency(IExtension *pOwner, IfaceInfo *pInfo) dvander@298: { dvander@304: CExtension *pExt = (CExtension *)pOwner; dvander@304: dvander@304: pExt->AddDependency(pInfo); 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@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@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@304: if (!dropped && !pAPI->QueryInterfaceDrop((*i_iter).iface)) dvander@304: { dvander@304: dropped = true; dvander@304: } dvander@304: pAPI->NotifyInterfaceDrop((*i_iter).iface); dvander@304: i_iter = pDep->m_Deps.erase(i_iter); dvander@304: } else { dvander@304: i_iter++; dvander@304: } dvander@304: } dvander@304: } dvander@304: } dvander@304: 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@310: void CExtensionManager::OnRootConsoleCommand(const char *cmd, unsigned int argcount) dvander@310: { dvander@310: if (argcount >= 3) dvander@310: { dvander@310: const char *cmd = g_RootMenu.GetArgument(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@310: IExtensionInterface *pAPI = pExt->GetAPI(); dvander@310: const char *name = pAPI->GetExtensionName(); dvander@310: const char *version = pAPI->GetExtensionVerString(); dvander@310: const char *descr = pAPI->GetExtensionDescription(); dvander@310: g_RootMenu.ConsolePrint("[%02d] %s (%s): %s", num, name, version, descr); 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: dvander@310: const char *sId = g_RootMenu.GetArgument(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@310: IExtensionInterface *pAPI = pExt->GetAPI(); dvander@310: g_RootMenu.ConsolePrint(" File: %s", pExt->GetFilename()); dvander@310: g_RootMenu.ConsolePrint(" Loaded: Yes (version %s)", pAPI->GetExtensionVerString()); dvander@310: g_RootMenu.ConsolePrint(" Name: %s (%s)", pAPI->GetExtensionName(), pAPI->GetExtensionDescription()); dvander@310: g_RootMenu.ConsolePrint(" Author: %s (%s)", pAPI->GetExtensionAuthor(), pAPI->GetExtensionURL()); dvander@310: g_RootMenu.ConsolePrint(" Binary info: API version %d (compiled %s)", pAPI->GetExtensionVersion(), pAPI->GetExtensionDateString()); dvander@310: g_RootMenu.ConsolePrint(" Metamod enabled: %s", pAPI->IsMetamodExtension() ? "yes" : "no"); 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: dvander@311: const char *arg = g_RootMenu.GetArgument(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: { dvander@311: const char *unload = g_RootMenu.GetArgument(4); dvander@311: if (pExt->unload_code == atoi(unload)) dvander@311: { dvander@311: char filename[PLATFORM_MAX_PATH+1]; 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: } else { 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@311: || (!pExt->m_Deps.size() && !pExt->m_Plugins.size())) dvander@311: { dvander@311: char filename[PLATFORM_MAX_PATH+1]; 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@311: } else { dvander@311: List plugins; dvander@311: if (pExt->m_Deps.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: }