core/systems/ExtensionSys.cpp
author David Anderson <dvander@alliedmods.net>
Wed Jan 17 18:22:00 2007 +0000 (2007-01-17)
changeset 309 51e67f50830e
parent 305 59ef1afbe958
child 310 1efceafbda26
permissions -rw-r--r--
Finished porting the "plugins" command to the new menu system
     1 #include "ExtensionSys.h"
     2 #include "LibrarySys.h"
     3 #include "ShareSys.h"
     4 #include "CLogger.h"
     5 #include "sourcemm_api.h"
     6 #include "PluginSys.h"
     7 
     8 CExtensionManager g_Extensions;
     9 IdentityType_t g_ExtType;
    10 
    11 CExtension::CExtension(const char *filename, char *error, size_t err_max)
    12 {
    13 	m_File.assign(filename);
    14 	m_pAPI = NULL;
    15 	m_pIdentToken = NULL;
    16 	m_PlId = 0;
    17 
    18 	char path[PLATFORM_MAX_PATH+1];
    19 	g_LibSys.PathFormat(path, PLATFORM_MAX_PATH, "%s/extensions/%s", g_SourceMod.GetSMBaseDir(), filename);
    20 
    21 	m_pLib = g_LibSys.OpenLibrary(path, error, err_max);
    22 
    23 	if (m_pLib == NULL)
    24 	{
    25 		return;
    26 	}
    27 
    28 	typedef IExtensionInterface *(*GETAPI)();
    29 	GETAPI pfnGetAPI = NULL;
    30 
    31 	if ((pfnGetAPI=(GETAPI)m_pLib->GetSymbolAddress("GetSMExtAPI")) == NULL)
    32 	{
    33 		m_pLib->CloseLibrary();
    34 		m_pLib = NULL;
    35 		snprintf(error, err_max, "Unable to find extension entry point");
    36 		return;
    37 	}
    38 
    39 	m_pAPI = pfnGetAPI();
    40 	if (!m_pAPI || m_pAPI->GetExtensionVersion() > SMINTERFACE_EXTENSIONAPI_VERSION)
    41 	{
    42 		m_pLib->CloseLibrary();
    43 		m_pLib = NULL;
    44 		snprintf(error, err_max, "Extension version is too new to load (%d, max is %d)", m_pAPI->GetExtensionVersion(), SMINTERFACE_EXTENSIONAPI_VERSION);
    45 		return;
    46 	}
    47 
    48 	if (m_pAPI->IsMetamodExtension())
    49 	{
    50 		bool already;
    51 		m_PlId = g_pMMPlugins->Load(path, g_PLID, already, error, err_max);
    52 	}
    53 
    54 	m_pIdentToken = g_ShareSys.CreateIdentity(g_ExtType);
    55 
    56 	if (!m_pAPI->OnExtensionLoad(this, &g_ShareSys, error, err_max, g_SourceMod.IsLateLoadInMap()))
    57 	{
    58 		if (m_pAPI->IsMetamodExtension())
    59 		{
    60 			if (m_PlId)
    61 			{
    62 				char dummy[255];
    63 				g_pMMPlugins->Unload(m_PlId, true, dummy, sizeof(dummy));
    64 				m_PlId = 0;
    65 			}
    66 		}
    67 		m_pAPI = NULL;
    68 		m_pLib->CloseLibrary();
    69 		m_pLib = NULL;
    70 		g_ShareSys.DestroyIdentity(m_pIdentToken);
    71 		m_pIdentToken = NULL;
    72 		return;
    73 	}
    74 }
    75 
    76 CExtension::~CExtension()
    77 {
    78 	if (m_pAPI)
    79 	{
    80 		m_pAPI->OnExtensionUnload();
    81 		if (m_PlId)
    82 		{
    83 			g_pMMPlugins->Unload(m_PlId, true, NULL, 0);
    84 		}
    85 	}
    86 
    87 	if (m_pIdentToken)
    88 	{
    89 		g_ShareSys.DestroyIdentity(m_pIdentToken);
    90 	}
    91 
    92 	if (m_pLib)
    93 	{
    94 		m_pLib->CloseLibrary();
    95 	}
    96 }
    97 
    98 void CExtension::AddPlugin(IPlugin *pPlugin)
    99 {
   100 	m_Plugins.push_back(pPlugin);
   101 }
   102 
   103 void CExtension::RemovePlugin(IPlugin *pPlugin)
   104 {
   105 	m_Plugins.remove(pPlugin);
   106 }
   107 
   108 void CExtension::SetError(const char *error)
   109 {
   110 	m_Error.assign(error);
   111 }
   112 
   113 IExtensionInterface *CExtension::GetAPI()
   114 {
   115 	return m_pAPI;
   116 }
   117 
   118 const char *CExtension::GetFilename()
   119 {
   120 	return m_File.c_str();
   121 }
   122 
   123 IdentityToken_t *CExtension::GetIdentity()
   124 {
   125 	return m_pIdentToken;
   126 }
   127 
   128 bool CExtension::IsLoaded()
   129 {
   130 	return (m_pLib != NULL);
   131 }
   132 
   133 void CExtension::AddDependency(IfaceInfo *pInfo)
   134 {
   135 	m_Deps.push_back(*pInfo);
   136 }
   137 
   138 ITERATOR *CExtension::FindFirstDependency(IExtension **pOwner, SMInterface **pInterface)
   139 {
   140 	List<IfaceInfo>::iterator iter = m_Deps.begin();
   141 
   142 	if (iter == m_Deps.end())
   143 	{
   144 		return NULL;
   145 	}
   146 
   147 	if (pOwner)
   148 	{
   149 		*pOwner = (*iter).owner;
   150 	}
   151 	if (pInterface)
   152 	{
   153 		*pInterface = (*iter).iface;
   154 	}
   155 
   156 	List<IfaceInfo>::iterator *pIter = new List<IfaceInfo>::iterator(iter);
   157 
   158 	return (ITERATOR *)pIter;
   159 }
   160 
   161 bool CExtension::FindNextDependency(ITERATOR *iter, IExtension **pOwner, SMInterface **pInterface)
   162 {
   163 	List<IfaceInfo>::iterator *pIter = (List<IfaceInfo>::iterator *)iter;
   164 	List<IfaceInfo>::iterator _iter;
   165 
   166 	if (_iter == m_Deps.end())
   167 	{
   168 		return false;
   169 	}
   170 
   171 	_iter++;
   172 
   173 	if (pOwner)
   174 	{
   175 		*pOwner = (*_iter).owner;
   176 	}
   177 	if (pInterface)
   178 	{
   179 		*pInterface = (*_iter).iface;
   180 	}
   181 
   182 	*pIter = _iter;
   183 
   184 	if (_iter == m_Deps.end())
   185 	{
   186 		return false;
   187 	}
   188 
   189 	return true;
   190 }
   191 
   192 void CExtension::FreeDependencyIterator(ITERATOR *iter)
   193 {
   194 	List<IfaceInfo>::iterator *pIter = (List<IfaceInfo>::iterator *)iter;
   195 
   196 	delete pIter;
   197 }
   198 
   199 void CExtension::AddInterface(SMInterface *pInterface)
   200 {
   201 	m_Interfaces.push_back(pInterface);
   202 }
   203 
   204 /*********************
   205  * EXTENSION MANAGER *
   206  *********************/
   207 
   208 void CExtensionManager::OnSourceModAllInitialized()
   209 {
   210 	g_ExtType = g_ShareSys.CreateIdentType("EXTENSION");
   211 	g_PluginSys.AddPluginsListener(this);
   212 }
   213 
   214 void CExtensionManager::OnSourceModShutdown()
   215 {
   216 	g_PluginSys.RemovePluginsListener(this);
   217 	g_ShareSys.DestroyIdentType(g_ExtType);
   218 }
   219 
   220 IExtension *CExtensionManager::LoadAutoExtension(const char *path)
   221 {
   222 	IExtension *pAlready;
   223 	if ((pAlready=FindExtensionByFile(path)) != NULL)
   224 	{
   225 		return pAlready;
   226 	}
   227 
   228 	char error[256];
   229 	CExtension *p = new CExtension(path, error, sizeof(error));
   230 
   231 	if (!p->IsLoaded())
   232 	{
   233 		g_Logger.LogError("[SM] Unable to load extension \"%s\": %s", path, error);
   234 		p->SetError(error);
   235 	}
   236 
   237 	m_Libs.push_back(p);
   238 
   239 	return p;
   240 }
   241 
   242 IExtension *CExtensionManager::FindExtensionByFile(const char *file)
   243 {
   244 	List<CExtension *>::iterator iter;
   245 	CExtension *pExt;
   246 
   247 	/* Make sure the file direction is right */
   248 	char path[PLATFORM_MAX_PATH+1];
   249 	g_LibSys.PathFormat(path, PLATFORM_MAX_PATH, "%s", file);
   250 
   251 	for (iter=m_Libs.begin(); iter!=m_Libs.end(); iter++)
   252 	{
   253 		pExt = (*iter);
   254 		if (strcmp(pExt->GetFilename(), path) == 0)
   255 		{
   256 			return pExt;
   257 		}
   258 	}
   259 
   260 	return NULL;
   261 }
   262 
   263 IExtension *CExtensionManager::FindExtensionByName(const char *ext)
   264 {
   265 	List<CExtension *>::iterator iter;
   266 	CExtension *pExt;
   267 	IExtensionInterface *pAPI;
   268 	const char *name;
   269 
   270 	for (iter=m_Libs.begin(); iter!=m_Libs.end(); iter++)
   271 	{
   272 		pExt = (*iter);
   273 		if (!pExt->IsLoaded())
   274 		{
   275 			continue;
   276 		}
   277 		if ((pAPI = pExt->GetAPI()) == NULL)
   278 		{
   279 			continue;
   280 		}
   281 		name = pAPI->GetExtensionName();
   282 		if (!name)
   283 		{
   284 			continue;
   285 		}
   286 		if (strcmp(name, ext) == 0)
   287 		{
   288 			return pExt;
   289 		}
   290 	}
   291 
   292 	return NULL;
   293 }
   294 
   295 IExtension *CExtensionManager::LoadExtension(const char *file, ExtensionLifetime lifetime, char *error, size_t err_max)
   296 {
   297 	IExtension *pAlready;
   298 	if ((pAlready=FindExtensionByFile(file)) != NULL)
   299 	{
   300 		return pAlready;
   301 	}
   302 
   303 	CExtension *pExt = new CExtension(file, error, err_max);
   304 
   305 	/* :NOTE: lifetime is currently ignored */
   306 
   307 	if (!pExt->IsLoaded())
   308 	{
   309 		delete pExt;
   310 		return NULL;
   311 	}
   312 
   313 	m_Libs.push_back(pExt);
   314 
   315 	return pExt;
   316 }
   317 
   318 void CExtensionManager::BindDependency(IExtension *pOwner, IfaceInfo *pInfo)
   319 {
   320 	CExtension *pExt = (CExtension *)pOwner;
   321 
   322 	pExt->AddDependency(pInfo);
   323 }
   324 
   325 void CExtensionManager::AddInterface(IExtension *pOwner, SMInterface *pInterface)
   326 {
   327 	CExtension *pExt = (CExtension *)pOwner;
   328 
   329 	pExt->AddInterface(pInterface);
   330 }
   331 
   332 void CExtensionManager::BindChildPlugin(IExtension *pParent, IPlugin *pPlugin)
   333 {
   334 	CExtension *pExt = (CExtension *)pParent;
   335 
   336 	pExt->AddPlugin(pPlugin);
   337 }
   338 
   339 void CExtensionManager::OnPluginDestroyed(IPlugin *plugin)
   340 {
   341 	List<CExtension *>::iterator iter;
   342 
   343 	for (iter=m_Libs.begin(); iter!=m_Libs.end(); iter++)
   344 	{
   345 		(*iter)->RemovePlugin(plugin);
   346 	}
   347 }
   348 
   349 bool CExtensionManager::UnloadExtension(IExtension *_pExt)
   350 {
   351 	if (!_pExt)
   352 	{
   353 		return false;
   354 	}
   355 
   356 	CExtension *pExt = (CExtension *)_pExt;
   357 
   358 	if (m_Libs.find(pExt) == m_Libs.end())
   359 	{
   360 		return false;
   361 	}
   362 
   363 	/* First remove us from internal lists */
   364 	g_ShareSys.RemoveInterfaces(_pExt);
   365 	m_Libs.remove(pExt);
   366 
   367 	List<CExtension *> UnloadQueue;
   368 
   369 	/* Handle dependencies */
   370 	if (pExt->IsLoaded())
   371 	{
   372 		/* Unload any dependent plugins */
   373 		List<IPlugin *>::iterator p_iter = pExt->m_Plugins.begin();
   374 		while (p_iter != pExt->m_Plugins.end())
   375 		{
   376 			g_PluginSys.UnloadPlugin((*p_iter));
   377 			/* It should already have been removed! */
   378 			assert(pExt->m_Plugins.find((*p_iter)) != pExt->m_Plugins.end());
   379 		}
   380 
   381 		/* Notify and/or unload all dependencies */
   382 		List<CExtension *>::iterator c_iter;
   383 		CExtension *pDep;
   384 		IExtensionInterface *pAPI;
   385 		for (c_iter = m_Libs.begin(); c_iter != m_Libs.end(); c_iter++)
   386 		{
   387 			pDep = (*c_iter);
   388 			if ((pAPI=pDep->GetAPI()) == NULL)
   389 			{
   390 				continue;
   391 			}
   392 			/* Now, get its dependency list */
   393 			bool dropped = false;
   394 			List<IfaceInfo>::iterator i_iter = pDep->m_Deps.begin();
   395 			while (i_iter != pDep->m_Deps.end())
   396 			{
   397 				if ((*i_iter).owner == _pExt)
   398 				{
   399 					if (!dropped && !pAPI->QueryInterfaceDrop((*i_iter).iface))
   400 					{
   401 						dropped = true;
   402 					}
   403 					pAPI->NotifyInterfaceDrop((*i_iter).iface);
   404 					i_iter = pDep->m_Deps.erase(i_iter);
   405 				} else {
   406 					i_iter++;
   407 				}
   408 			}
   409 		}
   410 	}
   411 
   412 	List<CExtension *>::iterator iter;
   413 	for (iter=UnloadQueue.begin(); iter!=UnloadQueue.end(); iter++)
   414 	{
   415 		/* NOTE: This is safe because the unload function backs out of anything not present */
   416 		UnloadExtension((*iter));
   417 	}
   418 
   419 	return true;
   420 }