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