1 #include "ExtensionSys.h"
2 #include "LibrarySys.h"
5 #include "sourcemm_api.h"
7 #include "sm_srvcmds.h"
10 CExtensionManager g_Extensions;
11 IdentityType_t g_ExtType;
13 CExtension::CExtension(const char *filename, char *error, size_t err_max)
15 m_File.assign(filename);
20 m_FullyLoaded = false;
22 char path[PLATFORM_MAX_PATH+1];
23 g_SourceMod.BuildPath(Path_SM, path, PLATFORM_MAX_PATH, "extensions/%s", filename);
25 m_pLib = g_LibSys.OpenLibrary(path, error, err_max);
32 typedef IExtensionInterface *(*GETAPI)();
33 GETAPI pfnGetAPI = NULL;
35 if ((pfnGetAPI=(GETAPI)m_pLib->GetSymbolAddress("GetSMExtAPI")) == NULL)
37 m_pLib->CloseLibrary();
39 snprintf(error, err_max, "Unable to find extension entry point");
44 if (!m_pAPI || m_pAPI->GetExtensionVersion() > SMINTERFACE_EXTENSIONAPI_VERSION)
46 m_pLib->CloseLibrary();
48 snprintf(error, err_max, "Extension version is too new to load (%d, max is %d)", m_pAPI->GetExtensionVersion(), SMINTERFACE_EXTENSIONAPI_VERSION);
52 if (m_pAPI->IsMetamodExtension())
55 m_PlId = g_pMMPlugins->Load(path, g_PLID, already, error, err_max);
58 m_pIdentToken = g_ShareSys.CreateIdentity(g_ExtType);
60 if (!m_pAPI->OnExtensionLoad(this, &g_ShareSys, error, err_max, !g_SourceMod.IsMapLoading()))
62 if (m_pAPI->IsMetamodExtension())
67 g_pMMPlugins->Unload(m_PlId, true, dummy, sizeof(dummy));
72 m_pLib->CloseLibrary();
74 g_ShareSys.DestroyIdentity(m_pIdentToken);
78 /* Check if we're past load time */
79 if (!g_SourceMod.IsMapLoading())
81 m_pAPI->OnExtensionsAllLoaded();
86 CExtension::~CExtension()
90 m_pAPI->OnExtensionUnload();
93 g_pMMPlugins->Unload(m_PlId, true, NULL, 0);
99 g_ShareSys.DestroyIdentity(m_pIdentToken);
104 m_pLib->CloseLibrary();
108 void CExtension::MarkAllLoaded()
110 assert(m_FullyLoaded == false);
113 m_FullyLoaded = true;
114 m_pAPI->OnExtensionsAllLoaded();
118 void CExtension::AddPlugin(IPlugin *pPlugin)
120 m_Plugins.push_back(pPlugin);
123 void CExtension::RemovePlugin(IPlugin *pPlugin)
125 m_Plugins.remove(pPlugin);
128 void CExtension::SetError(const char *error)
130 m_Error.assign(error);
133 IExtensionInterface *CExtension::GetAPI()
138 const char *CExtension::GetFilename()
140 return m_File.c_str();
143 IdentityToken_t *CExtension::GetIdentity()
145 return m_pIdentToken;
148 bool CExtension::IsLoaded()
150 return (m_pLib != NULL);
153 void CExtension::AddDependency(IfaceInfo *pInfo)
155 m_Deps.push_back(*pInfo);
158 ITERATOR *CExtension::FindFirstDependency(IExtension **pOwner, SMInterface **pInterface)
160 List<IfaceInfo>::iterator iter = m_Deps.begin();
162 if (iter == m_Deps.end())
169 *pOwner = (*iter).owner;
173 *pInterface = (*iter).iface;
176 List<IfaceInfo>::iterator *pIter = new List<IfaceInfo>::iterator(iter);
178 return (ITERATOR *)pIter;
181 bool CExtension::FindNextDependency(ITERATOR *iter, IExtension **pOwner, SMInterface **pInterface)
183 List<IfaceInfo>::iterator *pIter = (List<IfaceInfo>::iterator *)iter;
184 List<IfaceInfo>::iterator _iter;
186 if (_iter == m_Deps.end())
195 *pOwner = (*_iter).owner;
199 *pInterface = (*_iter).iface;
204 if (_iter == m_Deps.end())
212 void CExtension::FreeDependencyIterator(ITERATOR *iter)
214 List<IfaceInfo>::iterator *pIter = (List<IfaceInfo>::iterator *)iter;
219 void CExtension::AddInterface(SMInterface *pInterface)
221 m_Interfaces.push_back(pInterface);
224 bool CExtension::IsRunning(char *error, size_t maxlength)
230 snprintf(error, maxlength, "%s", m_Error.c_str());
235 return m_pAPI->QueryRunning(error, maxlength);
238 /*********************
239 * EXTENSION MANAGER *
240 *********************/
242 void CExtensionManager::OnSourceModAllInitialized()
244 g_ExtType = g_ShareSys.CreateIdentType("EXTENSION");
245 g_PluginSys.AddPluginsListener(this);
246 g_RootMenu.AddRootConsoleCommand("exts", "Manage extensions", this);
249 void CExtensionManager::OnSourceModShutdown()
251 g_RootMenu.RemoveRootConsoleCommand("exts", this);
252 g_PluginSys.RemovePluginsListener(this);
253 g_ShareSys.DestroyIdentType(g_ExtType);
256 IExtension *CExtensionManager::LoadAutoExtension(const char *path)
258 if (!strstr(path, "." PLATFORM_LIB_EXT))
260 char newpath[PLATFORM_MAX_PATH+1];
261 snprintf(newpath, PLATFORM_MAX_PATH, "%s.%s", path, PLATFORM_LIB_EXT);
262 return LoadAutoExtension(newpath);
265 IExtension *pAlready;
266 if ((pAlready=FindExtensionByFile(path)) != NULL)
272 CExtension *p = new CExtension(path, error, sizeof(error));
276 g_Logger.LogError("[SM] Unable to load extension \"%s\": %s", path, error);
285 IExtension *CExtensionManager::FindExtensionByFile(const char *file)
287 List<CExtension *>::iterator iter;
290 if (!strstr(file, "." PLATFORM_LIB_EXT))
292 char path[PLATFORM_MAX_PATH];
293 snprintf(path, PLATFORM_MAX_PATH, "%s.%s", file, PLATFORM_LIB_EXT);
294 return FindExtensionByFile(path);
297 /* Make sure the file direction is right */
298 char path[PLATFORM_MAX_PATH+1];
299 g_LibSys.PathFormat(path, PLATFORM_MAX_PATH, "%s", file);
301 for (iter=m_Libs.begin(); iter!=m_Libs.end(); iter++)
304 if (strcmp(pExt->GetFilename(), path) == 0)
313 IExtension *CExtensionManager::FindExtensionByName(const char *ext)
315 List<CExtension *>::iterator iter;
317 IExtensionInterface *pAPI;
320 for (iter=m_Libs.begin(); iter!=m_Libs.end(); iter++)
323 if (!pExt->IsLoaded())
327 if ((pAPI = pExt->GetAPI()) == NULL)
331 name = pAPI->GetExtensionName();
336 if (strcmp(name, ext) == 0)
345 IExtension *CExtensionManager::LoadExtension(const char *file, ExtensionLifetime lifetime, char *error, size_t err_max)
347 IExtension *pAlready;
348 if ((pAlready=FindExtensionByFile(file)) != NULL)
353 CExtension *pExt = new CExtension(file, error, err_max);
355 /* :NOTE: lifetime is currently ignored */
357 if (!pExt->IsLoaded())
363 /* :TODO: do QueryRunning check if the map is loaded */
365 m_Libs.push_back(pExt);
370 void CExtensionManager::BindDependency(IExtension *pOwner, IfaceInfo *pInfo)
372 CExtension *pExt = (CExtension *)pOwner;
374 pExt->AddDependency(pInfo);
377 void CExtensionManager::AddInterface(IExtension *pOwner, SMInterface *pInterface)
379 CExtension *pExt = (CExtension *)pOwner;
381 pExt->AddInterface(pInterface);
384 void CExtensionManager::BindChildPlugin(IExtension *pParent, IPlugin *pPlugin)
386 CExtension *pExt = (CExtension *)pParent;
388 pExt->AddPlugin(pPlugin);
391 void CExtensionManager::OnPluginDestroyed(IPlugin *plugin)
393 List<CExtension *>::iterator iter;
395 for (iter=m_Libs.begin(); iter!=m_Libs.end(); iter++)
397 (*iter)->RemovePlugin(plugin);
401 CExtension *CExtensionManager::FindByOrder(unsigned int num)
403 if (num < 1 || num > m_Libs.size())
408 List<CExtension *>::iterator iter = m_Libs.begin();
410 while (iter != m_Libs.end())
422 void CExtensionManager::BindAllNativesToPlugin(IPlugin *pPlugin)
424 List<CExtension *>::iterator iter;
426 IPluginContext *pContext = pPlugin->GetBaseContext();
427 for (iter=m_Libs.begin(); iter!=m_Libs.end(); iter++)
430 if (!pExt->IsLoaded())
434 if (!pExt->m_Natives.size())
439 List<const sp_nativeinfo_t *>::iterator n_iter;
440 const sp_nativeinfo_t *natives;
441 for (n_iter = pExt->m_Natives.begin();
442 n_iter != pExt->m_Natives.end();
447 while (natives[i].func != NULL && natives[i].name != NULL)
449 if (pContext->BindNative(&natives[i++]) == SP_ERROR_NONE)
455 if (set && (pExt->m_Plugins.find(pPlugin) == pExt->m_Plugins.end()))
457 /* For now, mark this plugin as a requirement. Later we'll introduce native filtering. */
458 pExt->m_Plugins.push_back(pPlugin);
463 bool CExtensionManager::UnloadExtension(IExtension *_pExt)
470 CExtension *pExt = (CExtension *)_pExt;
472 if (m_Libs.find(pExt) == m_Libs.end())
477 /* First remove us from internal lists */
478 g_ShareSys.RemoveInterfaces(_pExt);
481 List<CExtension *> UnloadQueue;
483 /* Handle dependencies */
484 if (pExt->IsLoaded())
486 /* Unload any dependent plugins */
487 List<IPlugin *>::iterator p_iter = pExt->m_Plugins.begin();
488 while (p_iter != pExt->m_Plugins.end())
490 /* We have to manually unlink ourselves here, since we're no longer being managed */
491 g_PluginSys.UnloadPlugin((*p_iter));
492 p_iter = pExt->m_Plugins.erase(p_iter);
495 /* Notify and/or unload all dependencies */
496 List<CExtension *>::iterator c_iter;
498 IExtensionInterface *pAPI;
499 for (c_iter = m_Libs.begin(); c_iter != m_Libs.end(); c_iter++)
502 if ((pAPI=pDep->GetAPI()) == NULL)
510 /* Now, get its dependency list */
511 bool dropped = false;
512 List<IfaceInfo>::iterator i_iter = pDep->m_Deps.begin();
513 while (i_iter != pDep->m_Deps.end())
515 if ((*i_iter).owner == _pExt)
517 if (!dropped && !pAPI->QueryInterfaceDrop((*i_iter).iface))
521 pAPI->NotifyInterfaceDrop((*i_iter).iface);
522 i_iter = pDep->m_Deps.erase(i_iter);
530 List<CExtension *>::iterator iter;
531 for (iter=UnloadQueue.begin(); iter!=UnloadQueue.end(); iter++)
533 /* NOTE: This is safe because the unload function backs out of anything not present */
534 UnloadExtension((*iter));
540 void CExtensionManager::AddNatives(IExtension *pOwner, const sp_nativeinfo_t *natives)
542 CExtension *pExt = (CExtension *)pOwner;
544 pExt->m_Natives.push_back(natives);
547 void CExtensionManager::MarkAllLoaded()
549 List<CExtension *>::iterator iter;
552 for (iter=m_Libs.begin(); iter!=m_Libs.end(); iter++)
555 if (!pExt->IsLoaded())
559 if (pExt->m_FullyLoaded)
563 pExt->MarkAllLoaded();
567 void CExtensionManager::OnRootConsoleCommand(const char *cmd, unsigned int argcount)
571 const char *cmd = g_RootMenu.GetArgument(2);
572 if (strcmp(cmd, "list") == 0)
574 List<CExtension *>::iterator iter;
576 unsigned int num = 1;
577 switch (m_Libs.size())
581 g_RootMenu.ConsolePrint("[SM] Displaying 1 extension:");
586 g_RootMenu.ConsolePrint("[SM] No extensions are loaded.");
591 g_RootMenu.ConsolePrint("[SM] Displaying %d extensions:", m_Libs.size());
595 for (iter=m_Libs.begin(); iter!=m_Libs.end(); iter++,num++)
598 if (pExt->IsLoaded())
601 if (!pExt->IsRunning(error, sizeof(error)))
603 g_RootMenu.ConsolePrint("[%02d] <FAILED> file \"%s\": %s", num, pExt->GetFilename(), error);
605 IExtensionInterface *pAPI = pExt->GetAPI();
606 const char *name = pAPI->GetExtensionName();
607 const char *version = pAPI->GetExtensionVerString();
608 const char *descr = pAPI->GetExtensionDescription();
609 g_RootMenu.ConsolePrint("[%02d] %s (%s): %s", num, name, version, descr);
612 g_RootMenu.ConsolePrint("[%02d] <FAILED> file \"%s\": %s", num, pExt->GetFilename(), pExt->m_Error.c_str());
616 } else if (strcmp(cmd, "info") == 0) {
619 g_RootMenu.ConsolePrint("[SM] Usage: sm info <#>");
623 const char *sId = g_RootMenu.GetArgument(3);
624 unsigned int id = atoi(sId);
627 g_RootMenu.ConsolePrint("[SM] Usage: sm info <#>");
631 if (m_Libs.size() == 0)
633 g_RootMenu.ConsolePrint("[SM] No extensions are loaded.");
637 if (id > m_Libs.size())
639 g_RootMenu.ConsolePrint("[SM] No extension was found with id %d.", id);
643 List<CExtension *>::iterator iter = m_Libs.begin();
644 CExtension *pExt = NULL;
645 while (iter != m_Libs.end())
654 /* This should never happen */
657 g_RootMenu.ConsolePrint("[SM] No extension was found with id %d.", id);
661 if (!pExt->IsLoaded())
663 g_RootMenu.ConsolePrint(" File: %s", pExt->GetFilename());
664 g_RootMenu.ConsolePrint(" Loaded: No (%s)", pExt->m_Error.c_str());
667 if (!pExt->IsRunning(error, sizeof(error)))
669 g_RootMenu.ConsolePrint(" File: %s", pExt->GetFilename());
670 g_RootMenu.ConsolePrint(" Loaded: Yes");
671 g_RootMenu.ConsolePrint(" Running: No (%s)", error);
673 IExtensionInterface *pAPI = pExt->GetAPI();
674 g_RootMenu.ConsolePrint(" File: %s", pExt->GetFilename());
675 g_RootMenu.ConsolePrint(" Loaded: Yes (version %s)", pAPI->GetExtensionVerString());
676 g_RootMenu.ConsolePrint(" Name: %s (%s)", pAPI->GetExtensionName(), pAPI->GetExtensionDescription());
677 g_RootMenu.ConsolePrint(" Author: %s (%s)", pAPI->GetExtensionAuthor(), pAPI->GetExtensionURL());
678 g_RootMenu.ConsolePrint(" Binary info: API version %d (compiled %s)", pAPI->GetExtensionVersion(), pAPI->GetExtensionDateString());
679 g_RootMenu.ConsolePrint(" Metamod enabled: %s", pAPI->IsMetamodExtension() ? "yes" : "no");
683 } else if (strcmp(cmd, "unload") == 0) {
686 g_RootMenu.ConsolePrint("[SM] Usage: sm unload <#> [code]");
690 const char *arg = g_RootMenu.GetArgument(3);
691 unsigned int num = atoi(arg);
692 CExtension *pExt = FindByOrder(num);
696 g_RootMenu.ConsolePrint("[SM] Extension number %d was not found.", num);
700 if (argcount > 4 && pExt->unload_code)
702 const char *unload = g_RootMenu.GetArgument(4);
703 if (pExt->unload_code == atoi(unload))
705 char filename[PLATFORM_MAX_PATH+1];
706 snprintf(filename, PLATFORM_MAX_PATH, "%s", pExt->GetFilename());
707 UnloadExtension(pExt);
708 g_RootMenu.ConsolePrint("[SM] Extension %s is now unloaded.", filename);
710 g_RootMenu.ConsolePrint("[SM] Please try again, the correct unload code is \"%d\"", pExt->unload_code);
715 if (!pExt->IsLoaded()
716 || (!pExt->m_Deps.size() && !pExt->m_Plugins.size()))
718 char filename[PLATFORM_MAX_PATH+1];
719 snprintf(filename, PLATFORM_MAX_PATH, "%s", pExt->GetFilename());
720 UnloadExtension(pExt);
721 g_RootMenu.ConsolePrint("[SM] Extension %s is now unloaded.", filename);
724 List<IPlugin *> plugins;
725 if (pExt->m_Deps.size())
727 g_RootMenu.ConsolePrint("[SM] Unloading %s will unload the following extensions: ", pExt->GetFilename());
728 List<CExtension *>::iterator iter;
730 /* Get list of all extensions */
731 for (iter=m_Libs.begin(); iter!=m_Libs.end(); iter++)
733 List<IfaceInfo>::iterator i_iter;
735 if (!pOther->IsLoaded() || pOther == pExt)
739 /* Get their dependencies */
740 for (i_iter=pOther->m_Deps.begin();
741 i_iter!=pOther->m_Deps.end();
744 /* Is this dependency to us? */
745 if ((*i_iter).owner != pExt)
749 /* Will our dependent care? */
750 if (!pExt->GetAPI()->QueryInterfaceDrop((*i_iter).iface))
752 g_RootMenu.ConsolePrint(" -> %s", pExt->GetFilename());
753 /* Add to plugin unload list */
754 List<IPlugin *>::iterator p_iter;
755 for (p_iter=pOther->m_Plugins.begin();
756 p_iter!=pOther->m_Plugins.end();
759 if (plugins.find((*p_iter)) == plugins.end())
761 plugins.push_back((*p_iter));
768 if (pExt->m_Plugins.size())
770 g_RootMenu.ConsolePrint("[SM] Unloading %s will unload the following plugins: ", pExt->GetFilename());
771 List<IPlugin *>::iterator iter;
773 for (iter = pExt->m_Plugins.begin(); iter != pExt->m_Plugins.end(); iter++)
776 if (plugins.find(pPlugin) == plugins.end())
778 plugins.push_back(pPlugin);
781 for (iter = plugins.begin(); iter != plugins.end(); iter++)
784 g_RootMenu.ConsolePrint(" -> %s", pPlugin->GetFilename());
787 srand(static_cast<int>(time(NULL)));
788 pExt->unload_code = (rand() % 877) + 123; //123 to 999
789 g_RootMenu.ConsolePrint("[SM] To verify unloading %s, please use the following: ", pExt->GetFilename());
790 g_RootMenu.ConsolePrint("[SM] sm exts unload %d %d", num, pExt->unload_code);
797 g_RootMenu.ConsolePrint("SourceMod Extensions Menu:");
798 g_RootMenu.DrawGenericOption("info", "Extra extension information");
799 g_RootMenu.DrawGenericOption("list", "List extensions");
800 g_RootMenu.DrawGenericOption("unload", "Unload an extension");