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